/*++

Copyright (c) 1990-1998,  Microsoft Corporation  All rights reserved.

Module Name:

    prnsetup.c

Abstract:

    This module implements the Win32 print dialogs.

Revision History:

--*/

// precompiled headers
#include "precomp.h"
#pragma hdrstop

#include "prnsetup.h"
#include "util.h"

//
//  The PrintDlgEx routines.
//
extern VOID Print_UnloadLibraries();
extern BOOL Print_NewPrintDlg(PPRINTINFO pPI);





#ifdef UNICODE

////////////////////////////////////////////////////////////////////////////
//
//  PrintDlgA
//
//  ANSI entry point for PrintDlg when this code is built UNICODE.
//
////////////////////////////////////////////////////////////////////////////

BOOL WINAPI PrintDlgA(
    LPPRINTDLGA pPDA)
{
    PRINTINFO PI;
    BOOL bResult = FALSE;
    DWORD Flags;


    ZeroMemory(&PI, sizeof(PRINTINFO));

    if (bResult = ThunkPrintDlg(&PI, pPDA))
    {
        ThunkPrintDlgA2W(&PI);

        Flags = pPDA->Flags;

        bResult = PrintDlgX(&PI);

        if ((bResult) || (Flags & (PD_ENABLEPRINTHOOK | PD_ENABLESETUPHOOK)))
        {
            ThunkPrintDlgW2A(&PI);
        }
    }
    FreeThunkPrintDlg(&PI);

    return (bResult);
}

#else

////////////////////////////////////////////////////////////////////////////
//
//  PrintDlgW
//
//  Stub UNICODE function for PrintDlg when this code is built ANSI.
//
////////////////////////////////////////////////////////////////////////////

BOOL WINAPI PrintDlgW(
    LPPRINTDLGW pPDW)
{
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
    return (FALSE);
}

#endif


////////////////////////////////////////////////////////////////////////////
//
//  PrintDlg
//
//  The PrintDlg function displays a Print dialog box or a Print Setup
//  dialog box.  The Print dialog box enables the user to specify the
//  properties of a particular print job.  The Print Setup dialog box
//  allows the user to select additional job properties and to configure
//  the printer.
//
////////////////////////////////////////////////////////////////////////////

BOOL WINAPI PrintDlg(
    LPPRINTDLG pPD)
{
    PRINTINFO PI;

    ZeroMemory(&PI, sizeof(PRINTINFO));

    PI.pPD = pPD;
    PI.ApiType = COMDLG_WIDE;

    return ( PrintDlgX(&PI) );
}

////////////////////////////////////////////////////////////////////////////
//
//  ShowErrorMessage
//
//  Shows an error message
//
////////////////////////////////////////////////////////////////////////////

static HRESULT ShowErrorMessage(
    IN  const PRINTDLG *pPD,
    IN  const PRINTINFO *pPI,
    OUT BOOL *pbTryAgain        OPTIONAL
    )
{
    HRESULT hr = S_OK;
    BOOL bTryAgain = FALSE;
    BOOL bPrinterAdded = FALSE;

    if (!(pPD->Flags & PD_NOWARNING))
    {
        DWORD dwErr = GetStoredExtendedError();

        //
        //  Only do this for new apps.
        //
        if ( (pPI->ProcessVersion >= 0x40000) ||
             (dwErr == PDERR_NODEFAULTPRN) ||
             (dwErr == PDERR_PRINTERNOTFOUND) )
        {
            TCHAR szWarning[SCRATCHBUF_SIZE];
            TCHAR szTitle[SCRATCHBUF_SIZE];
            int iszWarning;

            szTitle[0] = TEXT('\0');
            if (pPD->hwndOwner)
            {
                GetWindowText(pPD->hwndOwner, szTitle, SCRATCHBUF_SIZE);
            }
            if (!szTitle[0])
            {
                CDLoadString(g_hinst, iszWarningTitle, szTitle, SCRATCHBUF_SIZE);
            }

            switch (dwErr)
            {
                case ( PDERR_NODEFAULTPRN ) :
                {
                    //
                    //  Notes: if the app is a 16-bit app, we don't ask the user whether to install a
                    //  new printer. This is because some components may break if we do it.
                    //

                    if (IS16BITWOWAPP(pPD))
                    {
                        iszWarning = iszNoPrnsInstalled;
                    }
                    else
                    {
                        iszWarning = iszNoPrinters;
                    }
                    break;
                }
                case ( PDERR_PRINTERNOTFOUND ) :
                {
                    iszWarning = iszPrnNotFound;
                    break;
                }
                case ( CDERR_MEMLOCKFAILURE ) :
                case ( CDERR_MEMALLOCFAILURE ) :
                case ( PDERR_LOADDRVFAILURE ) :
                {
                    iszWarning = iszMemoryError;
                    break;
                }
                default :
                {
                    iszWarning = iszGeneralWarning;
                    break;
                }
            }

            if (iszWarning == iszNoPrnsInstalled)
            {
                FormatMessage(
                    FORMAT_MESSAGE_FROM_SYSTEM |
                    FORMAT_MESSAGE_IGNORE_INSERTS,
                    NULL,
                    ERROR_PRINTER_NOT_FOUND,
                    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
                    (LPTSTR)szWarning,
                    sizeof(szWarning),
                    NULL);
            }
            else
            {
                CDLoadString(g_hinst, iszWarning, szWarning, SCRATCHBUF_SIZE);
            }

            MessageBeep(MB_ICONEXCLAMATION);

            if (iszWarning == iszNoPrinters)
            {
                //
                // If the problem is that there are no printers installed then ask the
                // user if he wants to add one, and then launch the add printer wizard.
                //
                if (IDYES == MessageBox( pPD->hwndOwner,
                                         szWarning,
                                         szTitle,
                                         MB_ICONQUESTION | MB_YESNO))
                {
                    hr = InvokeAddPrinterWizardModal(pPD->hwndOwner, &bPrinterAdded);

                    if (SUCCEEDED(hr) && bPrinterAdded)
                    {
                        //
                        // A printer was added successfully. Tell the caller to try again.
                        //
                        bTryAgain = TRUE;
                    }
                }
            }
            else
            {
                //
                // This is a fatal error. Just show an error message and bail.
                //
                MessageBox( pPD->hwndOwner,
                            szWarning,
                            szTitle,
                            MB_ICONEXCLAMATION | MB_OK );
            }
        }
    }

    if (pbTryAgain)
    {
        *pbTryAgain = bTryAgain;
    }

    return hr;
}



////////////////////////////////////////////////////////////////////////////
//
//  PrintDlgX
//
//  Worker routine for the PrintDlg api.
//
////////////////////////////////////////////////////////////////////////////

BOOL PrintDlgX(
    PPRINTINFO pPI)
{
    LPPRINTDLG pPD = pPI->pPD;
    BOOL nResult = -1;                      // <0==error, 0==CANCEL, >0==OK
    LPDEVMODE pDM = NULL;
    LPDEVMODE pDevMode = NULL;
    LPDEVNAMES pDN = NULL;
    DWORD dwFlags;                          // old copy
    WORD nCopies, nFromPage, nToPage;       // old copy
    HGLOBAL hDevNames, hDevMode;            // old copy
    TCHAR szPrinterName[MAX_PRINTERNAME];   // old copy
    LONG cbNeeded;
#ifndef WINNT
    LPCTSTR pDN_Device = NULL;
    TCHAR szDev[2];
#endif
    DWORD dwResult = 0;
    BOOL bTryAgain = TRUE;


    if (!pPD)
    {
        StoreExtendedError(CDERR_INITIALIZATION);
        return (FALSE);
    }

    if (pPD->lStructSize != sizeof(PRINTDLG))
    {
        StoreExtendedError(CDERR_STRUCTSIZE);
        return (FALSE);
    }

    if (pPD->hwndOwner && !IsWindow(pPD->hwndOwner))
    {
        StoreExtendedError(CDERR_DIALOGFAILURE);
        return (FALSE);
    }

#ifdef WINNT
    //
    //  See if the application should get the new look.
    //
    //  Do not allow the new look if they have hooks, templates,
    //  Invalid hwndOwner,
    //  they want the setup dialog, or they just want to get the default
    //  printer.
    //
    //  Also don't allow the new look if we are in the context of
    //  a 16 bit process.
    //
    if ( (!(pPI->Status & PI_PRINTDLGX_RECURSE)) &&
         (!pPI->pPSD) &&
         ((!(pPD->Flags & (PD_PAGESETUP |
                           PD_PRINTSETUP |
                           PD_RETURNDEFAULT |
                           PD_ENABLEPRINTHOOK |
                           PD_ENABLESETUPHOOK |
                           PD_ENABLEPRINTTEMPLATE |
                           PD_ENABLESETUPTEMPLATE |
                           PD_ENABLEPRINTTEMPLATEHANDLE |
                           PD_ENABLESETUPTEMPLATEHANDLE)))) &&
         (pPD->hwndOwner && IsWindow(pPD->hwndOwner)) &&
         (!IS16BITWOWAPP(pPD)) )
    {
        //
        //  Show the new dialog.
        //
        StoreExtendedError(0);

        return Print_NewPrintDlg(pPI);
    }
#endif

    //
    // Warning! Warning! Warning!
    //
    // We have to set g_tlsLangID before any call for CDLoadString
    //
    TlsSetValue(g_tlsLangID, (LPVOID) GetDialogLanguage(pPD->hwndOwner, NULL));

    //
    //  Get the process version of the app for later use.
    //
    pPI->ProcessVersion = GetProcessVersion(0);

#ifdef UNICODE
    //
    //  Check if we need to use ExtDeviceMode.  We use this
    //  mode only if a 16 bit app is calling us with a NULL
    //  devmode.
    //
    if ((pPD->Flags & CD_WOWAPP) && !pPD->hDevMode)
    {
        pPI->bUseExtDeviceMode = TRUE;
    }
    else
    {
        pPI->bUseExtDeviceMode = FALSE;
    }
#endif

#ifndef WINNT
    //
    //  This should NOT be in NT.  The device name cannot be longer than
    //  32 characters, so it may be truncated.  If we look for it in the
    //  registry and it's supposed to be larger than 32 characters, then
    //  we won't find it and we'll fail.
    //
    //  LATER: It probably shouldn't be in WIN95 either, but I'll leave
    //         it for now.
    //
    if (pPD->hDevMode)
    {
        if (!(pDM = GlobalLock(pPD->hDevMode)))
        {
            StoreExtendedError(CDERR_MEMLOCKFAILURE);
            goto PrintDlgX_DisplayWarning;
        }
    }

    if (pPD->hDevNames)
    {
        if (!(pDN = GlobalLock(pPD->hDevNames)))
        {
            if (pDM)
            {
                GlobalUnlock(pPD->hDevMode);
            }
            StoreExtendedError(CDERR_MEMLOCKFAILURE);
            goto PrintDlgX_DisplayWarning;
        }
        else
        {
            if (pDN->wDeviceOffset)
            {
                pDN_Device = (LPCTSTR)pDN + pDN->wDeviceOffset;
            }
        }
    }

    if (pDM && pDM->dmDeviceName[0])
    {
        pDM->dmDeviceName[CCHDEVICENAME - 1] = 0;
        GetProfileString(szTextDevices, pDM->dmDeviceName, szTextNull, szDev, 2);
        if (!szDev[0])
        {
            GlobalUnlock(pPD->hDevMode);
            if (pDN)
            {
                GlobalUnlock(pPD->hDevNames);
            }
            StoreExtendedError(PDERR_PRINTERNOTFOUND);
            goto PrintDlgX_DisplayWarning;
        }
    }

    if (pDN_Device && pDN_Device[0])
    {
        GetProfileString(szTextDevices, pDN_Device, szTextNull, szDev, 2);
        if (!szDev[0])
        {
            if (pDM)
            {
                GlobalUnlock(pPD->hDevMode);
            }
            GlobalUnlock(pPD->hDevNames);
            StoreExtendedError(PDERR_PRINTERNOTFOUND);
            goto PrintDlgX_DisplayWarning;
        }
    }

    if (pDM)
    {
        GlobalUnlock(pPD->hDevMode);
    }
    if (pDN)
    {
        GlobalUnlock(pPD->hDevNames);
    }
#endif

    pPD->hDC = 0;

    StoreExtendedError(CDERR_GENERALCODES);

    //
    //  Do minimal work when requesting a default printer.
    //
    if (pPD->Flags & PD_RETURNDEFAULT)
    {
        //
        //  Do not display a warning in this case if it fails.
        //  MFC 3.1 does not specify PD_NOWARNING, but that's what
        //  it really wants.
        //
        nResult = PrintReturnDefault(pPI);
        PrintClosePrinters(pPI);
        return (nResult);
    }

    if (!PrintLoadIcons())
    {
        //
        //  If the icons cannot be loaded, then fail.
        //
        StoreExtendedError(PDERR_SETUPFAILURE);
        goto PrintDlgX_DisplayWarning;
    }

    //
    //  Printer enumeration is delayed until the combobox is dropped down.
    //  However, if a printer is specified, we must force enumeration in
    //  order to find the printer so that the correct devmode can be created.
    //
    if ((pPD->hDevMode) &&
        (pPD->hDevNames) &&
        (pDM = GlobalLock(pPD->hDevMode)))
    {
        if (pDN = GlobalLock(pPD->hDevNames))
        {
            dwResult = lstrcmp((LPTSTR)pDM->dmDeviceName,
                               (LPTSTR)pDN + pDN->wDeviceOffset);
            GlobalUnlock(pPD->hDevNames);
        }
        GlobalUnlock(pPD->hDevMode);
    }

    //
    //  First : Try to open the printer in the DevMode.
    //
    //  Note: The printer name field in the DEVMODE struct is limited to
    //        32 chars which may cause this case to fail.
    //
    if ( (!dwResult) &&
         (!pPI->hCurPrinter) &&
         (pPD->hDevMode) &&
         (pDM = GlobalLock(pPD->hDevMode)) )
    {
        PrintOpenPrinter(pPI, pDM->dmDeviceName);
        GlobalUnlock(pPD->hDevMode);
    }

    //
    //  Second : Try to open the printer in the DevNames.
    //
    if ( (!pPI->hCurPrinter) &&
         (pPD->hDevNames) &&
         (pDN = GlobalLock(pPD->hDevNames)) )
    {
        PrintOpenPrinter(pPI, (LPTSTR)pDN + pDN->wDeviceOffset);
        GlobalUnlock(pPD->hDevNames);
    }

    for (;;)
    {
        //
        //  Third : Try to open the Default Printer.
        //
        PrintGetDefaultPrinterName(pPI->szDefaultPrinter, MAX_PRINTERNAME);
        if (!pPI->hCurPrinter)
        {
            if (pPI->szDefaultPrinter[0])
            {
                PrintOpenPrinter(pPI, pPI->szDefaultPrinter);
            }
        }

        //
        //  Fourth : Enumerate the Printers and try to open one of those.
        //
        if (!pPI->hCurPrinter)
        {
            if (!PrintEnumAndSelect(pPD->hwndOwner, 0, pPI, NULL, TRUE))
            {
                //
                //  There are no printers installed in the system.
                //
                if (SUCCEEDED(ShowErrorMessage(pPD, pPI, &bTryAgain)) && bTryAgain)
                {
                    //
                    // The user has installed a printer. Let's try again now.
                    //
                    continue;
                }
            }
        }

        break;
    }

    if (!bTryAgain && IS16BITWOWAPP(pPD))
    {
        //
        //  If it's a 16-bit app, we'll return immediately without showing
        //  warning message. This is because some 16-bit app will crash in
        //  the common print dialog.
        //
        return (FALSE);
    }

    //
    //  Save the original information passed in by the app in case the user
    //  hits cancel.
    //
    dwFlags = pPD->Flags;
    nCopies = pPD->nCopies;
    nFromPage = pPD->nFromPage;
    nToPage = pPD->nToPage;
    hDevNames = pPD->hDevNames;
    hDevMode = pPD->hDevMode;
    if ((pPI->pCurPrinter) &&
        (lstrlen(pPI->pCurPrinter->pPrinterName) < MAX_PRINTERNAME))
    {
        lstrcpy(szPrinterName, pPI->pCurPrinter->pPrinterName);
    }
    else
    {
        szPrinterName[0] = 0;
    }

    pPD->hDevNames = NULL;
    pPD->hDevMode = NULL;

    //
    //  Build a copy of the DevNames.
    //
    PrintBuildDevNames(pPI);

    //
    //  Get the *correct* DevMode.
    //
    if (hDevMode)
    {
        pDevMode = GlobalLock(hDevMode);
    }

#ifdef UNICODE
    else
    {
        //
        //  If it's WOW and the app didn't specify a devmode, get the 16-bit
        //  devmode out of the registry (ie. win.ini [Windows] device section).
        //
        if (pPI->bUseExtDeviceMode && pPI->pCurPrinter)
        {
            pDevMode = (pPI->pCurPrinter)->pDevMode;
            if (pDevMode)
            {
                cbNeeded = sizeof(DEVMODEW) + pDevMode->dmDriverExtra;
                goto GotWOWDMSize;
            }

            //
            //  If a 16-bit devmode isn't available in the registry,
            //  drop through and get the system default devmode.
            //
        }
    }
#endif

    cbNeeded = DocumentProperties( pPD->hwndOwner,
                                   pPI->hCurPrinter,
                                   (pPI->pCurPrinter)
                                       ? pPI->pCurPrinter->pPrinterName
                                       : NULL,
                                   NULL,
                                   NULL,
                                   0 );
#ifdef UNICODE
GotWOWDMSize:
#endif
    if ((cbNeeded > 0) &&
        (pPD->hDevMode = GlobalAlloc(GHND, cbNeeded)))
    {
        BOOL fSuccess = FALSE;

        if (pDM = GlobalLock(pPD->hDevMode))
        {
#ifdef UNICODE
            if (pPI->bUseExtDeviceMode && !hDevMode)
            {
                CopyMemory(pDM, pDevMode, cbNeeded);
                fSuccess = TRUE;
                goto GotNewWOWDM;
            }
#endif
            fSuccess = DocumentProperties( pPD->hwndOwner,
                                           pPI->hCurPrinter,
                                           (pPI->pCurPrinter)
                                               ? pPI->pCurPrinter->pPrinterName
                                               : NULL,
                                           pDM,            // out
                                           pDevMode,       // in
                                           DM_MODIFY | DM_COPY ) == IDOK;
#ifdef UNICODE
GotNewWOWDM:
#endif
            if (pDM->dmFields & DM_COPIES)
            {
                if ((hDevMode) || (pPD->Flags & PD_USEDEVMODECOPIES))
                {
                    pPD->nCopies = pDM->dmCopies;
                }
                else if (pPD->nCopies)
                {
                    pDM->dmCopies = pPD->nCopies;
                }
            }
            if (pDM->dmFields & DM_COLLATE)
            {
                //
                // if PD_COLLATE is not set, we also use the setting in
                // the returned DEVMODE structure
                //
                if ((hDevMode) || (pPD->Flags & PD_USEDEVMODECOPIES) || !(pPD->Flags & PD_COLLATE))
                {
                    if (pDM->dmCollate == DMCOLLATE_FALSE)
                    {
                        pPD->Flags  &= ~PD_COLLATE;
                        pPI->Status &= ~PI_COLLATE_REQUESTED;
                    }
                    else
                    {
                        pPD->Flags  |= PD_COLLATE;
                        pPI->Status |= PI_COLLATE_REQUESTED;
                    }
                }
                else // in this case (pPD->Flags & PD_COLLATE) must be TRUE
                {
                    pDM->dmCollate = DMCOLLATE_TRUE;
                }
            }

            GlobalUnlock(pPD->hDevMode);
        }

        if (!fSuccess)
        {
            GlobalFree(pPD->hDevMode);
            pPD->hDevMode = NULL;
        }
    }

    if (hDevMode)
    {
        GlobalUnlock(hDevMode);
    }

    //
    //  Get the default source string.
    //
    CDLoadString(g_hinst, iszDefaultSource, szDefaultSrc, SCRATCHBUF_SIZE);

    //
    //  Call the appropriate dialog routine.
    //
    switch (pPD->Flags & (PD_PRINTSETUP | PD_PAGESETUP))
    {
        case ( 0 ) :
        {
            nResult = PrintDisplayPrintDlg(pPI);
            break;
        }
        case ( PD_PRINTSETUP ) :
        case ( PD_PAGESETUP ) :
        {
            nResult = PrintDisplaySetupDlg(pPI);
            break;
        }
        default :
        {
            StoreExtendedError(CDERR_INITIALIZATION);
            break;
        }
    }

    if (nResult > 0)
    {
        //
        //  User hit OK, so free the copies of the handles passed in
        //  by the app.
        //
        if (hDevMode && (hDevMode != pPD->hDevMode))
        {
            GlobalFree(hDevMode);
            hDevMode = NULL;
        }
        if (hDevNames && (hDevNames != pPD->hDevNames))
        {
            GlobalFree(hDevNames);
            hDevNames = NULL;
        }

        if (pPD->hDevMode)
        {
            //
            //  Make sure the device name in the devmode is null
            //  terminated.
            //
            pDevMode = GlobalLock(pPD->hDevMode);
            pDevMode->dmDeviceName[CCHDEVICENAME - 1] = 0;
            GlobalUnlock(pPD->hDevMode);
        }
    }
    else
    {
        //
        //  User hit CANCEL or there was an error, so restore original
        //  values passed in by the app.
        //
        pPD->Flags = dwFlags;
        pPD->nCopies = nCopies;
        pPD->nFromPage = nFromPage;
        pPD->nToPage = nToPage;
        if (pPD->hDevMode && (pPD->hDevMode != hDevMode))
        {
            GlobalFree(pPD->hDevMode);
        }
        if (pPD->hDevNames && (pPD->hDevNames != hDevNames))
        {
            GlobalFree(pPD->hDevNames);
        }
        pPD->hDevNames = hDevNames;
        pPD->hDevMode = hDevMode;

        //
        //  If we've been called from Page Setup, then we need to reset
        //  the current printer.
        //
        if (pPI->Status & PI_PRINTDLGX_RECURSE)
        {
            PrintCancelPrinterChanged(pPI, szPrinterName);
        }
    }

    //
    //  Make sure that we are really supposed to be leaving this function
    //  before we start closing printers and displaying error messages.
    //
    if (pPI->Status & PI_PRINTDLGX_RECURSE)
    {
        return (nResult > 0);
    }

    //
    //  Close the printers that were opened.
    //
    PrintClosePrinters(pPI);

    //
    //  Display any error messages.
    //
PrintDlgX_DisplayWarning:

    if (nResult < 0)
    {
        //
        // Display an error message and ignore the return code
        // since we don't care.
        //
        ShowErrorMessage(pPD, pPI, NULL);
    }

    return (nResult > 0);
}


#ifdef UNICODE

////////////////////////////////////////////////////////////////////////////
//
//  PageSetupDlgA
//
//  ANSI entry point for PageSetupDlg when this code is built UNICODE.
//
////////////////////////////////////////////////////////////////////////////

BOOL WINAPI PageSetupDlgA(
    LPPAGESETUPDLGA pPSDA)
{
    PRINTINFO PI;
    BOOL bResult = FALSE;
    HANDLE hDevMode;
    HANDLE hDevNames;
    LPCSTR pTemplateName;


    ZeroMemory(&PI, sizeof(PRINTINFO));

    //
    //  Get the pPDA structure from the pPSDA structure.
    //
    if (bResult = ThunkPageSetupDlg(&PI, pPSDA))
    {
        //
        //  Save the original devmode and devnames.
        //
        hDevMode = pPSDA->hDevMode;
        hDevNames = pPSDA->hDevNames;
        pTemplateName = pPSDA->lpPageSetupTemplateName;

        //
        //  Convert the pPDA structure to Unicode (pPI->pPD).
        //
        if (bResult = ThunkPrintDlg(&PI, PI.pPDA))
        {
            //
            //  Fill in the pPI->pPD structure.
            //
            ThunkPrintDlgA2W(&PI);

            //
            //  Copy the Unicode information from the pPD structure to
            //  the pPSD structure for the call to PageSetupDlgX.
            //
            (PI.pPSD)->hDevMode  = (PI.pPD)->hDevMode;
            (PI.pPSD)->hDevNames = (PI.pPD)->hDevNames;

            (PI.pPSD)->lpPageSetupTemplateName = (PI.pPD)->lpSetupTemplateName;

            //
            //  Call the PageSetupDlgX function to do the work.
            //
            if (bResult = PageSetupDlgX(&PI))
            {
                //
                //  Success.  Convert the Unicode pPD structure to
                //  its ANSI equivalent.
                //
                ThunkPrintDlgW2A(&PI);

                //
                //  Save the ANSI devmode and devnames in the
                //  pPSD structure to be returned to the caller.
                //
                pPSDA->hDevMode  = (PI.pPDA)->hDevMode;
                pPSDA->hDevNames = (PI.pPDA)->hDevNames;
            }
            else
            {
                //
                //  Failure.  Restore the old devmode and devnames.
                //
                pPSDA->hDevMode = hDevMode;
                pPSDA->hDevNames = hDevNames;
            }

            //
            //  Restore the old template name (always).
            //
            pPSDA->lpPageSetupTemplateName = pTemplateName;
        }
        FreeThunkPrintDlg(&PI);
    }
    FreeThunkPageSetupDlg(&PI);

    return (bResult);
}

#else

////////////////////////////////////////////////////////////////////////////
//
//  PageSetupDlgW
//
//  Stub UNICODE function for PageSetupDlg when this code is built ANSI.
//
////////////////////////////////////////////////////////////////////////////

BOOL WINAPI PageSetupDlgW(
    LPPAGESETUPDLGW pPSDW)
{
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
    return (FALSE);
}

#endif


////////////////////////////////////////////////////////////////////////////
//
//  PageSetupDlg
//
//  The PageSetupDlg function displays a Page Setup dialog box.  This
//  dialog box enables the user to specify the page orientation, the
//  paper size, the paper source, and the margin settings.  The
//  appearance of the printed page is shown in the dialog's page preview.
//
////////////////////////////////////////////////////////////////////////////

BOOL WINAPI PageSetupDlg(
    LPPAGESETUPDLG pPSD)
{
    PRINTINFO PI;
    BOOL bResult;

    ZeroMemory(&PI, sizeof(PRINTINFO));

    PI.pPSD = pPSD;
    PI.ApiType = COMDLG_WIDE;

    bResult = PageSetupDlgX(&PI);

    if (PI.pPD)
    {
        GlobalFree(PI.pPD);
    }

    return (bResult);
}


////////////////////////////////////////////////////////////////////////////
//
//  PageSetupDlgX
//
//  Worker routine for the PageSetupDlg api.
//
//  NOTE:  Caller of this routine must free pPI->pPD.
//
////////////////////////////////////////////////////////////////////////////

BOOL PageSetupDlgX(
    PPRINTINFO pPI)
{
    LPPAGESETUPDLG pPSD = pPI->pPSD;
    BOOL bResult = FALSE;
    LPPRINTDLG pPD;
    RECT rtMinMargin;
    RECT rtMargin;
    POINT ptPaperSize;
    DWORD Flags;


    if (!pPSD)
    {
        StoreExtendedError(CDERR_INITIALIZATION);
        return (FALSE);
    }

    if (pPSD->lStructSize != sizeof(PAGESETUPDLG))
    {
        StoreExtendedError(CDERR_STRUCTSIZE);
        return (FALSE);
    }

    if ((pPSD->Flags & PSD_RETURNDEFAULT) &&
        (pPSD->hDevNames || pPSD->hDevMode))
    {
        StoreExtendedError(PDERR_RETDEFFAILURE);
        return (FALSE);
    }

    //
    //  Make sure only the PSD_* bits are on.  Otherwise, bad things
    //  will happen.
    //
    if ((pPSD->Flags & ~(PSD_MINMARGINS |
                         PSD_MARGINS |
                         PSD_INTHOUSANDTHSOFINCHES |
                         PSD_INHUNDREDTHSOFMILLIMETERS |
                         PSD_DISABLEMARGINS |
                         PSD_DISABLEPRINTER |
                         PSD_NOWARNING |                     // must be same as PD_*
                         PSD_DISABLEORIENTATION |
                         PSD_DISABLEPAPER |
                         PSD_RETURNDEFAULT |                 // must be same as PD_*
                         PSD_SHOWHELP |                      // must be same as PD_*
                         PSD_ENABLEPAGESETUPHOOK |           // must be same as PD_*
                         PSD_ENABLEPAGESETUPTEMPLATE |       // must be same as PD_*
                         PSD_ENABLEPAGESETUPTEMPLATEHANDLE | // must be same as PD_*
                         PSD_ENABLEPAGEPAINTHOOK |
                         PSD_DISABLEPAGEPAINTING |
                         CD_WX86APP |
                         PSD_NONETWORKBUTTON))  ||           // must be same as PD_*
        ((pPSD->Flags & (PSD_INTHOUSANDTHSOFINCHES |
                         PSD_INHUNDREDTHSOFMILLIMETERS)) ==
         (PSD_INTHOUSANDTHSOFINCHES | PSD_INHUNDREDTHSOFMILLIMETERS)))
    {
        StoreExtendedError(PDERR_INITFAILURE);
        return (FALSE);
    }

    if ((pPSD->Flags & PSD_MINMARGINS) && (pPSD->Flags & PSD_MARGINS))
    {
        if ( (pPSD->rtMargin.left   < pPSD->rtMinMargin.left)  ||
             (pPSD->rtMargin.top    < pPSD->rtMinMargin.top)   ||
             (pPSD->rtMargin.right  < pPSD->rtMinMargin.right) ||
             (pPSD->rtMargin.bottom < pPSD->rtMinMargin.bottom) )
        {
            StoreExtendedError(PDERR_INITFAILURE);
            return (FALSE);
        }
    }

    if (pPSD->Flags & PSD_ENABLEPAGESETUPHOOK)
    {
        if (!pPSD->lpfnPageSetupHook)
        {
            StoreExtendedError(CDERR_NOHOOK);
            return (FALSE);
        }
    }
    else
    {
        pPSD->lpfnPageSetupHook = NULL;
    }

    if (pPSD->Flags & PSD_ENABLEPAGEPAINTHOOK)
    {
        if (!pPSD->lpfnPagePaintHook)
        {
            StoreExtendedError(CDERR_NOHOOK);
            return (FALSE);
        }
    }
    else
    {
        pPSD->lpfnPagePaintHook = NULL;
    }

    if ((pPI->pPD) || (pPI->pPD = GlobalAlloc(GPTR, sizeof(PRINTDLG))))
    {
        pPD = pPI->pPD;

        pPD->lStructSize         = sizeof(PRINTDLG);
        pPD->hwndOwner           = pPSD->hwndOwner;
        pPD->Flags               = PD_PAGESETUP |
                                     (pPSD->Flags &
                                       (PSD_NOWARNING |
                                        PSD_SHOWHELP |
                                        PSD_ENABLEPAGESETUPHOOK |
                                        PSD_ENABLEPAGESETUPTEMPLATE |
                                        PSD_ENABLEPAGESETUPTEMPLATEHANDLE |
                                        CD_WX86APP |
                                        PSD_NONETWORKBUTTON));
        pPD->hInstance           = pPSD->hInstance;
        pPD->lCustData           = pPSD->lCustData;
        pPD->lpfnSetupHook       = pPSD->lpfnPageSetupHook;
        pPD->lpSetupTemplateName = pPSD->lpPageSetupTemplateName;
        pPD->hSetupTemplate      = pPSD->hPageSetupTemplate;

        //
        //  Save original settings in case the user hits cancel.
        //
        rtMinMargin = pPSD->rtMinMargin;
        rtMargin    = pPSD->rtMargin;
        ptPaperSize = pPSD->ptPaperSize;
        Flags       = pPSD->Flags;

        //
        //  Make sure the measure choice is set.
        //
        if ((pPSD->Flags & (PSD_INTHOUSANDTHSOFINCHES |
                            PSD_INHUNDREDTHSOFMILLIMETERS)) == 0)
        {
            TCHAR szIMeasure[2];

            GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IMEASURE, szIMeasure, 2);
            if (szIMeasure[0] == TEXT('1'))
            {
                pPSD->Flags |= PSD_INTHOUSANDTHSOFINCHES;
            }
            else
            {
                pPSD->Flags |= PSD_INHUNDREDTHSOFMILLIMETERS;
            }
        }

        //
        //  Set minimum margins to 0 if not passed in.
        //
        if (!(pPSD->Flags & PSD_MINMARGINS))
        {
            pPSD->rtMinMargin.left   = 0;
            pPSD->rtMinMargin.top    = 0;
            pPSD->rtMinMargin.right  = 0;
            pPSD->rtMinMargin.bottom = 0;
        }

        //
        //  Set margins to defaults if not passed in.
        //
        if (!(pPSD->Flags & PSD_MARGINS))
        {
            LONG MarginDefault = (pPSD->Flags & PSD_INTHOUSANDTHSOFINCHES)
                                     ? INCHES_DEFAULT
                                     : MMS_DEFAULT;

            pPSD->rtMargin.left   = MarginDefault;
            pPSD->rtMargin.top    = MarginDefault;
            pPSD->rtMargin.right  = MarginDefault;
            pPSD->rtMargin.bottom = MarginDefault;
        }

        TransferPSD2PD(pPI);

        bResult = PrintDlgX(pPI);

        TransferPD2PSD(pPI);

        if (!bResult)
        {
            //
            //  Restore original settings when the user hits cancel.
            //
            pPSD->rtMinMargin = rtMinMargin;
            pPSD->rtMargin    = rtMargin;
            pPSD->ptPaperSize = ptPaperSize;
            pPSD->Flags       = Flags;
        }
    }
    else
    {
        StoreExtendedError(CDERR_MEMALLOCFAILURE);
    }

    return (bResult);
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintLoadIcons
//
////////////////////////////////////////////////////////////////////////////

BOOL PrintLoadIcons()
{
    //
    //  See if we need to load the icons.
    //
    if (bAllIconsLoaded == FALSE)
    {
        //
        //  Load the orientation icons.
        //
        hIconPortrait = LoadIcon(g_hinst, MAKEINTRESOURCE(ICO_PORTRAIT));
        hIconLandscape = LoadIcon(g_hinst, MAKEINTRESOURCE(ICO_LANDSCAPE));

        //
        //  Load the duplex icons.
        //
        hIconPDuplexNone = LoadIcon(g_hinst, MAKEINTRESOURCE(ICO_P_NONE));
        hIconLDuplexNone = LoadIcon(g_hinst, MAKEINTRESOURCE(ICO_L_NONE));
        hIconPDuplexTumble = LoadIcon(g_hinst, MAKEINTRESOURCE(ICO_P_HORIZ));
        hIconLDuplexTumble = LoadIcon(g_hinst, MAKEINTRESOURCE(ICO_L_VERT));
        hIconPDuplexNoTumble = LoadIcon(g_hinst, MAKEINTRESOURCE(ICO_P_VERT));
        hIconLDuplexNoTumble = LoadIcon(g_hinst, MAKEINTRESOURCE(ICO_L_HORIZ));

        //
        //  Load the page setup icons.
        //
        hIconPSStampP = LoadIcon(g_hinst, MAKEINTRESOURCE(ICO_P_PSSTAMP));
        hIconPSStampL = LoadIcon(g_hinst, MAKEINTRESOURCE(ICO_L_PSSTAMP));

        //
        //  Load the collation images.
        //
        hIconCollate = LoadImage( g_hinst,
                                  MAKEINTRESOURCE(ICO_COLLATE),
                                  IMAGE_ICON,
                                  0,
                                  0,
                                  LR_SHARED );
        hIconNoCollate = LoadImage( g_hinst,
                                    MAKEINTRESOURCE(ICO_NO_COLLATE),
                                    IMAGE_ICON,
                                    0,
                                    0,
                                    LR_SHARED );

        bAllIconsLoaded = ( hIconPortrait &&
                            hIconLandscape &&
                            hIconPDuplexNone &&
                            hIconLDuplexNone &&
                            hIconPDuplexTumble &&
                            hIconLDuplexTumble &&
                            hIconPDuplexNoTumble &&
                            hIconLDuplexNoTumble &&
                            hIconPSStampP &&
                            hIconPSStampL &&
                            hIconCollate &&
                            hIconNoCollate );
    }

    //
    //  Return TRUE only if all icons/images were loaded properly.
    //
    return (bAllIconsLoaded);
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintDisplayPrintDlg
//
////////////////////////////////////////////////////////////////////////////

int PrintDisplayPrintDlg(
    PPRINTINFO pPI)
{
    LPPRINTDLG pPD = pPI->pPD;
    int fGotInput = -1;
    HANDLE hDlgTemplate = NULL;
    HANDLE hInstance;
#ifdef UNICODE
    UINT uiWOWFlag = 0;
#endif


    //
    //  NOTE:  The print hook check must be done here rather than in
    //         PrintDlgX.  Old apps that set this flag without the
    //         PrintHook when calling Print Setup will fail - they
    //         used to succeed.
    //
    if (pPD->Flags & PD_ENABLEPRINTHOOK)
    {
        if (!pPD->lpfnPrintHook)
        {
            StoreExtendedError(CDERR_NOHOOK);
            return (FALSE);
        }
    }
    else
    {
        pPD->lpfnPrintHook = NULL;
    }

    if (pPD->Flags & PD_ENABLEPRINTTEMPLATEHANDLE)
    {
        if (pPD->hPrintTemplate)
        {
            hDlgTemplate = pPD->hPrintTemplate;
            hInstance = g_hinst;
        }
        else
        {
            StoreExtendedError(CDERR_NOTEMPLATE);
        }
    }
    else
    {
        LPTSTR pTemplateName = NULL;

        if (pPD->Flags & PD_ENABLEPRINTTEMPLATE)
        {
            if (pPD->lpPrintTemplateName)
            {
                if (pPD->hInstance)
                {
                    pTemplateName = (LPTSTR)pPD->lpPrintTemplateName;
                    hInstance = pPD->hInstance;

                }
                else
                {
                    StoreExtendedError(CDERR_NOHINSTANCE);
                }
            }
            else
            {
                StoreExtendedError(CDERR_NOTEMPLATE);
            }
        }
        else
        {
            hInstance = g_hinst;
            pTemplateName = MAKEINTRESOURCE(PRINTDLGORD);
        }

        if (pTemplateName)
        {
            hDlgTemplate = PrintLoadResource( hInstance,
                                              pTemplateName,
                                              RT_DIALOG);
        }
    }

    if (!hDlgTemplate)
    {
        return (FALSE);
    }

    if (LockResource(hDlgTemplate))
    {
        glpfnPrintHook = GETPRINTHOOKFN(pPD);

#ifdef UNICODE
        if (IS16BITWOWAPP(pPD))
        {
            uiWOWFlag = SCDLG_16BIT;
        }

        fGotInput = (BOOL)DialogBoxIndirectParamAorW( hInstance,
                                                (LPDLGTEMPLATE)hDlgTemplate,
                                                pPD->hwndOwner,
                                                PrintDlgProc,
                                                (LPARAM)pPI,
                                                uiWOWFlag );
#else
        fGotInput = (BOOL)DialogBoxIndirectParam( hInstance,
                                            (LPDLGTEMPLATE)hDlgTemplate,
                                            pPD->hwndOwner,
                                            PrintDlgProc,
                                            (LPARAM)pPI );
#endif

        glpfnPrintHook = NULL;
        if (fGotInput == -1)
        {
            StoreExtendedError(CDERR_DIALOGFAILURE);
        }
    }
    else
    {
        StoreExtendedError(CDERR_LOCKRESFAILURE);
    }

    return (fGotInput);
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintDisplaySetupDlg
//
////////////////////////////////////////////////////////////////////////////

int PrintDisplaySetupDlg(
    PPRINTINFO pPI)
{
    LPPRINTDLG pPD = pPI->pPD;
    int fGotInput = -1;
    HANDLE hDlgTemplate = NULL;
    HANDLE hInstance;
#ifdef UNICODE
    UINT uiWOWFlag = 0;
#endif


    //
    //  NOTE:  The setup hook check must be done here rather than in
    //         PrintDlgX.  Old apps that set this flag without the
    //         SetupHook when calling Print will fail - they
    //         used to succeed.
    //
    if (pPD->Flags & PD_ENABLESETUPHOOK)
    {
        if (!pPD->lpfnSetupHook)
        {
            StoreExtendedError(CDERR_NOHOOK);
            return (FALSE);
        }
    }
    else
    {
        pPD->lpfnSetupHook = NULL;
    }

    if (pPD->Flags & PD_ENABLESETUPTEMPLATEHANDLE)
    {
        if (pPD->hSetupTemplate)
        {
            hDlgTemplate = pPD->hSetupTemplate;
            hInstance = g_hinst;
        }
        else
        {
            StoreExtendedError(CDERR_NOTEMPLATE);
        }
    }
    else
    {
        LPTSTR pTemplateName = NULL;

        if (pPD->Flags & PD_ENABLESETUPTEMPLATE)
        {
            if (pPD->lpSetupTemplateName)
            {
                if (pPD->hInstance)
                {
                    pTemplateName = (LPTSTR)pPD->lpSetupTemplateName;
                    hInstance = pPD->hInstance;
                }
                else
                {
                    StoreExtendedError(CDERR_NOHINSTANCE);
                }
            }
            else
            {
                StoreExtendedError(CDERR_NOTEMPLATE);
            }
        }
        else
        {
            hInstance = g_hinst;
            pTemplateName = ( (pPD->Flags & PD_PRINTSETUP)
                                 ? MAKEINTRESOURCE(PRNSETUPDLGORD)
                                 : MAKEINTRESOURCE(PAGESETUPDLGORD) );
        }

        if (pTemplateName)
        {
            hDlgTemplate = PrintLoadResource( hInstance,
                                              pTemplateName,
                                              RT_DIALOG);
        }
    }

    if (!hDlgTemplate)
    {
        return (FALSE);
    }

    if (LockResource(hDlgTemplate))
    {
        glpfnSetupHook = GETSETUPHOOKFN(pPD);

#ifdef UNICODE
        if (IS16BITWOWAPP(pPD))
        {
            uiWOWFlag = SCDLG_16BIT;
        }

        fGotInput = (BOOL)DialogBoxIndirectParamAorW( hInstance,
                                                (LPDLGTEMPLATE)hDlgTemplate,
                                                pPD->hwndOwner,
                                                PrintSetupDlgProc,
                                                (LPARAM)pPI,
                                                uiWOWFlag );
#else
        fGotInput = (BOOL)DialogBoxIndirectParam( hInstance,
                                            (LPDLGTEMPLATE)hDlgTemplate,
                                            pPD->hwndOwner,
                                            PrintSetupDlgProc,
                                            (LPARAM)pPI );
#endif

        glpfnSetupHook = NULL;
        if (fGotInput == -1)
        {
            StoreExtendedError(CDERR_DIALOGFAILURE);
        }
    }
    else
    {
        StoreExtendedError(CDERR_LOCKRESFAILURE);
    }

    return (fGotInput);
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintDlgProc
//
//  Print Dialog procedure.
//
////////////////////////////////////////////////////////////////////////////

BOOL_PTR CALLBACK PrintDlgProc(
    HWND hDlg,
    UINT wMsg,
    WPARAM wParam,
    LPARAM lParam)
{
    PPRINTINFO pPI;
    LPPRINTDLG pPD;
    HWND hCtl;
    BOOL bTest;
    BOOL_PTR bResult;
    LPDEVMODE pDM;
    LPDEVNAMES pDN;


    if (pPI = (PPRINTINFO)GetProp(hDlg, PRNPROP))
    {
        if ((pPD = pPI->pPD) && (pPD->lpfnPrintHook))
        {
            LPPRINTHOOKPROC lpfnPrintHook = GETPRINTHOOKFN(pPD);

#ifdef UNICODE
            if (pPI->ApiType == COMDLG_ANSI)
            {
                ThunkPrintDlgW2A(pPI);
            }
#endif
            if ((bResult = (*lpfnPrintHook)(hDlg, wMsg, wParam, lParam)))
            {
#ifdef UNICODE
                if (pPI->ApiType == COMDLG_ANSI)
                {
                    ThunkPrintDlgA2W(pPI);
                }
#endif
                return (bResult);
            }
        }
    }
    else if (glpfnPrintHook &&
             (wMsg != WM_INITDIALOG) &&
             (bResult = (*glpfnPrintHook)(hDlg, wMsg, wParam, lParam)))
    {

        return (bResult);
    }

    switch (wMsg)
    {
        case ( WM_INITDIALOG ) :
        {
            DWORD dwResult = 0;

            HourGlass(TRUE);
#ifndef WINNT
            msgHELPA = RegisterWindowMessage(szCommdlgHelp);
#endif
            SetProp(hDlg, PRNPROP, (HANDLE)lParam);
            glpfnPrintHook = NULL;

            pPI = (PPRINTINFO)lParam;
            pPD = pPI->pPD;
            if (pPI->pPSD)
            {
                TCHAR szTitle[32];
                RECT aRtDlg;
                RECT aRtGrp;
                RECT aRtYep;
                RECT aRtCan;
                HWND hBtnYep = GetDlgItem(hDlg, IDOK);
                HWND hBtnCan = GetDlgItem(hDlg, IDCANCEL);
                RECT aRtWhere;
                RECT aRtCmmnt;
                LONG GapHeight, DlgTop;

                //
                //  Save the client coordinate for the top of the dialog.
                //  Also, save the height of the gap between the bottom of
                //  the original OK button and the bottom of the original
                //  dialog.
                //
                GetWindowRect(hDlg, &aRtDlg);
                GetWindowRect(hBtnYep, &aRtYep);
                ScreenToClient(hDlg, (LPPOINT)&aRtDlg.left);
                ScreenToClient(hDlg, (LPPOINT)&aRtDlg.right);
                ScreenToClient(hDlg, (LPPOINT)&aRtYep.right);
                DlgTop = aRtDlg.top;
                GapHeight = (aRtDlg.bottom - aRtYep.bottom > 0)
                               ? aRtDlg.bottom - aRtYep.bottom
                               : 15;

                //
                //  Display the title of the dialog box.
                //
                GetWindowText(GetParent(hDlg), szTitle, 32);
                SetWindowText(hDlg, szTitle);

                //
                //  Get the screen and client coordinates for the dialog,
                //  the Printer group box, the OK button, and the Cancel
                //  button.  These will be used to reposition the OK,
                //  Cancel, and Help buttons.
                //
                GetWindowRect(hDlg, &aRtDlg);
                GetWindowRect(GetDlgItem(hDlg, ID_PRINT_G_PRINTER), &aRtGrp);
                GetWindowRect(hBtnYep, &aRtYep);
                GetWindowRect(hBtnCan, &aRtCan);

                //
                //If we are in a mirrored Dlg the use the left side.
                //
                if (IS_WINDOW_RTL_MIRRORED(hDlg)) {
                    aRtDlg.right = aRtDlg.left;
                }
                ScreenToClient(hDlg   , (LPPOINT)&aRtDlg.right);
                ScreenToClient(hDlg   , (LPPOINT)&aRtGrp.right);
                MapWindowPoints(NULL, hDlg, (LPPOINT)&aRtYep, 2);
                aRtYep.right -= aRtYep.left;
                aRtYep.bottom -= aRtYep.top;

                MapWindowPoints(NULL, hDlg, (LPPOINT)&aRtCan, 2);
                aRtCan.right -= aRtCan.left;
                aRtCan.bottom -= aRtCan.top;
#ifdef WINNT
                if (pPD->Flags & PD_SHOWHELP)
                {
                    HWND hBtnHlp = GetDlgItem(hDlg, ID_BOTH_P_HELP);
                    RECT aRtHlp;

                    //
                    //  Move the Help button up underneath the
                    //  Printer group box.
                    //
                    if (hBtnHlp)
                    {
                        GetWindowRect(hBtnHlp, &aRtHlp);
                        MapWindowPoints(NULL, hDlg, (LPPOINT)&aRtHlp, 2);
                        aRtHlp.right -= aRtHlp.left;
                        aRtHlp.bottom -= aRtHlp.top;

                        MoveWindow( hBtnHlp,
                                    aRtHlp.left,
                                    aRtGrp.bottom + 2 * aRtHlp.bottom / 3,
                                    aRtHlp.right,
                                    aRtHlp.bottom,
                                    FALSE );
                    }
                }
#endif
                //
                //  Move the OK and Cancel buttons up underneath the
                //  Printer group box.
                //
                MoveWindow( hBtnYep,
                            aRtYep.left,
                            aRtGrp.bottom + 2 * aRtYep.bottom / 3,
                            aRtYep.right,
                            aRtYep.bottom,
                            FALSE );
                MoveWindow( hBtnCan,
                            aRtCan.left,
                            aRtGrp.bottom + 2 * aRtCan.bottom / 3,
                            aRtCan.right,
                            aRtCan.bottom,
                            FALSE );

                //
                //  Resize the dialog.
                //
                GetWindowRect(hBtnYep, &aRtYep);
                MapWindowPoints(NULL, hDlg, (LPPOINT)&aRtYep, 2);
                MoveWindow( hDlg,
                            aRtDlg.left,
                            aRtDlg.top,
                            aRtDlg.right,
                            (aRtYep.bottom - DlgTop) + GapHeight,
                            FALSE );

                //
                //  Hide all other print dlg items.
                //
                //  NOTE: Need to do a SetWindowPos to actually remove
                //        the window so that the AddNetButton call does
                //        not think it's there.
                //
                SetWindowPos( GetDlgItem(hDlg, ID_PRINT_X_TOFILE),
                              NULL,
                              0, 0, 0, 0,
                              SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOZORDER );
                SetWindowPos( GetDlgItem(hDlg, ID_PRINT_X_COLLATE),
                              NULL,
                              0, 0, 0, 0,
                              SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOZORDER );
                SetWindowPos( GetDlgItem(hDlg, ID_PRINT_E_FROM),
                              NULL,
                              0, 0, 0, 0,
                              SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOZORDER );
                SetWindowPos( GetDlgItem(hDlg, ID_PRINT_E_TO),
                              NULL,
                              0, 0, 0, 0,
                              SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOZORDER );
                SetWindowPos( GetDlgItem(hDlg, ID_PRINT_E_COPIES),
                              NULL,
                              0, 0, 0, 0,
                              SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOZORDER );
                SetWindowPos( GetDlgItem(hDlg, ID_PRINT_G_RANGE),
                              NULL,
                              0, 0, 0, 0,
                              SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOZORDER );
                SetWindowPos( GetDlgItem(hDlg, ID_PRINT_G_COPIES),
                              NULL,
                              0, 0, 0, 0,
                              SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOZORDER );
                SetWindowPos( GetDlgItem(hDlg, ID_PRINT_I_COLLATE),
                              NULL,
                              0, 0, 0, 0,
                              SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOZORDER );
                SetWindowPos( GetDlgItem(hDlg, ID_PRINT_R_ALL),
                              NULL,
                              0, 0, 0, 0,
                              SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOZORDER );
                SetWindowPos( GetDlgItem(hDlg, ID_PRINT_R_SELECTION),
                              NULL,
                              0, 0, 0, 0,
                              SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOZORDER );
                SetWindowPos( GetDlgItem(hDlg, ID_PRINT_R_PAGES),
                              NULL,
                              0, 0, 0, 0,
                              SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOZORDER );
                SetWindowPos( GetDlgItem(hDlg, ID_PRINT_S_FROM),
                              NULL,
                              0, 0, 0, 0,
                              SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOZORDER );
                SetWindowPos( GetDlgItem(hDlg, ID_PRINT_S_TO),
                              NULL,
                              0, 0, 0, 0,
                              SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOZORDER );
                SetWindowPos( GetDlgItem(hDlg, ID_PRINT_S_COPIES),
                              NULL,
                              0, 0, 0, 0,
                              SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOZORDER );

                //
                //  Enlarge the comment edit control, since the
                //  "print to file" check box is hidden.
                //
                GetWindowRect(GetDlgItem(hDlg, ID_BOTH_S_WHERE), &aRtWhere);
                GetWindowRect( hCtl = GetDlgItem(hDlg, ID_BOTH_S_COMMENT),
                               &aRtCmmnt );
                MapWindowPoints(NULL, hDlg, (LPPOINT)&aRtCmmnt, 2);
                MoveWindow( hCtl,
                            aRtCmmnt.left,
                            aRtCmmnt.top,
                            aRtWhere.right  - aRtWhere.left,
                            aRtWhere.bottom - aRtWhere.top,
                            FALSE );
#ifdef WINNT
                //
                //  Add or hide net button, if necessary.
                //
                if ((pPD->Flags & PD_NONETWORKBUTTON))
                {
                    if (hCtl = GetDlgItem(hDlg, ID_BOTH_P_NETWORK))
                    {
                        EnableWindow(hCtl, FALSE);
                        ShowWindow(hCtl, SW_HIDE);
                    }
                }
                else
                {
                    AddNetButton( hDlg,
                                  g_hinst,
                                  FILE_BOTTOM_MARGIN,
                                  TRUE,
                                  FALSE,
                                  TRUE);

                    //
                    //  The button can be added in two ways -
                    //      statically (they have it predefined in their template) and
                    //      dynamically (successful call to AddNetButton).
                    //
                    if (!IsNetworkInstalled())
                    {
                        hCtl = GetDlgItem(hDlg, ID_BOTH_P_NETWORK);

                        EnableWindow(hCtl, FALSE);
                        ShowWindow(hCtl, SW_HIDE);
                    }
                }
#endif
            }
            else
            {
                if (pPD->Flags & PD_COLLATE)
                {
                    pPI->Status |= PI_COLLATE_REQUESTED;
                }
            }

            if (!PrintInitGeneral(hDlg, ID_PRINT_C_NAME, pPI) ||
                ((dwResult = PrintInitPrintDlg( hDlg,
                                                wParam,
                                                pPI )) == 0xFFFFFFFF))
            {
                RemoveProp(hDlg, PRNPROP);
                EndDialog(hDlg, -2);
            }

            HourGlass(FALSE);
            bResult = (dwResult == 1);
            return (bResult);
        }
        case ( WM_COMMAND ) :
        {
            if (!pPI)
            {
                return (FALSE);
            }

            bResult = FALSE;

            switch (GET_WM_COMMAND_ID(wParam, lParam))
            {
                case ( ID_PRINT_C_NAME ) :       // Printer Name combobox
                {
                    if (GET_WM_COMMAND_CMD(wParam, lParam) == CBN_SELCHANGE)
                    {
                        PrintPrinterChanged(hDlg, ID_PRINT_C_NAME, pPI);
                    }
                    else if ( (GET_WM_COMMAND_CMD(wParam, lParam) == CBN_DROPDOWN) &&
                              !(pPI->Status & PI_PRINTERS_ENUMERATED) )
                    {
                        //
                        //  Enumerate printers if this hasn't been done yet.
                        //
                        PrintEnumAndSelect( hDlg,
                                            ID_PRINT_C_NAME,
                                            pPI,
                                            (pPI->pCurPrinter)
                                              ? pPI->pCurPrinter->pPrinterName
                                              : NULL,
                                            TRUE );
                    }

                    break;
                }
                case ( ID_BOTH_P_PROPERTIES ) :  // Properties... button
                {
                    PrintChangeProperties(hDlg, ID_PRINT_C_NAME, pPI);

                    break;
                }
                case ( ID_PRINT_P_SETUP ) :      // Setup... button
                {
                    DWORD dwFlags = pPD->Flags;
                    HWND hwndOwner = pPD->hwndOwner;

                    pPD->Flags |= PD_PRINTSETUP;
                    pPD->Flags &= ~(PD_RETURNDC | PD_RETURNIC);
                    pPI->Status |= PI_PRINTDLGX_RECURSE;
                    pPD->hwndOwner = hDlg;

                    if (PrintDlgX(pPI))
                    {
                        if (!PrintInitBannerAndQuality(hDlg, pPI, pPD))
                        {
                            StoreExtendedError(CDERR_GENERALCODES);
                        }
                    }

                    pPI->Status &= ~PI_PRINTDLGX_RECURSE;
                    pPD->Flags = dwFlags;
                    pPD->hwndOwner = hwndOwner;

                    break;
                }
                case ( ID_PRINT_R_ALL ) :        // Print Range - All
                case ( ID_PRINT_R_SELECTION ) :  // Print Range - Selection
                case ( ID_PRINT_R_PAGES ) :      // Print Range - Pages (From, To)
                {
                    CheckRadioButton( hDlg,
                                      ID_PRINT_R_ALL,
                                      ID_PRINT_R_PAGES,
                                      GET_WM_COMMAND_ID(wParam, lParam) );

                    //
                    //  Only move the the focus to the "From" control when
                    //  the up/down arrow is NOT used.
                    //
                    if ( !IS_KEY_PRESSED(VK_UP) &&
                         !IS_KEY_PRESSED(VK_DOWN) &&
                         ((BOOL)(GET_WM_COMMAND_ID(wParam, lParam) == ID_PRINT_R_PAGES)) )
                    {
                        SendMessage( hDlg,
                                     WM_NEXTDLGCTL,
                                     (WPARAM)GetDlgItem(hDlg, ID_PRINT_E_FROM),
                                     1L );
                    }

                    break;
                }
                case ( ID_PRINT_E_FROM ) :       // From  (Print Range - Pages)
                {
                    //
                    //  Only enable the "To" control if the "From" control
                    //  contains a value.
                    //
                    GetDlgItemInt(hDlg, ID_PRINT_E_FROM, &bTest, FALSE);
                    EnableWindow(GetDlgItem(hDlg, ID_PRINT_S_TO), bTest);
                    EnableWindow(GetDlgItem(hDlg, ID_PRINT_E_TO), bTest);

                    //  FALL THRU...
                }
                case ( ID_PRINT_E_TO ) :         // To  (Print Range - Pages)
                {
                    if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE)
                    {
                        CheckRadioButton( hDlg,
                                          ID_PRINT_R_ALL,
                                          ID_PRINT_R_PAGES,
                                          ID_PRINT_R_PAGES );
                    }

                    break;
                }


                case (ID_PRINT_E_COPIES ) :
                {
                    if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE)
                    {
                        BOOL bTest;
                        //
                        //  Save the number of copies.
                        //
                        DWORD nCopies = GetDlgItemInt(hDlg, ID_PRINT_E_COPIES, &bTest, FALSE);

                        //
                        //  If the copy count is > 1, enable collate.  Otherwise,
                        //  disable it.
                        //
                        if (hCtl = GetDlgItem(hDlg, ID_PRINT_X_COLLATE))
                        {
                            EnableWindow(hCtl, (nCopies > 1));
                        }

                    }

                    break;
                }

                case ( ID_PRINT_X_COLLATE ) :    // Collate check box
                {
                    if (hCtl = GetDlgItem(hDlg, ID_PRINT_I_COLLATE))
                    {
                        ShowWindow(hCtl, SW_HIDE);
                        SendMessage( hCtl,
                                     STM_SETICON,
                                     IsDlgButtonChecked(hDlg, ID_PRINT_X_COLLATE)
                                         ? (LONG_PTR)hIconCollate
                                         : (LONG_PTR)hIconNoCollate,
                                     0L );
                        ShowWindow(hCtl, SW_SHOW);

                        if (IsDlgButtonChecked(hDlg, ID_PRINT_X_COLLATE))
                        {
                            pPI->Status |= PI_COLLATE_REQUESTED;
                        }
                        else
                        {
                            pPI->Status &= ~PI_COLLATE_REQUESTED;
                        }
                    }

                    break;
                }
                case ( ID_BOTH_P_NETWORK ) :     // Network... button
                {
#ifdef WINNT
                    HANDLE hPrinter;
                    DWORD cbPrinter = 0;
                    PPRINTER_INFO_2 pPrinter = NULL;

                    hPrinter = (HANDLE)ConnectToPrinterDlg(hDlg, 0);
                    if (hPrinter)
                    {
                        if (!GetPrinter( hPrinter,
                                         2,
                                         (LPBYTE)pPrinter,
                                         cbPrinter,
                                         &cbPrinter ))
                        {
                            if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
                            {
                                if (pPrinter = LocalAlloc(LPTR, cbPrinter))
                                {
                                    if (!GetPrinter( hPrinter,
                                                     2,
                                                     (LPBYTE)pPrinter,
                                                     cbPrinter,
                                                     &cbPrinter ))
                                    {
                                        StoreExtendedError(PDERR_PRINTERNOTFOUND);
                                    }
                                    else
                                    {
                                        SendDlgItemMessage( hDlg,
                                                            ID_PRINT_C_NAME,
                                                            CB_RESETCONTENT,
                                                            0,
                                                            0 );
                                        PrintEnumAndSelect( hDlg,
                                                            ID_PRINT_C_NAME,
                                                            pPI,
                                                            pPrinter->pPrinterName,
                                                            TRUE );
                                    }
                                }
                                else
                                {
                                    StoreExtendedError(CDERR_MEMALLOCFAILURE);
                                }
                            }
                            else
                            {
                                StoreExtendedError(PDERR_SETUPFAILURE);
                            }
                        }

                        if (!GetStoredExtendedError())
                        {
                            SendDlgItemMessage( hDlg,
                                                ID_PRINT_C_NAME,
                                                CB_SETCURSEL,
                                                (WPARAM)SendDlgItemMessage(
                                                      hDlg,
                                                      ID_PRINT_C_NAME,
                                                      CB_FINDSTRING,
                                                      0,
                                                      (LPARAM)pPrinter->pPrinterName ),
                                                (LPARAM)0 );

                            PrintPrinterChanged(hDlg, ID_PRINT_C_NAME, pPI);
                        }

                        LocalFree(pPrinter);
                        ClosePrinter(hPrinter);
                    }
#else
                    WNetConnectionDialog(hDlg, RESOURCETYPE_PRINT);
#endif
                    break;
                }
                case ( ID_BOTH_P_HELP ) :        // Help button
                {
#ifdef UNICODE
                    if (pPI->ApiType == COMDLG_ANSI)
                    {
                        if (msgHELPA && pPD->hwndOwner)
                        {
                            SendMessage( pPD->hwndOwner,
                                         msgHELPA,
                                         (WPARAM)hDlg,
                                         (DWORD_PTR)pPI->pPDA );
                        }
                    }
                    else
#endif
                    {
                        if (msgHELPW && pPD->hwndOwner)
                        {
                            SendMessage( pPD->hwndOwner,
                                         msgHELPW,
                                         (WPARAM)hDlg,
                                         (DWORD_PTR)pPD );
                        }
                    }

                    break;
                }
                case ( IDOK ) :                  // OK button
                {
                    bResult = TRUE;
                    if (!(pPI->pPSD))
                    {
                        pPD->Flags &= ~((DWORD)( PD_PRINTTOFILE |
                                                 PD_PAGENUMS    |
                                                 PD_SELECTION   |
                                                 PD_COLLATE ));

                        pPD->nCopies = (WORD)GetDlgItemInt( hDlg,
                                                            ID_PRINT_E_COPIES,
                                                            &bTest,
                                                            FALSE );
                        if ((!bTest) || (!pPD->nCopies))
                        {
                            PrintEditError( hDlg,
                                            ID_PRINT_E_COPIES,
                                            iszCopiesZero );
                            return (TRUE);
                        }

                        if (IsDlgButtonChecked(hDlg, ID_PRINT_R_SELECTION))
                        {
                            pPD->Flags |= PD_SELECTION;
                        }
                        else if (IsDlgButtonChecked(hDlg, ID_PRINT_R_PAGES))
                        {
                            //
                            //  Check the "From" and "To" values.
                            //
                            pPD->Flags |= PD_PAGENUMS;
                            pPD->nFromPage = (WORD)GetDlgItemInt( hDlg,
                                                                  ID_PRINT_E_FROM,
                                                                  &bTest,
                                                                  FALSE );
                            if (!bTest)
                            {
                                PrintEditError( hDlg,
                                                ID_PRINT_E_FROM,
                                                iszPageFromError );
                                return (TRUE);
                            }

                            pPD->nToPage = (WORD)GetDlgItemInt( hDlg,
                                                                ID_PRINT_E_TO,
                                                                &bTest,
                                                                FALSE );
                            if (!bTest)
                            {
                                TCHAR szBuf[PAGE_EDIT_SIZE + 1];

                                if (GetDlgItemText( hDlg,
                                                    ID_PRINT_E_TO,
                                                    szBuf,
                                                    PAGE_EDIT_SIZE + 1 ))
                                {
                                    PrintEditError( hDlg,
                                                    ID_PRINT_E_TO,
                                                    iszPageToError );
                                    return (TRUE);
                                }
                                else
                                {
                                    pPD->nToPage = pPD->nFromPage;
                                }
                            }

                            if ( (pPD->nFromPage < pPD->nMinPage) ||
                                 (pPD->nFromPage > pPD->nMaxPage) )
                            {
                                PrintEditError( hDlg,
                                                ID_PRINT_E_FROM,
                                                iszPageRangeError,
                                                pPD->nMinPage,
                                                pPD->nMaxPage );
                                return (TRUE);
                            }
                            if ( (pPD->nToPage < pPD->nMinPage) ||
                                 (pPD->nToPage > pPD->nMaxPage) )
                            {
                                PrintEditError( hDlg,
                                                ID_PRINT_E_TO,
                                                iszPageRangeError,
                                                pPD->nMinPage,
                                                pPD->nMaxPage );
                                return (TRUE);
                            }
                            if (pPD->nFromPage > pPD->nToPage)
                            {
                                PrintEditError( hDlg,
                                                ID_PRINT_E_FROM,
                                                iszFromToError );
                                return (TRUE);
                            }
                        }
                    }

                    HourGlass(TRUE);

                    if (IsDlgButtonChecked(hDlg, ID_PRINT_X_TOFILE))
                    {
                        pPD->Flags |= PD_PRINTTOFILE;
                    }

                    if ( (hCtl = GetDlgItem(hDlg, ID_PRINT_X_COLLATE)) &&
                         IsWindowEnabled(hCtl) &&
                         IsDlgButtonChecked(hDlg, ID_PRINT_X_COLLATE) )
                    {
                        pPD->Flags |= PD_COLLATE;
                    }

                    if (!PrintSetCopies(hDlg, pPI, ID_PRINT_C_NAME))
                    {
                        HourGlass(FALSE);
                        return (TRUE);
                    }

                    pDM = NULL;
                    pDN = NULL;
                    if (pPD->hDevMode)
                    {
                        pDM = GlobalLock(pPD->hDevMode);
                    }
                    if (pPD->hDevNames)
                    {
                        pDN = GlobalLock(pPD->hDevNames);
                    }
                    if (pDM && pDN)
                    {
                        DWORD nNum;

                        if ( GetDlgItem(hDlg, ID_PRINT_C_QUALITY) &&
                             (nNum = (DWORD) SendDlgItemMessage( hDlg,
                                                         ID_PRINT_C_QUALITY,
                                                         CB_GETCURSEL,
                                                         0,
                                                         0L )) != CB_ERR )
                        {
                            pDM->dmPrintQuality =
                                (WORD)SendDlgItemMessage( hDlg,
                                                          ID_PRINT_C_QUALITY,
                                                          CB_GETITEMDATA,
                                                          (WPARAM)nNum,
                                                          0L );
                        }

                        PrintReturnICDC(pPD, pDN, pDM);
                    }
                    if (pDM)
                    {
                        GlobalUnlock(pPD->hDevMode);
                    }
                    if (pDN)
                    {
                        GlobalUnlock(pPD->hDevNames);
                    }

#ifdef UNICODE
                    if (pPD->Flags & CD_WOWAPP)
                    {
                        UpdateSpoolerInfo(pPI);
                    }
#endif

                    //  FALL THRU...
                }
                case ( IDCANCEL ) :              // Cancel button
                case ( IDABORT ) :
                {
                    HourGlass(TRUE);

                    glpfnPrintHook = GETPRINTHOOKFN(pPD);

                    RemoveProp(hDlg, PRNPROP);
                    EndDialog(hDlg, bResult);

                    HourGlass(FALSE);

                    break;
                }
                default :
                {
                    return (FALSE);
                    break;
                }
            }

            break;
        }
        case ( WM_MEASUREITEM ) :
        {
            PrintMeasureItem(hDlg, (LPMEASUREITEMSTRUCT)lParam);
            break;
        }
        case ( WM_HELP ) :
        {
            if (IsWindowEnabled(hDlg))
            {
                WinHelp( (HWND)((LPHELPINFO)lParam)->hItemHandle,
                         NULL,
                         HELP_WM_HELP,
                         (ULONG_PTR)(LPTSTR)aPrintHelpIDs );
            }
            break;
        }
        case ( WM_CONTEXTMENU ) :
        {
            if (IsWindowEnabled(hDlg))
            {
                WinHelp( (HWND)wParam,
                         NULL,
                         HELP_CONTEXTMENU,
                         (ULONG_PTR)(LPVOID)aPrintHelpIDs );
            }
            break;
        }
        case ( WM_CTLCOLOREDIT ) :
        {
            if (GetWindowLong((HWND)lParam, GWL_STYLE) & ES_READONLY)
            {
                return ( (BOOL_PTR) SendMessage(hDlg, WM_CTLCOLORDLG, wParam, lParam) );
            }

            //  FALL THRU...
        }
        default :
        {
            return (FALSE);
            break;
        }
    }

    return (TRUE);
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintSetupDlgProc
//
//  Print Setup Dialog proc.
//
////////////////////////////////////////////////////////////////////////////

BOOL_PTR CALLBACK PrintSetupDlgProc(
    HWND hDlg,
    UINT wMsg,
    WPARAM wParam,
    LPARAM lParam)
{
    PPRINTINFO pPI;
    LPPRINTDLG pPD = NULL;
    BOOL_PTR bResult;
    UINT uCmdId;
    LPDEVMODE pDM;


    if (pPI = (PPRINTINFO)GetProp(hDlg, PRNPROP))
    {
        if ((pPD = pPI->pPD) && (pPD->lpfnSetupHook))
        {
            LPSETUPHOOKPROC lpfnSetupHook = GETSETUPHOOKFN(pPD);

#ifdef UNICODE
            if (pPI->ApiType == COMDLG_ANSI)
            {
                ThunkPrintDlgW2A(pPI);
                TransferPDA2PSD(pPI);

                pPI->NestCtr++;
                bResult = (*lpfnSetupHook)(hDlg, wMsg, wParam, lParam);
                pPI->NestCtr--;

                if (bResult)
                {
                    TransferPSD2PDA(pPI);
                    ThunkPrintDlgA2W(pPI);
                    if (pPI->NestCtr == 0)
                    {
                        TransferPD2PSD(pPI);
                    }
                    return (bResult);
                }
            }
            else
#endif
            {
                TransferPD2PSD(pPI);

                bResult = (*lpfnSetupHook)(hDlg, wMsg, wParam, lParam);

                if (bResult)
                {
                    TransferPSD2PD(pPI);
                    return (bResult);
                }
            }
        }
    }
    else if (glpfnSetupHook &&
             (wMsg != WM_INITDIALOG) &&
             (bResult = (*glpfnSetupHook)(hDlg, wMsg, wParam, lParam)))
    {
        return (bResult);
    }

    switch (wMsg)
    {
        case ( WM_INITDIALOG ) :
        {
            DWORD dwResult = 0;

            //
            // Disable RTL mirroring on the WHITE-SAMPLE
            //
            SetWindowLong(GetDlgItem(hDlg, ID_SETUP_W_SAMPLE),
                          GWL_EXSTYLE,
                          GetWindowLong(GetDlgItem(hDlg, ID_SETUP_W_SAMPLE), GWL_EXSTYLE) & ~RTL_MIRRORED_WINDOW);
            HourGlass(TRUE);
#ifndef WINNT
            msgHELPA = RegisterWindowMessage(szCommdlgHelp);
#endif
            SetProp(hDlg, PRNPROP, (HANDLE)lParam);
            pPI = (PPRINTINFO)lParam;
            pPI->bKillFocus = FALSE;
            glpfnSetupHook = NULL;

            if (!PrintInitGeneral(hDlg, ID_SETUP_C_NAME, pPI) ||
                ((dwResult = PrintInitSetupDlg( hDlg,
                                                wParam,
                                                pPI )) == 0xFFFFFFFF))
            {
                RemoveProp(hDlg, PRNPROP);
                EndDialog(hDlg, FALSE);
            }
            else if (pPI->pPSD && (pPI->pPSD->Flags & PSD_RETURNDEFAULT))
            {
                //
                //  PSD_RETURNDEFAULT goes through the entire initialization
                //  in order to set rtMinMargin, rtMargin, and ptPaperSize.
                //  Win95 Notepad relies on this behavior.
                //
                SendMessage(hDlg, WM_COMMAND, IDOK, 0);
            }

            HourGlass(FALSE);
            bResult = (dwResult == 1);
            return (bResult);
        }
        case ( WM_COMMAND ) :
        {
            if (!pPI)
            {
                return (FALSE);
            }

            bResult = FALSE;

            switch (uCmdId = GET_WM_COMMAND_ID(wParam, lParam))
            {
                case ( ID_SETUP_C_NAME ) :       // Printer Name combobox
                {
                    if ( (GET_WM_COMMAND_CMD(wParam, lParam) == CBN_DROPDOWN) &&
                         !(pPI->Status & PI_PRINTERS_ENUMERATED) )
                    {
                        //
                        //  Enumerate printers if this hasn't been done yet.
                        //
                        PrintEnumAndSelect( hDlg,
                                            ID_SETUP_C_NAME,
                                            pPI,
                                            (pPI->pCurPrinter)
                                              ? pPI->pCurPrinter->pPrinterName
                                              : NULL,
                                            TRUE );
                    }
                    if (GET_WM_COMMAND_CMD(wParam, lParam) != CBN_SELCHANGE)
                    {
                        break;
                    }
                    if ( !GetDlgItem(hDlg, ID_SETUP_R_SPECIFIC) ||
                         IsDlgButtonChecked(hDlg, ID_SETUP_R_SPECIFIC) )
                    {
                        PrintPrinterChanged(hDlg, ID_SETUP_C_NAME, pPI);
                        break;
                    }

                    uCmdId = ID_SETUP_R_SPECIFIC;

                    // FALL THRU...
                }
                case ( ID_SETUP_R_DEFAULT ) :    // Default printer
                case ( ID_SETUP_R_SPECIFIC ) :   // Specific printer
                {
                    //
                    //  Sanity check for Publisher bug where user tries to
                    //  set focus to ID_SETUP_R_DEFAULT on exit if the
                    //  dialog has no default printer.
                    //
                    if (pPI->hCurPrinter)
                    {
                        HWND hCmb;
                        DWORD dwStyle;

                        hCmb = GetDlgItem(hDlg, ID_SETUP_C_NAME);
                        if (hCmb && (uCmdId == ID_SETUP_R_DEFAULT))
                        {
                            if (!(pPI->Status & PI_PRINTERS_ENUMERATED))
                            {
                                //
                                //  Enumerate printers if this hasn't been
                                //  done yet.  Otherwise, the default printer
                                //  may not be found in the list box when
                                //  switching from Specific to Default.
                                //
                                PrintEnumAndSelect( hDlg,
                                                    ID_SETUP_C_NAME,
                                                    pPI,
                                                    NULL,
                                                    TRUE );
                            }

                            SendMessage( hCmb,
                                         CB_SETCURSEL,
                                         (WPARAM)SendMessage(
                                             hCmb,
                                             CB_FINDSTRINGEXACT,
                                             (WPARAM)-1,
                                             (LPARAM)(pPI->szDefaultPrinter) ),
                                         (LPARAM)0 );
                        }

                        PrintPrinterChanged(hDlg, ID_SETUP_C_NAME, pPI);

                        CheckRadioButton( hDlg,
                                          ID_SETUP_R_DEFAULT,
                                          ID_SETUP_R_SPECIFIC,
                                          uCmdId);

                        dwStyle = GetWindowLong(hCmb, GWL_STYLE);
                        if (uCmdId == ID_SETUP_R_DEFAULT)
                        {
                            dwStyle &= ~WS_TABSTOP;
                        }
                        else
                        {
                            dwStyle |= WS_TABSTOP;
                            SendMessage(hDlg, WM_NEXTDLGCTL, (WPARAM)hCmb, 1L);
                        }
                        SetWindowLong(hCmb, GWL_STYLE, dwStyle);
                    }

                    break;
                }
                case ( ID_BOTH_P_PROPERTIES ) :  // Properties... button
                {
                    PrintChangeProperties(hDlg, ID_SETUP_C_NAME, pPI);

                    break;
                }
                case ( ID_SETUP_P_MORE ) :      // More... button
                {
                    pDM = GlobalLock(pPD->hDevMode);

                    AdvancedDocumentProperties( hDlg,
                                                pPI->hCurPrinter,
                                                (pPI->pCurPrinter)
                                                  ? pPI->pCurPrinter->pPrinterName
                                                  : NULL,
                                                pDM,
                                                pDM );

                    GlobalUnlock(pPD->hDevMode);
                    SendMessage( hDlg,
                                 WM_NEXTDLGCTL,
                                 (WPARAM)GetDlgItem(hDlg, IDOK),
                                 1L );

                    break;
                }
                case ( ID_SETUP_R_PORTRAIT ) :   // Portrait
                case ( ID_SETUP_R_LANDSCAPE ) :  // Landscape
                {
                    if ((pPD->hDevMode) && (pDM = GlobalLock(pPD->hDevMode)))
                    {
                        PrintSetOrientation( hDlg,
                                             pPI,
                                             pDM,
                                             pPI->uiOrientationID,
                                             uCmdId );
                        GlobalUnlock(pPD->hDevMode);
                    }

                    //  FALL THRU ...
                }
                case ( ID_SETUP_R_NONE ) :       // None       (2-Sided)
                case ( ID_SETUP_R_LONG ) :       // Long Side  (2-Sided)
                case ( ID_SETUP_R_SHORT ) :      // Short Side (2-Sided)
                {
                    if ((pPD->hDevMode) && (pDM = GlobalLock(pPD->hDevMode)))
                    {
                        PrintSetDuplex(hDlg, pDM, uCmdId);
                        GlobalUnlock(pPD->hDevMode);
                    }

                    break;
                }
                case ( ID_SETUP_C_SIZE ) :       // Size combobox
                {
                    UINT Orientation;

                    if (GET_WM_COMMAND_CMD(wParam, lParam) == CBN_SELCHANGE)
                    {
                        if ((pPD->hDevMode) && (pDM = GlobalLock(pPD->hDevMode)))
                        {
                        //  pDM->dmFields |= DM_PAPERSIZE;
                            pDM->dmPaperSize =
                                (SHORT)SendMessage( (HWND)lParam,
                                                    CB_GETITEMDATA,
                                                    SendMessage( (HWND)lParam,
                                                                 CB_GETCURSEL,
                                                                 0,
                                                                 0L ),
                                                    0L );

                            Orientation =
                                IsDlgButtonChecked(hDlg, ID_SETUP_R_PORTRAIT)
                                              ? ID_SETUP_R_PORTRAIT
                                              : ID_SETUP_R_LANDSCAPE;
                            PrintSetOrientation( hDlg,
                                                 pPI,
                                                 pDM,
                                                 Orientation,
                                                 Orientation );
                            GlobalUnlock(pPD->hDevMode);
                        }
                    }

                    break;
                }
                case ( ID_SETUP_C_SOURCE ) :       // Source combobox
                {
                    if (GET_WM_COMMAND_CMD(wParam, lParam) == CBN_SELCHANGE)
                    {
                        if ((pPD->hDevMode) && (pDM = GlobalLock(pPD->hDevMode)))
                        {
                        //  pDM->dmFields |= DM_DEFAULTSOURCE;
                            pDM->dmDefaultSource =
                                (SHORT)SendMessage( (HWND)lParam,
                                                    CB_GETITEMDATA,
                                                    SendMessage( (HWND)lParam,
                                                                 CB_GETCURSEL,
                                                                 0,
                                                                 0L ),
                                                    0L );

                            GlobalUnlock(pPD->hDevMode);
                        }
                    }

                    break;
                }
                case ( ID_SETUP_E_LEFT ) :       // Left    (Margins)
                case ( ID_SETUP_E_TOP ) :        // Top     (Margins)
                case ( ID_SETUP_E_RIGHT ) :      // Right   (Margins)
                case ( ID_SETUP_E_BOTTOM ) :     // Bottom  (Margins)
                {
                    if (pPI->bKillFocus)
                    {
                        break;
                    }

                    switch (GET_WM_COMMAND_CMD(wParam, lParam))
                    {
                        case ( EN_KILLFOCUS ) :
                        {
                            pPI->bKillFocus = TRUE;
                            PrintSetMargin( hDlg,
                                            pPI,
                                            uCmdId,
                                            *((LONG*)&pPI->pPSD->rtMargin +
                                              uCmdId - ID_SETUP_E_LEFT) );
                            pPI->bKillFocus = FALSE;

                            break;
                        }
                        case ( EN_CHANGE ) :
                        {
                            HWND hSample;

                            PrintGetMargin( GET_WM_COMMAND_HWND(wParam, lParam),
                                            pPI,
                                            *((LONG*)&pPI->pPSD->rtMinMargin +
                                              uCmdId - ID_SETUP_E_LEFT),
                                            (LONG*)&pPI->pPSD->rtMargin +
                                              uCmdId - ID_SETUP_E_LEFT,
                                            (LONG*)&pPI->RtMarginMMs +
                                              uCmdId - ID_SETUP_E_LEFT );

                            if (hSample = GetDlgItem(hDlg, ID_SETUP_W_SAMPLE))
                            {
                                RECT rect;

                                GetClientRect(hSample, &rect);
                                InflateRect(&rect, -1, -1);
                                InvalidateRect(hSample, &rect, TRUE);
                            }

                            break;
                        }
                    }

                    break;
                }
                case ( ID_SETUP_P_PRINTER ) :    // Printer... button
                {
                    //
                    //  Save a copy of the original values.
                    //
                    HWND hwndOwner = pPD->hwndOwner;
                    DWORD dwFlags = pPD->Flags;
                    HINSTANCE hInstance = pPD->hInstance;
                    LPCTSTR lpPrintTemplateName = pPD->lpPrintTemplateName;

                    //
                    //  Set up pPI so that PrintDlgX can do all the work.
                    //
                    pPD->hwndOwner = hDlg;
                    pPD->Flags &= ~( PD_ENABLEPRINTTEMPLATEHANDLE |
                                     PD_RETURNIC |
                                     PD_RETURNDC |
                                     PD_PAGESETUP );
                    pPD->Flags |= PD_ENABLEPRINTTEMPLATE;
                    pPD->hInstance = g_hinst;
                    pPD->lpPrintTemplateName = MAKEINTRESOURCE(PRINTDLGORD);
                    pPI->Status |= PI_PRINTDLGX_RECURSE;

                    if (PrintDlgX(pPI))
                    {
                        PrintUpdateSetupDlg( hDlg,
                                             pPI,
                                             GlobalLock(pPD->hDevMode),
                                             TRUE );
                        GlobalUnlock(pPD->hDevMode);
                    }

                    //
                    //  Restore the original values.
                    //
                    pPD->hwndOwner = hwndOwner;
                    pPD->Flags = dwFlags;
                    pPD->hInstance = hInstance;
                    pPD->lpPrintTemplateName = lpPrintTemplateName;
                    pPI->Status &= ~PI_PRINTDLGX_RECURSE;

                    //
                    //  Set the keyboard focus to the OK button.
                    //
                    SendMessage( hDlg,
                                 WM_NEXTDLGCTL,
                                 (WPARAM)GetDlgItem(hDlg, IDOK),
                                 1L );

                    HourGlass(FALSE);

                    break;
                }
                case ( ID_BOTH_P_NETWORK ) :     // Network... button
                {
#ifdef WINNT
                    HANDLE hPrinter;
                    DWORD cbPrinter = 0;
                    PPRINTER_INFO_2 pPrinter = NULL;

                    hPrinter = (HANDLE)ConnectToPrinterDlg(hDlg, 0);
                    if (hPrinter)
                    {
                        if (!GetPrinter( hPrinter,
                                         2,
                                         (LPBYTE)pPrinter,
                                         cbPrinter,
                                         &cbPrinter ))
                        {
                            if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
                            {
                                if (pPrinter = LocalAlloc(LPTR, cbPrinter))
                                {
                                    if (!GetPrinter( hPrinter,
                                                     2,
                                                     (LPBYTE)pPrinter,
                                                     cbPrinter,
                                                     &cbPrinter ))
                                    {
                                        StoreExtendedError(PDERR_PRINTERNOTFOUND);
                                    }
                                    else
                                    {
                                        SendDlgItemMessage( hDlg,
                                                            ID_SETUP_C_NAME,
                                                            CB_RESETCONTENT,
                                                            0,
                                                            0 );
                                        PrintEnumAndSelect( hDlg,
                                                            ID_SETUP_C_NAME,
                                                            pPI,
                                                            pPrinter->pPrinterName,
                                                            TRUE );
                                    }
                                }
                                else
                                {
                                    StoreExtendedError(CDERR_MEMALLOCFAILURE);
                                }
                            }
                            else
                            {
                                StoreExtendedError(PDERR_SETUPFAILURE);
                            }
                        }

                        if (!GetStoredExtendedError())
                        {
                            SendDlgItemMessage( hDlg,
                                                ID_SETUP_C_NAME,
                                                CB_SETCURSEL,
                                                (WPARAM)SendDlgItemMessage(
                                                      hDlg,
                                                      ID_SETUP_C_NAME,
                                                      CB_FINDSTRING,
                                                      0,
                                                      (LPARAM)pPrinter->pPrinterName ),
                                                (LPARAM)0 );

                            PrintPrinterChanged(hDlg, ID_SETUP_C_NAME, pPI);
                        }

                        LocalFree(pPrinter);
                        ClosePrinter(hPrinter);
                    }
#else
                    WNetConnectionDialog(hDlg, RESOURCETYPE_PRINT);
#endif
                    break;
                }
                case ( ID_BOTH_P_HELP ) :        // Help button
                {
#ifdef UNICODE
                    if (pPI->ApiType == COMDLG_ANSI)
                    {
                        if (msgHELPA && pPD->hwndOwner)
                        {
                            SendMessage( pPD->hwndOwner,
                                         msgHELPA,
                                         (WPARAM)hDlg,
                                         (LPARAM)pPI->pPDA );
                        }
                    }
                    else
#endif
                    {
                        if (msgHELPW && pPD->hwndOwner)
                        {
                            SendMessage( pPD->hwndOwner,
                                         msgHELPW,
                                         (WPARAM)hDlg,
                                         (LPARAM)pPD );
                        }
                    }

                    break;
                }
                case ( IDOK ) :                  // OK button
                {
                    LPPAGESETUPDLG pPSD = pPI->pPSD;
                    int i;

                    if (pPSD)
                    {
                        if ((pPSD->rtMinMargin.left + pPSD->rtMinMargin.right >
                                pPSD->ptPaperSize.x) ||
                            (pPSD->rtMinMargin.top + pPSD->rtMinMargin.bottom >
                                pPSD->ptPaperSize.y))
                        {
                            //
                            //  This is an unprintable case that can happen.
                            //  Let's assume that the driver is at fault
                            //  and accept whatever the user entered.
                            //
                        }
                        else if (pPSD->rtMargin.left + pPSD->rtMargin.right >
                                     pPSD->ptPaperSize.x)
                        {
                            i = (pPSD->rtMargin.left >= pPSD->rtMargin.right)
                                    ? ID_SETUP_E_LEFT
                                    : ID_SETUP_E_RIGHT;
                            PrintEditError(hDlg, i, iszBadMarginError);
                            return (TRUE);
                        }
                        else if (pPSD->rtMargin.top + pPSD->rtMargin.bottom >
                                     pPSD->ptPaperSize.y)
                        {
                            i = (pPSD->rtMargin.top >= pPSD->rtMargin.bottom)
                                    ? ID_SETUP_E_TOP
                                    : ID_SETUP_E_BOTTOM;
                            PrintEditError(hDlg, i, iszBadMarginError);
                            return (TRUE);
                        }
                    }
                    else
                    {
                        HourGlass(TRUE);
                        if (!PrintSetCopies(hDlg, pPI, ID_SETUP_C_NAME))
                        {
                            HourGlass(FALSE);
                            return (TRUE);
                        }
                    }

                    bResult = TRUE;
                    SetFocus( GetDlgItem(hDlg, IDOK) );

                    //  FALL THRU...
                }
                case ( IDCANCEL ) :              // Cancel button
                case ( IDABORT ) :
                {
                    HourGlass(TRUE);

                    if (bResult)
                    {
                        PrintGetSetupInfo(hDlg, pPD);
#ifdef UNICODE
                        if (pPD->Flags & CD_WOWAPP)
                        {
                            UpdateSpoolerInfo(pPI);
                        }
#endif
                    }
                    else
                    {
                        SetFocus( GetDlgItem(hDlg, IDCANCEL) );
                    }
                    pPI->bKillFocus = TRUE;

                    glpfnSetupHook = GETSETUPHOOKFN(pPD);

                    RemoveProp(hDlg, PRNPROP);
                    EndDialog(hDlg, bResult);

                    HourGlass(FALSE);

                    break;
                }
                default :
                {
                    return (FALSE);
                    break;
                }
            }

            break;
        }
        case ( WM_MEASUREITEM ) :
        {
            PrintMeasureItem(hDlg, (LPMEASUREITEMSTRUCT)lParam);
            break;
        }
        case ( WM_HELP ) :
        {
            if (IsWindowEnabled(hDlg))
            {
                WinHelp( (HWND)((LPHELPINFO)lParam)->hItemHandle,
                         NULL,
                         HELP_WM_HELP,
                         (ULONG_PTR)(LPTSTR)( pPD && (pPD->Flags & PD_PRINTSETUP)
                                            ? aPrintSetupHelpIDs
                                            : aPageSetupHelpIDs) );
            }
            break;
        }
        case ( WM_CONTEXTMENU ) :
        {
            if (IsWindowEnabled(hDlg))
            {
                WinHelp( (HWND)wParam,
                         NULL,
                         HELP_CONTEXTMENU,
                         (ULONG_PTR)(LPVOID)( pPD && (pPD->Flags & PD_PRINTSETUP)
                                            ? aPrintSetupHelpIDs
                                            : aPageSetupHelpIDs) );
            }
            break;
        }
        case ( WM_CTLCOLOREDIT ) :
        {
            if (GetWindowLong((HWND)lParam, GWL_STYLE) & ES_READONLY)
            {
                return ( (BOOL_PTR) SendMessage(hDlg, WM_CTLCOLORDLG, wParam, lParam) );
            }

            //  FALL THRU...
        }
        default :
        {
            return (FALSE);
            break;
        }
    }

    return (TRUE);
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintEditMarginProc
//
////////////////////////////////////////////////////////////////////////////

LRESULT PrintEditMarginProc(
    HWND hWnd,
    UINT msg,
    WPARAM wP,
    LPARAM lP)
{
    if ( (msg == WM_CHAR) &&
         (wP != BACKSPACE) &&
         (wP != CTRL_X_CUT) &&
         (wP != CTRL_C_COPY) &&
         (wP != CTRL_V_PASTE) &&
         (wP != (WPARAM)cIntlDecimal) &&
         ((wP < TEXT('0')) || (wP > TEXT('9'))) )
    {
        MessageBeep(0);
        return (FALSE);
    }

    return ( CallWindowProc(lpEditMarginProc, hWnd, msg, wP, lP) );
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintPageSetupPaintProc
//
////////////////////////////////////////////////////////////////////////////

LRESULT PrintPageSetupPaintProc(
    HWND hWnd,
    UINT msg,
    WPARAM wP,
    LPARAM lP)
{
    LRESULT lResult;
    PPRINTINFO pPI;
    LPPAGESETUPDLG pPSD;
    HDC hDC;
    RECT aRt, aRtPage, aRtUser;
    PAINTSTRUCT aPs;
    HGDIOBJ hPen, hBr, hFont, hFontGreek;
    HRGN hRgn;
    TCHAR szGreekText[] = TEXT("Dheevaeilnorpoefdi lfaocr, \nMoiccsriocsnoafrtf \tbnya\nSFlr acnn IF iynnnaepgmaonc\n F&i nyneelglaanm 'Ox' Mnaalgleenyn i&f QCnoamgpeannnyi FI nxca.r\nFSoaynb  Ftrfaonscoirscciom,  \rCoafl idfeopronlieav\ne\n");
    LPTSTR psGreekText;
    int i;


    if (msg != WM_PAINT)
    {
        return ( CallWindowProc(lpStaticProc, hWnd, msg, wP, lP) );
    }

    hDC = BeginPaint(hWnd, &aPs);
    GetClientRect(hWnd, &aRt);
    FillRect(hDC, &aRt, (HBRUSH)GetStockObject(WHITE_BRUSH));
    EndPaint(hWnd, &aPs);
    lResult = 0;

    if ( (!(hDC = GetDC(hWnd))) ||
         (!(pPI = (PPRINTINFO)GetProp(GetParent(hWnd), PRNPROP))) )
    {
        return (0);
    }
    pPSD = pPI->pPSD;

    TransferPD2PSD(pPI);
    aRtPage = aRt;
    hPen = (HGDIOBJ)CreatePen(PS_SOLID, 1, RGB(128, 128, 128));
    hPen = SelectObject(hDC, hPen);

    // Rectangle() does not work here
    MoveToEx( hDC, 0            , 0             , NULL );
    LineTo(   hDC, aRt.right - 1, 0              );
    MoveToEx( hDC, 0            , 1             , NULL );
    LineTo(   hDC, 0            , aRt.bottom - 1 );
    DeleteObject(SelectObject(hDC, hPen));

    // Rectangle() does not work here
    MoveToEx( hDC, aRt.right - 1, 0             , NULL );
    LineTo(   hDC, aRt.right - 1, aRt.bottom - 1 );
    MoveToEx( hDC, 0            , aRt.bottom - 1, NULL );
    LineTo(   hDC, aRt.right    , aRt.bottom - 1 );

    SetBkMode(hDC, TRANSPARENT);
    hPen = (HGDIOBJ)CreatePen(PS_DOT, 1, RGB(128, 128, 128));
    hPen = SelectObject(hDC, hPen);
    hBr  = (HGDIOBJ)GetStockObject(NULL_BRUSH);
    hBr  = SelectObject(hDC, hBr);

    hFont = hFontGreek = CreateFont( pPI->PtMargins.y,
                                     pPI->PtMargins.x,
                                     0,
                                     0,
                                     FW_DONTCARE,
                                     0,
                                     0,
                                     0,
                                     ANSI_CHARSET,
                                     OUT_DEFAULT_PRECIS,
                                     CLIP_DEFAULT_PRECIS,
                                     DEFAULT_QUALITY,
                                     VARIABLE_PITCH | FF_SWISS,
                                     NULL );
    hFont = SelectObject(hDC, hFont);

    InflateRect(&aRt, -1, -1);
    aRtUser = aRt;
    hRgn = CreateRectRgnIndirect(&aRtUser);
    SelectClipRgn(hDC, hRgn);
    DeleteObject(hRgn);

    if (pPSD->lpfnPagePaintHook)
    {
        WORD wFlags;
        LPPAGEPAINTHOOK lpfnPagePaintHook = GETPAGEPAINTHOOKFN(pPSD);

        switch (pPI->dwRotation)
        {
            default :
            {
                //
                //  Portrait mode only.
                //
                wFlags = 0x0000;
                break;
            }
            case ( ROTATE_LEFT ) :
            {
                //
                //  Dot-Matrix (270)
                //
                wFlags = 0x0001;
                break;
            }
            case ( ROTATE_RIGHT ) :
            {
                //
                //  HP PCL (90)
                //
                wFlags = 0x0003;
                break;
            }
        }
        if ( !wFlags ||
             IsDlgButtonChecked(GetParent(hWnd), ID_SETUP_R_PORTRAIT) )
        {
            //
            //  Paper in portrait.
            //
            wFlags |= 0x0004;
        }
        if (pPI->pPD->Flags & PI_WPAPER_ENVELOPE)
        {
            wFlags |= 0x0008;
            if (aRt.right < aRt.bottom)
            {
                //
                //  Envelope in portrait.
                //
                wFlags |= 0x0010;
            }
        }
        if ((*lpfnPagePaintHook)( hWnd,
                                  WM_PSD_PAGESETUPDLG,
                                  MAKELONG(pPI->wPaper, wFlags),
                                  (LPARAM)pPSD ) ||
            (*lpfnPagePaintHook)( hWnd,
                                  WM_PSD_FULLPAGERECT,
                                  (WPARAM)hDC,
                                  (LPARAM)(LPRECT)&aRtUser ))
        {
            goto NoMorePainting;
        }

        aRtUser = aRt;
        aRtUser.left   += aRtUser.right  * pPI->RtMinMarginMMs.left   / pPI->PtPaperSizeMMs.x;
        aRtUser.top    += aRtUser.bottom * pPI->RtMinMarginMMs.top    / pPI->PtPaperSizeMMs.y;
        aRtUser.right  -= aRtUser.right  * pPI->RtMinMarginMMs.right  / pPI->PtPaperSizeMMs.x;
        aRtUser.bottom -= aRtUser.bottom * pPI->RtMinMarginMMs.bottom / pPI->PtPaperSizeMMs.y;

        if ((aRtUser.left   < aRtUser.right)  &&
            (aRtUser.top    < aRtUser.bottom) &&
            (aRtUser.left   > aRtPage.left)   &&
            (aRtUser.top    > aRtPage.top)    &&
            (aRtUser.right  < aRtPage.right)  &&
            (aRtUser.bottom < aRtPage.bottom))
        {
            hRgn = CreateRectRgnIndirect(&aRtUser);
            SelectClipRgn(hDC, hRgn);
            DeleteObject(hRgn);
            if ((*lpfnPagePaintHook)( hWnd,
                                      WM_PSD_MINMARGINRECT,
                                      (WPARAM)hDC,
                                      (LPARAM)(LPRECT)&aRtUser ))
            {
                goto NoMorePainting;
            }
        }
    }

    aRt.left   += aRt.right  * pPI->RtMarginMMs.left   / pPI->PtPaperSizeMMs.x;
    aRt.top    += aRt.bottom * pPI->RtMarginMMs.top    / pPI->PtPaperSizeMMs.y;
    aRt.right  -= aRt.right  * pPI->RtMarginMMs.right  / pPI->PtPaperSizeMMs.x;
    aRt.bottom -= aRt.bottom * pPI->RtMarginMMs.bottom / pPI->PtPaperSizeMMs.y;

    if ( (aRt.left > aRtPage.left) && (aRt.left < aRtPage.right) &&
         (aRt.right < aRtPage.right) && (aRt.right > aRtPage.left) &&
         (aRt.top > aRtPage.top) && (aRt.top < aRtPage.bottom) &&
         (aRt.bottom < aRtPage.bottom) && (aRt.bottom > aRtPage.top) &&
         (aRt.left < aRt.right) &&
         (aRt.top < aRt.bottom) )
    {
        if (pPSD->lpfnPagePaintHook)
        {
            LPPAGEPAINTHOOK lpfnPagePaintHook = GETPAGEPAINTHOOKFN(pPSD);

            aRtUser = aRt;
            hRgn = CreateRectRgnIndirect(&aRtUser);
            SelectClipRgn(hDC, hRgn);
            DeleteObject(hRgn);
            if ((*lpfnPagePaintHook)( hWnd,
                                      WM_PSD_MARGINRECT,
                                      (WPARAM)hDC,
                                      (LPARAM)(LPRECT)&aRtUser ))
            {
                goto SkipMarginRectangle;
            }
        }
        if (!(pPSD->Flags & PSD_DISABLEPAGEPAINTING))
        {
            Rectangle(hDC, aRt.left, aRt.top, aRt.right, aRt.bottom);
        }

SkipMarginRectangle:

        InflateRect(&aRt, -1, -1);
        if (pPSD->lpfnPagePaintHook)
        {
            LPPAGEPAINTHOOK lpfnPagePaintHook = GETPAGEPAINTHOOKFN(pPSD);

            aRtUser = aRt;
            hRgn = CreateRectRgnIndirect(&aRtUser);
            SelectClipRgn(hDC, hRgn);
            DeleteObject(hRgn);
            if ((*lpfnPagePaintHook)( hWnd,
                                      WM_PSD_GREEKTEXTRECT,
                                      (WPARAM)hDC,
                                      (LPARAM)(LPRECT)&aRtUser ))
            {
                goto SkipGreekText;
            }
        }
        if (!(pPSD->Flags & PSD_DISABLEPAGEPAINTING))
        {
            psGreekText = LocalAlloc( LPTR,
                                      10 * (sizeof(szGreekText) + sizeof(TCHAR)) );
            for (i = 0; i < 10; i++)
            {
                CopyMemory( &(psGreekText[i * (sizeof(szGreekText) / sizeof(TCHAR))]),
                            szGreekText,
                            sizeof(szGreekText) );
            }
            aRt.left++;
            aRt.right--;
            aRt.bottom -= (aRt.bottom - aRt.top) % pPI->PtMargins.y;
            hFontGreek = SelectObject(hDC, hFontGreek);
            DrawText( hDC,
                      psGreekText,
                      10 * (sizeof(szGreekText) / sizeof(TCHAR)),
                      &aRt,
                      DT_NOPREFIX | DT_WORDBREAK );
            SelectObject(hDC, hFontGreek);
            LocalFree(psGreekText);
        }
    }

SkipGreekText:

    InflateRect(&aRtPage, -1, -1);
    if (pPI->pPD->Flags & PI_WPAPER_ENVELOPE)
    {
        int iOrientation;

        aRt = aRtPage;
        if (aRt.right < aRt.bottom)     // portrait
        //  switch (pPI->dwRotation)
            {
        //      default :               // no landscape
        //      case ( ROTATE_LEFT ) :  // dot-matrix
        //      {
        //          aRt.left = aRt.right  - 16;
        //          aRt.top  = aRt.bottom - 32;
        //          iOrientation = 2;
        //          break;
        //      }
        //      case ( ROTATE_RIGHT ) : // HP PCL
        //      {
                    aRt.right  = aRt.left + 16;
                    aRt.bottom = aRt.top  + 32;
                    iOrientation = 1;
        //          break;
        //      }
            }
        else                            // landscape
        {
            aRt.left   = aRt.right - 32;
            aRt.bottom = aRt.top   + 16;
            iOrientation = 3;
        }
        hRgn = CreateRectRgnIndirect(&aRt);
        SelectClipRgn(hDC, hRgn);
        DeleteObject(hRgn);
        if (pPSD->lpfnPagePaintHook)
        {
            LPPAGEPAINTHOOK lpfnPagePaintHook = GETPAGEPAINTHOOKFN(pPSD);

            aRtUser = aRt;
            if ((*lpfnPagePaintHook)( hWnd,
                                      WM_PSD_ENVSTAMPRECT,
                                      (WPARAM)hDC,
                                      (LPARAM)(LPRECT)&aRtUser ))
            {
                goto SkipEnvelopeStamp;
            }
        }
        if (!(pPSD->Flags & PSD_DISABLEPAGEPAINTING))
        {
            switch (iOrientation)
            {
                default :          // HP PCL
            //  case ( 1 ) :
                {
                    DrawIcon(hDC, aRt.left, aRt.top, hIconPSStampP);
                    break;
                }
            //  case ( 2 ) :       // dot-matrix
            //  {
            //      DrawIcon(hDC, aRt.left - 16, aRt.top, hIconPSStampP);
            //      break;
            //  }
                case ( 3 ) :       // landscape
                {
                    DrawIcon(hDC, aRt.left, aRt.top, hIconPSStampL);
                    break;
                }
            }
        }
    }

SkipEnvelopeStamp:;

    aRtUser = aRtPage;
    hRgn = CreateRectRgnIndirect(&aRtUser);
    SelectClipRgn(hDC, hRgn);
    DeleteObject(hRgn);
    if (pPSD->lpfnPagePaintHook)
    {
        LPPAGEPAINTHOOK lpfnPagePaintHook = GETPAGEPAINTHOOKFN(pPSD);

        if ((*lpfnPagePaintHook)( hWnd,
                                  WM_PSD_YAFULLPAGERECT,
                                  (WPARAM)hDC,
                                  (LPARAM)(LPRECT)&aRtUser ))
        {
            goto NoMorePainting;
        }
    }

    //
    //  Draw the envelope lines.
    //
    if ( (!(pPSD->Flags & PSD_DISABLEPAGEPAINTING)) &&
         (pPI->pPD->Flags & PI_WPAPER_ENVELOPE) )
    {
        int iRotation;
        HGDIOBJ hPenBlack;

        aRt = aRtPage;
        if (aRt.right < aRt.bottom)                     // portrait
        {
        //  if (pPI->dwRotation == ROTATE_LEFT )        // dot-matrix
        //      iRotation = 3;
        //  else            // ROTATE_RIGHT             // HP PCL
                iRotation = 2;
        }
        else                                            // landscape
        {
            iRotation = 1;                              // normal
        }

        switch (iRotation)
        {
            default :
        //  case ( 1 ) :      // normal
            {
                aRt.right  = aRt.left + 32;
                aRt.bottom = aRt.top  + 13;
                break;
            }
            case ( 2 ) :      // left
            {
                aRt.right = aRt.left   + 13;
                aRt.top   = aRt.bottom - 32;
                break;
            }
        //  case ( 3 ) :      // right
        //  {
        //      aRt.left   = aRt.right - 13;
        //      aRt.bottom = aRt.top   + 32;
        //      break;
        //  }
        }

        InflateRect(&aRt, -3, -3);
        hPenBlack = SelectObject(hDC, GetStockObject(BLACK_PEN));
        switch (iRotation)
        {
            case ( 1 ) :       // normal
            {
                MoveToEx(hDC, aRt.left , aRt.top    , NULL);
                LineTo(  hDC, aRt.right, aRt.top);
                MoveToEx(hDC, aRt.left , aRt.top + 3, NULL);
                LineTo(  hDC, aRt.right, aRt.top + 3);
                MoveToEx(hDC, aRt.left , aRt.top + 6, NULL);
                LineTo(  hDC, aRt.right, aRt.top + 6);

                break;
            }
        //  case ( 2 ) :       // left
        //  case ( 3 ) :       // right
            default :
            {
                MoveToEx( hDC, aRt.left      , aRt.top       , NULL );
                LineTo(   hDC, aRt.left      , aRt.bottom     );
                MoveToEx( hDC, aRt.left   + 3, aRt.top       , NULL );
                LineTo(   hDC, aRt.left   + 3, aRt.bottom     );
                MoveToEx( hDC, aRt.left   + 6, aRt.top       , NULL );
                LineTo(   hDC, aRt.left   + 6, aRt.bottom     );

                break;
            }
        }
        SelectObject(hDC, hPenBlack);
    }

NoMorePainting:

    DeleteObject(SelectObject(hDC, hPen));
    SelectObject(hDC, hBr);
    DeleteObject(SelectObject(hDC, hFont));
    TransferPSD2PD(pPI);
    ReleaseDC(hWnd, hDC);

    return (lResult);
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintLoadResource
//
//  This routine loads the resource with the given name and type.
//
////////////////////////////////////////////////////////////////////////////

HANDLE PrintLoadResource(
    HANDLE hInst,
    LPTSTR pResName,
    LPTSTR pType)
{
    HANDLE hResInfo, hRes;
    LANGID LangID = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);

    //
    // If we are loading a resource from ComDlg32 then use the correct LangID.
    //
    if (hInst == g_hinst) {
        LangID = (LANGID) TlsGetValue(g_tlsLangID);
    }

    if (!(hResInfo = FindResourceExFallback(hInst, pType, pResName, LangID)))
    {
        StoreExtendedError(CDERR_FINDRESFAILURE);
        return (NULL);
    }

    if (!(hRes = LoadResource(hInst, hResInfo)))
    {
        StoreExtendedError(CDERR_LOADRESFAILURE);
        return (NULL);
    }

    return (hRes);
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintGetDefaultPrinterName
//
//  This routine gets the name of the default printer and stores it
//  in the given buffer.
//
////////////////////////////////////////////////////////////////////////////

VOID PrintGetDefaultPrinterName(
    LPTSTR pDefaultPrinter,
    UINT cchSize)
{
    DWORD dwSize;
    LPTSTR lpsz;


    if (pDefaultPrinter[0] != CHAR_NULL)
    {
        return;
    }

    //
    //  First, try to get the default printername from the win.ini file.
    //
    if (GetProfileString( szTextWindows,
                          szTextDevice,
                          szTextNull,
                          pDefaultPrinter,
                          cchSize ))
    {
        lpsz = pDefaultPrinter;

        while (*lpsz != CHAR_COMMA)
        {
            if (!*lpsz++)
            {
                pDefaultPrinter[0] = CHAR_NULL;
                goto GetDefaultFromRegistry;
            }
        }

        *lpsz = CHAR_NULL;
    }
    else
    {

GetDefaultFromRegistry:

        //
        //  Second, try to get it from the registry.
        //
        dwSize = cchSize * sizeof(TCHAR);

        if (RegOpenKeyEx( HKEY_CURRENT_USER,
                          szRegistryPrinter,
                          0,
                          KEY_READ,
                          &hPrinterKey ) == ERROR_SUCCESS)
        {
            RegQueryValueEx( hPrinterKey,
                             szRegistryDefaultValueName,
                             NULL,
                             NULL,
                             (LPBYTE)(pDefaultPrinter),
                             &dwSize );

            RegCloseKey(hPrinterKey);
        }
    }
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintReturnDefault
//
////////////////////////////////////////////////////////////////////////////

BOOL PrintReturnDefault(
    PPRINTINFO pPI)
{
    LPPRINTDLG pPD = pPI->pPD;
    LPDEVNAMES pDN;
    LPDEVMODE pDM;


    StoreExtendedError(CDERR_GENERALCODES);

    if (pPD->hDevNames || pPD->hDevMode)
    {
        StoreExtendedError(PDERR_RETDEFFAILURE);
        return (FALSE);
    }

    PrintBuildDevNames(pPI);

    if ((pPD->hDevNames) && (pDN = GlobalLock(pPD->hDevNames)))
    {
#ifdef WINNT
        //
        //  This is not needed in Win95.  An optimization was
        //  added to DocumentProperties that allows the caller to
        //  simply pass in the printer name without the printer
        //  handle.
        //
        LPTSTR pPrinterName;

        pPrinterName = (LPTSTR)pDN + pDN->wDeviceOffset;

        if (pPrinterName[0])
        {
            PrintOpenPrinter(pPI, pPrinterName);
        }

        pPD->hDevMode = PrintGetDevMode( 0,
                                         pPI->hCurPrinter,
                                         pPrinterName,
                                         NULL);
#else
        pPD->hDevMode = PrintGetDevMode( 0,
                                         NULL,
                                         (LPTSTR)pDN + pDN->wDeviceOffset,
                                         NULL );
#endif

        if ((pPD->hDevMode) && (pDM = GlobalLock(pPD->hDevMode)))
        {
            PrintReturnICDC(pPD, pDN, pDM);

            GlobalUnlock(pPD->hDevMode);
            GlobalUnlock(pPD->hDevNames);

            return (TRUE);
        }
        GlobalUnlock(pPD->hDevNames);
        GlobalFree(pPD->hDevNames);
        pPD->hDevNames = NULL;
    }

    StoreExtendedError(PDERR_NODEFAULTPRN);
    return (FALSE);
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintInitGeneral
//
//  Initialize (enable/disable) dialog elements general to both PrintDlg
//  and SetupDlg.
//
////////////////////////////////////////////////////////////////////////////

BOOL PrintInitGeneral(
    HWND hDlg,
    UINT Id,
    PPRINTINFO pPI)
{
    LPPRINTDLG pPD = pPI->pPD;
    HWND hCtl;


    SetWindowLong( hDlg,
                   GWL_STYLE,
                   GetWindowLong(hDlg, GWL_STYLE) | DS_CONTEXTHELP );

#ifndef WINNT
    if (!lpEditMarginProc)
    {
        WNDCLASSEX wc;

        wc.cbSize = sizeof(wc);

        GetClassInfoEx(NULL, TEXT("edit"), &wc);
        lpEditMarginProc = wc.lpfnWndProc;

        GetClassInfoEx(NULL, TEXT("static"), &wc);
        lpStaticProc = wc.lpfnWndProc;
    }
#endif

    //
    //  LATER: If we don't enumerate here, there will only be ONE item
    //         in the list box.  As a result, we won't catch the
    //         keyboard strokes within the list box (eg. arrow keys,
    //         pgup, pgdown, etc).  Need to subclass the combo boxes
    //         to catch these key strokes so that the printers can be
    //         enumerated.
    //
    if (!PrintEnumAndSelect( hDlg,
                             Id,
                             pPI,
                             (pPI->pCurPrinter)
                               ? pPI->pCurPrinter->pPrinterName
                               : NULL,
                             (!(pPI->Status & PI_PRINTERS_ENUMERATED)) ))
    {
        goto InitGeneral_ConstructFailure;
    }

    PrintUpdateStatus(hDlg, pPI);

    //
    //  See if the Help button should be hidden.
    //
    if (!(pPD->Flags & PD_SHOWHELP))
    {
        if (hCtl = GetDlgItem(hDlg, ID_BOTH_P_HELP))
        {
            EnableWindow(hCtl, FALSE);
            ShowWindow(hCtl, SW_HIDE);
#ifdef WINNT
            //
            //  Move the window out of this spot so that no overlap
            //  will be detected when adding the network button.
            //
            MoveWindow(hCtl, -8000, -8000, 20, 20, FALSE);
#endif
        }
    }

    return (TRUE);

InitGeneral_ConstructFailure:

    if (!GetStoredExtendedError())
    {
        StoreExtendedError(PDERR_INITFAILURE);
    }

    return (FALSE);
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintInitPrintDlg
//
//  Initialize PRINT DLG-specific dialog stuff.
//
//  Returns 0xFFFFFFFF if the dialog should be ended.
//  Otherwise, returns 1/0 (TRUE/FALSE) depending on focus.
//
////////////////////////////////////////////////////////////////////////////

DWORD PrintInitPrintDlg(
    HWND hDlg,
    WPARAM wParam,
    PPRINTINFO pPI)
{
    LPPRINTDLG pPD = pPI->pPD;
    WORD wCheckID;
    HWND hCtl;


    //
    //  Set the number of copies.
    //
    pPD->nCopies = max(pPD->nCopies, 1);
    pPD->nCopies = min(pPD->nCopies, MAX_COPIES);
    SetDlgItemInt(hDlg, ID_PRINT_E_COPIES, pPD->nCopies, FALSE);

    if ( !(pPI->pPSD) &&
         (hCtl = GetDlgItem(hDlg, ID_PRINT_E_COPIES)) &&
         (GetWindowLong(hCtl, GWL_STYLE) & WS_VISIBLE) )
    {
        //
        //  "9999" is the maximum value.
        //
        Edit_LimitText(hCtl, COPIES_EDIT_SIZE);

        CreateUpDownControl( WS_CHILD | WS_BORDER | WS_VISIBLE |
                                 UDS_ALIGNRIGHT | UDS_SETBUDDYINT |
                                 UDS_NOTHOUSANDS | UDS_ARROWKEYS,
                             0,
                             0,
                             0,
                             0,
                             hDlg,
                             IDC_COPIES_UDARROW,
                             g_hinst,
                             hCtl,
                             MAX_COPIES,
                             1,
                             pPD->nCopies );

        //
        // Adjust the width of the copies edit control using the current
        // font and the scroll bar width.  This is necessary to handle the
        // the up down control from encroching on the space in the edit
        // control when we are in High Contrast (extra large) mode.
        //
        SetCopiesEditWidth(hDlg, hCtl);
    }

    if (!PrintInitBannerAndQuality(hDlg, pPI, pPD))
    {
        if (!GetStoredExtendedError())
        {
            StoreExtendedError(PDERR_INITFAILURE);
        }
        return (0xFFFFFFFF);
    }

#ifdef WINNT
    if (!(pPD->Flags & PD_SHOWHELP))
    {
        if (hCtl = GetDlgItem(hDlg, ID_BOTH_P_HELP))
        {
            EnableWindow(hCtl, FALSE);
            ShowWindow(hCtl, SW_HIDE);

            //
            //  Move the window out of this spot so that no overlap
            //  will be detected when adding the network button.
            //
            MoveWindow(hCtl, -8000, -8000, 20, 20, FALSE);
        }
    }
#endif

    if (hCtl = GetDlgItem(hDlg, ID_PRINT_X_TOFILE))
    {
        if (pPD->Flags & PD_PRINTTOFILE)
        {
            CheckDlgButton(hDlg, ID_PRINT_X_TOFILE, TRUE);
        }

        if (pPD->Flags & PD_HIDEPRINTTOFILE)
        {
            EnableWindow(hCtl, FALSE);
            ShowWindow(hCtl, SW_HIDE);
        }
        else if (pPD->Flags & PD_DISABLEPRINTTOFILE)
        {
            EnableWindow(hCtl, FALSE);
        }
    }

    if (pPD->Flags & PD_NOPAGENUMS)
    {
        EnableWindow(GetDlgItem(hDlg, ID_PRINT_R_PAGES), FALSE);
        EnableWindow(GetDlgItem(hDlg, ID_PRINT_S_FROM), FALSE);
        EnableWindow(GetDlgItem(hDlg, ID_PRINT_E_FROM), FALSE);
        EnableWindow(GetDlgItem(hDlg, ID_PRINT_S_TO), FALSE);
        EnableWindow(GetDlgItem(hDlg, ID_PRINT_E_TO), FALSE);

        //
        //  Don't allow disabled button checked.
        //
        pPD->Flags &= ~((DWORD)PD_PAGENUMS);
    }
    else
    {
        //
        //  Some apps (marked 3.1) do not pass in valid ranges.
        //      (e.g. Corel Ventura)
        //
        if ((pPI->ProcessVersion < 0x40000) || (!(pPD->Flags & PD_PAGENUMS)))
        {
            if (pPD->nFromPage != 0xFFFF)
            {
                if (pPD->nFromPage < pPD->nMinPage)
                {
                    pPD->nFromPage = pPD->nMinPage;
                }
                else if (pPD->nFromPage > pPD->nMaxPage)
                {
                    pPD->nFromPage = pPD->nMaxPage;
                }
            }
            if (pPD->nToPage != 0xFFFF)
            {
                if (pPD->nToPage < pPD->nMinPage)
                {
                    pPD->nToPage = pPD->nMinPage;
                }
                else if (pPD->nToPage > pPD->nMaxPage)
                {
                    pPD->nToPage = pPD->nMaxPage;
                }
            }
        }

        if ( pPD->nMinPage > pPD->nMaxPage ||
             ( pPD->nFromPage != 0xFFFF &&
               ( pPD->nFromPage < pPD->nMinPage ||
                 pPD->nFromPage > pPD->nMaxPage ) ) ||
             ( pPD->nToPage != 0xFFFF &&
               ( pPD->nToPage < pPD->nMinPage ||
                 pPD->nToPage > pPD->nMaxPage ) ) )
        {
            StoreExtendedError(PDERR_INITFAILURE);
            return (0xFFFFFFFF);
        }

        if (pPD->nFromPage != 0xFFFF)
        {
            SetDlgItemInt(hDlg, ID_PRINT_E_FROM, pPD->nFromPage, FALSE);
            if (pPD->nToPage != 0xFFFF)
            {
                SetDlgItemInt(hDlg, ID_PRINT_E_TO, pPD->nToPage, FALSE);
            }
        }
        else
        {
            EnableWindow(GetDlgItem(hDlg, ID_PRINT_S_TO), FALSE);
            EnableWindow(GetDlgItem(hDlg, ID_PRINT_E_TO), FALSE);
        }

        if (pPD->nMinPage == pPD->nMaxPage)
        {
            EnableWindow(GetDlgItem(hDlg, ID_PRINT_R_PAGES), FALSE);
            EnableWindow(GetDlgItem(hDlg, ID_PRINT_S_FROM), FALSE);
            EnableWindow(GetDlgItem(hDlg, ID_PRINT_E_FROM), FALSE);
            EnableWindow(GetDlgItem(hDlg, ID_PRINT_S_TO), FALSE);
            EnableWindow(GetDlgItem(hDlg, ID_PRINT_E_TO), FALSE);

            //
            //  Don't allow disabled button checked.
            //
            pPD->Flags &= ~((DWORD)(PD_PAGENUMS | PD_COLLATE));
            pPI->Status &= ~PI_COLLATE_REQUESTED;
            EnableWindow(GetDlgItem(hDlg, ID_PRINT_X_COLLATE), FALSE);
            ShowWindow(GetDlgItem(hDlg, ID_PRINT_X_COLLATE), SW_HIDE);
        }
#ifndef WINNT
        //
        //  This is NOT desirable.  Most apps use a very high number for
        //  their max page, so it looks very strange.
        //
        else
        {
            TCHAR szAll[32];
            TCHAR szBuf[64];

            if (pPD->nMinPage != pPD->nMaxPage)
            {
                if (pPD->nMaxPage != 0xFFFF)
                {
                    if (CDLoadString(g_hinst, iszPrintRangeAll, szAll, 32))
                    {
                        wsprintf( szBuf,
                                  szAll,
                                  (DWORD)pPD->nMaxPage - (DWORD)pPD->nMinPage + 1 );
                        SetDlgItemText(hDlg, ID_PRINT_R_ALL, szBuf);
                    }
                    else
                    {
                        StoreExtendedError(CDERR_LOADSTRFAILURE);
                        return (0xFFFFFFFF);
                    }
                }
            }
        }
#endif
    }

    if (pPD->Flags & PD_NOSELECTION)
    {
        HWND hRad = GetDlgItem(hDlg, ID_PRINT_R_SELECTION);

        if (hRad)
        {
            EnableWindow(hRad, FALSE);
        }

        //
        //  Don't allow disabled button checked.
        //
        pPD->Flags &= ~((DWORD)PD_SELECTION);
    }

    if (pPD->Flags & PD_PAGENUMS)
    {
        wCheckID = ID_PRINT_R_PAGES;
    }
    else if (pPD->Flags & PD_SELECTION)
    {
        wCheckID = ID_PRINT_R_SELECTION;
    }
    else
    {
        // PD_ALL
        wCheckID = ID_PRINT_R_ALL;
    }

    CheckRadioButton(hDlg, ID_PRINT_R_ALL, ID_PRINT_R_PAGES, (int)wCheckID);

    //
    //  Subclass the integer only edit controls.
    //
    if (!(pPI->pPSD))
    {
        if (hCtl = GetDlgItem(hDlg, ID_PRINT_E_FROM))
        {
            //
            //  "99999" is the maximum value.
            //
            Edit_LimitText(hCtl, PAGE_EDIT_SIZE);
        }
        if (hCtl = GetDlgItem(hDlg, ID_PRINT_E_TO))
        {
            //
            //  "99999" is the maximum value.
            //
            Edit_LimitText(hCtl, PAGE_EDIT_SIZE);
        }
    }

    if (pPD->lpfnPrintHook)
    {
        LPPRINTHOOKPROC lpfnPrintHook = GETPRINTHOOKFN(pPD);

#ifdef UNICODE
        if (pPI->ApiType == COMDLG_ANSI)
        {
            DWORD dwHookRet;

            ThunkPrintDlgW2A(pPI);
            dwHookRet = (*lpfnPrintHook)( hDlg,
                                          WM_INITDIALOG,
                                          wParam,
                                          (LONG_PTR)pPI->pPDA ) != 0;
            if (dwHookRet)
            {
                ThunkPrintDlgA2W(pPI);
            }

            return (dwHookRet);
        }
        else
#endif
        {
            return ( (*lpfnPrintHook)( hDlg,
                                       WM_INITDIALOG,
                                       wParam,
                                       (LONG_PTR)pPD ) ) != 0;
        }
    }

    return (TRUE);
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintInitSetupDlg
//
//  Initialize SETUP-specific dialog stuff.
//
//  Returns 0xFFFFFFFF if the dialog should be ended.
//  Otherwise, returns 1/0 (TRUE/FALSE) depending on focus.
//
////////////////////////////////////////////////////////////////////////////

DWORD PrintInitSetupDlg(
    HWND hDlg,
    WPARAM wParam,
    PPRINTINFO pPI)
{
    LPPRINTDLG pPD = pPI->pPD;
    LPDEVMODE pDM = NULL;
    HWND hCtl;
    LPPAGESETUPDLG pPSD = pPI->pPSD;
    UINT Orientation;


    if (!pPD->hDevMode ||
        !(pDM = GlobalLock(pPD->hDevMode)))
    {
        StoreExtendedError(CDERR_MEMLOCKFAILURE);
        goto InitSetupDlg_ConstructFailure;
    }

    if (hCtl = GetDlgItem(hDlg, ID_SETUP_C_SIZE))
    {
        PrintInitPaperCombo( pPI,
                             hCtl,
                             GetDlgItem(hDlg, ID_SETUP_S_SIZE),
                             pPI->pCurPrinter,
                             pDM,
                             DC_PAPERNAMES,
                             CCHPAPERNAME,
                             DC_PAPERS );
    }

    //
    //  Provide backward compatibility for old-style-template sources
    //  ID_SETUP_C_SOURCE.
    //
    if (hCtl = GetDlgItem(hDlg, ID_SETUP_C_SOURCE))
    {
        PrintInitPaperCombo( pPI,
                             hCtl,
                             GetDlgItem(hDlg, ID_SETUP_S_SOURCE),
                             pPI->pCurPrinter,
                             pDM,
                             DC_BINNAMES,
                             CCHBINNAME,
                             DC_BINS );
    }

    //
    //  Set the edit field lengths and other setup stuff for margins.
    //  This must be called before PrintSetMargin, which is called in
    //  PrintSetOrientation.
    //
    PrintSetupMargins(hDlg, pPI);

    PrintInitOrientation(hDlg, pPI, pDM);
    Orientation = pDM->dmOrientation + ID_SETUP_R_PORTRAIT - DMORIENT_PORTRAIT;
    PrintSetOrientation( hDlg,
                         pPI,
                         pDM,
                         Orientation,
                         Orientation );

    PrintInitDuplex(hDlg, pDM);
    PrintSetDuplex( hDlg,
                    pDM,
                    pDM->dmDuplex + ID_SETUP_R_NONE - DMDUP_SIMPLEX );

    GlobalUnlock(pPD->hDevMode);

    if (pPSD)
    {
        if (pPSD->Flags & PSD_DISABLEORIENTATION)
        {
            EnableWindow(GetDlgItem(hDlg, ID_SETUP_R_PORTRAIT), FALSE );
            EnableWindow(GetDlgItem(hDlg, ID_SETUP_R_LANDSCAPE), FALSE );
        }
        if (pPSD->Flags & PSD_DISABLEPAPER)
        {
            EnableWindow(GetDlgItem(hDlg, ID_SETUP_S_SIZE), FALSE );
            EnableWindow(GetDlgItem(hDlg, ID_SETUP_C_SIZE), FALSE );
            EnableWindow(GetDlgItem(hDlg, ID_SETUP_S_SOURCE), FALSE );
            EnableWindow(GetDlgItem(hDlg, ID_SETUP_C_SOURCE), FALSE );
        }
        if (pPSD->Flags & PSD_DISABLEMARGINS)
        {
            EnableWindow(GetDlgItem(hDlg, ID_SETUP_S_LEFT), FALSE );
            EnableWindow(GetDlgItem(hDlg, ID_SETUP_E_LEFT),  FALSE );
            EnableWindow(GetDlgItem(hDlg, ID_SETUP_S_RIGHT), FALSE );
            EnableWindow(GetDlgItem(hDlg, ID_SETUP_E_RIGHT),  FALSE );
            EnableWindow(GetDlgItem(hDlg, ID_SETUP_S_TOP), FALSE );
            EnableWindow(GetDlgItem(hDlg, ID_SETUP_E_TOP),  FALSE );
            EnableWindow(GetDlgItem(hDlg, ID_SETUP_S_BOTTOM), FALSE );
            EnableWindow(GetDlgItem(hDlg, ID_SETUP_E_BOTTOM),  FALSE );
        }
        if (pPSD->Flags & PSD_DISABLEPRINTER)
        {
            EnableWindow(GetDlgItem(hDlg, ID_SETUP_P_PRINTER), FALSE );
        }
    }

    if (hCtl = GetDlgItem(hDlg, ID_SETUP_W_SAMPLE))
    {
        lpStaticProc = (WNDPROC)SetWindowLongPtr( hCtl,
                                               GWLP_WNDPROC,
                                               (LONG_PTR)PrintPageSetupPaintProc );
    }

    if ((pPD->Flags & PD_NONETWORKBUTTON))
    {
        if (hCtl = GetDlgItem(hDlg, ID_BOTH_P_NETWORK))
        {
            EnableWindow(hCtl, FALSE);
            ShowWindow(hCtl, SW_HIDE);
        }
    }
    else if (!(pPI->pPSD))
    {
#ifdef WINNT
        AddNetButton( hDlg,
                      ((pPD->Flags & PD_ENABLESETUPTEMPLATE)
                          ? pPD->hInstance : g_hinst),
                      FILE_BOTTOM_MARGIN,
                      (pPD->Flags & (PD_ENABLESETUPTEMPLATE |
                                     PD_ENABLESETUPTEMPLATEHANDLE))
                          ? FALSE : TRUE,
                      FALSE,
                      TRUE );
#endif
        //
        //  The button can be added in two ways -
        //      statically (they have it predefined in their template) and
        //      dynamically (successful call to AddNetButton).
        //
#ifdef WINNT
        if (!IsNetworkInstalled())
#else
        if (!GetSystemMetrics(SM_NETWORK))
#endif
        {
            hCtl = GetDlgItem(hDlg, ID_BOTH_P_NETWORK);

            EnableWindow(hCtl, FALSE);
            ShowWindow(hCtl, SW_HIDE);
        }
    }

#ifdef WINNT
    if (!(pPD->Flags & PD_SHOWHELP))
    {
        if (hCtl = GetDlgItem(hDlg, ID_BOTH_P_HELP))
        {
            EnableWindow(hCtl, FALSE);
            ShowWindow(hCtl, SW_HIDE);

            //
            //  Move the window out of this spot so that no overlap
            //  will be detected when adding the network button.
            //
            MoveWindow(hCtl, -8000, -8000, 20, 20, FALSE);
        }
    }
#endif

    //
    //  Provide backward compatibility for old-style-template radio buttons.
    //
    if (hCtl = GetDlgItem(hDlg, ID_SETUP_R_DEFAULT))
    {
        TCHAR szBuf[MAX_DEV_SECT];
        TCHAR szDefFormat[MAX_DEV_SECT];

        if (pPI->szDefaultPrinter[0])
        {
            if (!CDLoadString( g_hinst,
                             iszDefCurOn,
                             szDefFormat,
                             ARRAYSIZE(szDefFormat) ))
            {
                StoreExtendedError(CDERR_LOADSTRFAILURE);
                goto InitSetupDlg_ConstructFailure;
            }

            wnsprintf(szBuf, ARRAYSIZE(szBuf), szDefFormat, pPI->szDefaultPrinter);
        }
        else
        {
            szBuf[0] = CHAR_NULL;
            EnableWindow(hCtl, FALSE);
        }
        SetDlgItemText(hDlg, ID_SETUP_S_DEFAULT, szBuf);

        if ( pPI->pCurPrinter &&
             pPI->pCurPrinter->pPrinterName &&
             !lstrcmp(pPI->pCurPrinter->pPrinterName, pPI->szDefaultPrinter) )
        {
            CheckRadioButton( hDlg,
                              ID_SETUP_R_DEFAULT,
                              ID_SETUP_R_SPECIFIC,
                              ID_SETUP_R_DEFAULT );
        }
        else
        {
            CheckRadioButton( hDlg,
                              ID_SETUP_R_DEFAULT,
                              ID_SETUP_R_SPECIFIC,
                              ID_SETUP_R_SPECIFIC );
        }
    }

    if (pPD->lpfnSetupHook)
    {
        DWORD dwHookRet;
        LPSETUPHOOKPROC lpfnSetupHook = GETSETUPHOOKFN(pPD);

#ifdef UNICODE
        if (pPI->ApiType == COMDLG_ANSI)
        {
            ThunkPrintDlgW2A(pPI);
            TransferPDA2PSD(pPI);

            pPI->NestCtr++;
            dwHookRet = (*lpfnSetupHook)( hDlg,
                                          WM_INITDIALOG,
                                          wParam,
                                          (pPI->pPSD)
                                              ? (LONG_PTR)pPI->pPSD
                                              : (LONG_PTR)pPI->pPDA ) != 0;
            pPI->NestCtr--;

            if (dwHookRet)
            {
                TransferPSD2PDA(pPI);
                ThunkPrintDlgA2W(pPI);
                if (pPI->NestCtr == 0)
                {
                    TransferPD2PSD(pPI);
                }
            }
        }
        else
#endif
        {
            TransferPD2PSD(pPI);
            dwHookRet = (*lpfnSetupHook)( hDlg,
                                          WM_INITDIALOG,
                                          wParam,
                                          (pPI->pPSD)
                                              ? (LONG_PTR)pPI->pPSD
                                              : (LONG_PTR)pPD ) != 0;
            TransferPSD2PD(pPI);
        }


        return (dwHookRet);
    }

    return (TRUE);

InitSetupDlg_ConstructFailure:

    if (!GetStoredExtendedError())
    {
        StoreExtendedError(PDERR_INITFAILURE);
    }

    return (0xFFFFFFFF);
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintUpdateSetupDlg
//
//  Update the print setup and page setup dialogs with the new settings.
//
////////////////////////////////////////////////////////////////////////////

VOID PrintUpdateSetupDlg(
    HWND hDlg,
    PPRINTINFO pPI,
    LPDEVMODE pDM,
    BOOL fResetContent)
{
    HWND hCtl;
    UINT Count;
    UINT Orientation = 0;


    //
    //  Update the Size combo box.
    //
    if (hCtl = GetDlgItem(hDlg, ID_SETUP_C_SIZE))
    {
        if (fResetContent)
        {
            PrintInitPaperCombo( pPI,
                                 hCtl,
                                 GetDlgItem(hDlg, ID_SETUP_S_SIZE),
                                 pPI->pCurPrinter,
                                 pDM,
                                 DC_PAPERNAMES,
                                 CCHPAPERNAME,
                                 DC_PAPERS );
            //
            //  PrintInitPaperCombo will turn off the hour glass cursor, so
            //  turn it back on.
            //
            HourGlass(TRUE);
        }
        else
        {
            Count = (UINT) SendMessage(hCtl, CB_GETCOUNT, 0, 0);
            while (Count != 0)
            {
                Count--;
                if (pDM->dmPaperSize == (SHORT)SendMessage( hCtl,
                                                            CB_GETITEMDATA,
                                                            Count,
                                                            0 ) )
                {
                    break;
                }
            }

            SendMessage( hCtl,
                         CB_SETCURSEL,
                         Count,
                         0 );
        }
    }

    //
    //  Update the Source combo box.
    //
    if (hCtl = GetDlgItem(hDlg, ID_SETUP_C_SOURCE))
    {
        if (fResetContent)
        {
            PrintInitPaperCombo( pPI,
                                 hCtl,
                                 GetDlgItem(hDlg, ID_SETUP_S_SOURCE),
                                 pPI->pCurPrinter,
                                 pDM,
                                 DC_BINNAMES,
                                 CCHBINNAME,
                                 DC_BINS );
            //
            //  PrintInitPaperCombo will turn off the hour glass cursor, so
            //  turn it back on.
            //
            HourGlass(TRUE);
        }
        else
        {
            Count = (UINT) SendMessage(hCtl, CB_GETCOUNT, 0, 0);
            while (Count != 0)
            {
                Count--;
                if (pDM->dmDefaultSource == (SHORT)SendMessage( hCtl,
                                                                CB_GETITEMDATA,
                                                                Count,
                                                                0 ) )
                {
                    break;
                }
            }

            SendMessage( hCtl,
                         CB_SETCURSEL,
                         Count,
                         0 );
        }
    }

    //
    //  Update the Orientation radio buttons.
    //
    if (GetDlgItem(hDlg, ID_SETUP_R_PORTRAIT))
    {
        Orientation = pDM->dmOrientation + ID_SETUP_R_PORTRAIT - DMORIENT_PORTRAIT;
        PrintSetOrientation( hDlg,
                             pPI,
                             pDM,
                             IsDlgButtonChecked(hDlg, ID_SETUP_R_PORTRAIT)
                                 ? ID_SETUP_R_PORTRAIT
                                 : ID_SETUP_R_LANDSCAPE,
                             Orientation );
    }

    //
    //  Update the Duplex radio buttons.
    //
    if (GetDlgItem(hDlg, ID_SETUP_R_NONE))
    {
        PrintSetDuplex( hDlg,
                        pDM,
                        pDM->dmDuplex + ID_SETUP_R_NONE - DMDUP_SIMPLEX );
    }

    //
    //  Update the page setup sample picture.
    //
    if ((Orientation == 0) && (hCtl = GetDlgItem(hDlg, ID_SETUP_W_SAMPLE)))
    {
        Orientation = pDM->dmOrientation + ID_SETUP_R_PORTRAIT - DMORIENT_PORTRAIT;
        PrintUpdatePageSetup( hDlg,
                              pPI,
                              pDM,
                              0,
                              Orientation );
    }

    //
    //  Update the Default/Specific Printer radio buttons.
    //
    if (hCtl = GetDlgItem(hDlg, ID_SETUP_R_DEFAULT))
    {
        if ( pPI->pCurPrinter &&
             pPI->pCurPrinter->pPrinterName &&
             !lstrcmp(pPI->pCurPrinter->pPrinterName, pPI->szDefaultPrinter) )
        {
            CheckRadioButton( hDlg,
                              ID_SETUP_R_DEFAULT,
                              ID_SETUP_R_SPECIFIC,
                              ID_SETUP_R_DEFAULT );
        }
        else
        {
            CheckRadioButton( hDlg,
                              ID_SETUP_R_DEFAULT,
                              ID_SETUP_R_SPECIFIC,
                              ID_SETUP_R_SPECIFIC );
        }
    }
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintSetCopies
//
//  Sets the appropriate number of copies in the PrintDlg structure and
//  in the DevMode structure.
//
////////////////////////////////////////////////////////////////////////////

BOOL PrintSetCopies(
    HWND hDlg,
    PPRINTINFO pPI,
    UINT Id)
{
    LPPRINTDLG pPD = pPI->pPD;
    LPDEVMODE pDM;
    DWORD dwMaxCopies;
    DWORD dwCollate;
    BOOL bAllowCollate;


    if ( (pPD->hDevMode) &&
         (pDM = GlobalLock(pPD->hDevMode)) )
    {
#ifdef UNICODE
        //
        //  If we're coming from a WOW app, we need to only set
        //  the copies in the devmode if the PD_USEDEVMODECOPIES
        //  flag is set.
        //
        if (IS16BITWOWAPP(pPD))
        {
            if (pPD->Flags & PD_USEDEVMODECOPIES)
            {
                pDM->dmCopies = pPD->nCopies;
                pPD->nCopies = 1;
            }
            else
            {
                pDM->dmCopies = 1;
            }

            return (TRUE);
        }
#endif
        if ( (!(pDM->dmFields & DM_COPIES)) ||
             ((!(pPI->pPSD)) &&
              (pPI->ProcessVersion < 0x40000) &&
              (!(pPD->Flags & PD_USEDEVMODECOPIES))) )
        {
LeaveInfoInPD:
            //
            //  The driver cannot do copies, so leave the
            //  copy/collate info in the pPD.
            //
            pDM->dmCopies = 1;
            SetField(pDM, dmCollate, DMCOLLATE_FALSE);
        }
        else if ( (pDM->dmSpecVersion < 0x0400) ||
                  (!(pDM->dmFields & DM_COLLATE)) )
        {
            //
            //  The driver can do copies, but not collate.
            //  Where the info goes depends on the PD_COLLATE flag.
            //
            if (pPD->Flags & PD_COLLATE)
            {
                goto LeaveInfoInPD;
            }
            else
            {
                goto PutInfoInDevMode;
            }
        }
        else
        {
PutInfoInDevMode:
            //
            //  Make sure we have a current printer.
            //
            if (!pPI->pCurPrinter)
            {
                goto LeaveInfoInPD;
            }

            //
            //  Make sure the driver can support the number
            //  of copies requested and collation.
            //
            dwMaxCopies = DeviceCapabilities(
                                     pPI->pCurPrinter->pPrinterName,
                                     pPI->pCurPrinter->pPortName,
                                     DC_COPIES,
                                     NULL,
                                     NULL );

            //
            // If DeviceCapabilities() returns error, set the copy number to 1
            //
            if ((dwMaxCopies < 1) || (dwMaxCopies == (DWORD)(-1)))
            {
                dwMaxCopies = 1;
            }
            if (dwMaxCopies < pPD->nCopies)
            {
                if (pPD->Flags & PD_USEDEVMODECOPIES)
                {
                    PrintEditError( hDlg,
                                    (Id == ID_PRINT_C_NAME)
                                        ? ID_PRINT_E_COPIES
                                        : ID_BOTH_P_PROPERTIES,
                                    iszTooManyCopies,
                                    dwMaxCopies );

                    GlobalUnlock(pPD->hDevMode);
                    return (FALSE);
                }

                goto LeaveInfoInPD;
            }

            dwCollate = DeviceCapabilities(
                                     pPI->pCurPrinter->pPrinterName,
                                     pPI->pCurPrinter->pPortName,
                                     DC_COLLATE,
                                     NULL,
                                     NULL );

            bAllowCollate = ((dwCollate < 1) || (dwCollate == (DWORD)-1)) ? FALSE : TRUE;

            //
            //  The driver can do both copies and collate,
            //  so move the info to the devmode.
            //
            pDM->dmCopies = pPD->nCopies;
            SetField( pDM,
                      dmCollate,
                      (bAllowCollate && (pPD->Flags & PD_COLLATE))
                          ? DMCOLLATE_TRUE
                          : DMCOLLATE_FALSE );
            pPD->nCopies = 1;
            pPD->Flags &= ~PD_COLLATE;
        }

        GlobalUnlock(pPD->hDevMode);
    }

    return (TRUE);
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintSetMinMargins
//
////////////////////////////////////////////////////////////////////////////

VOID PrintSetMinMargins(
    HWND hDlg,
    PPRINTINFO pPI,
    LPDEVMODE pDM)
{
    LPPAGESETUPDLG pPSD = pPI->pPSD;
    HDC hDC;
    RECT rtMinMargin;


    if (!pPSD)
    {
        return;
    }

    if (pPSD->Flags & PSD_MINMARGINS)
    {
        //
        //  Convert passed in margins to 10th of MMs.
        //
        if (pPSD->Flags & PSD_INHUNDREDTHSOFMILLIMETERS)
        {
            pPI->RtMinMarginMMs.left   = pPSD->rtMinMargin.left / 10;
            pPI->RtMinMarginMMs.top    = pPSD->rtMinMargin.top / 10;
            pPI->RtMinMarginMMs.right  = pPSD->rtMinMargin.right / 10;
            pPI->RtMinMarginMMs.bottom = pPSD->rtMinMargin.bottom / 10;
        }
        else           // PSD_INTHOUSANDTHSOFINCHES
        {
            pPI->RtMinMarginMMs.left   = pPSD->rtMinMargin.left * MMS_PER_INCH / 100;
            pPI->RtMinMarginMMs.top    = pPSD->rtMinMargin.top * MMS_PER_INCH / 100;
            pPI->RtMinMarginMMs.right  = pPSD->rtMinMargin.right * MMS_PER_INCH / 100;
            pPI->RtMinMarginMMs.bottom = pPSD->rtMinMargin.bottom * MMS_PER_INCH / 100;
        }
    }
    else
    {
        //
        //  Default to no minimum if we can't get the info.
        //
        pPI->RtMinMarginMMs.left   = 0;
        pPI->RtMinMarginMMs.top    = 0;
        pPI->RtMinMarginMMs.right  = 0;
        pPI->RtMinMarginMMs.bottom = 0;
        pPSD->rtMinMargin.left   = 0;
        pPSD->rtMinMargin.top    = 0;
        pPSD->rtMinMargin.right  = 0;
        pPSD->rtMinMargin.bottom = 0;

        //
        //  Calculate new min margins from driver.
        //
        if (hDC = CreateIC(NULL, pDM->dmDeviceName, NULL, pDM))
        {
            //
            //  These are in PIXELS.
            //
            int nPageWidth = GetDeviceCaps(hDC, PHYSICALWIDTH);
            int nPageHeight = GetDeviceCaps(hDC, PHYSICALHEIGHT);
            int nPrintWidth = GetDeviceCaps(hDC, HORZRES);
            int nPrintHeight = GetDeviceCaps(hDC, VERTRES);
            int nOffsetWidth = GetDeviceCaps(hDC, PHYSICALOFFSETX);
            int nOffsetHeight = GetDeviceCaps(hDC, PHYSICALOFFSETY);
            int nPerInchWidth = GetDeviceCaps(hDC, LOGPIXELSX);
            int nPerInchHeight = GetDeviceCaps(hDC, LOGPIXELSY);

            //
            //  Calculate min margins in PIXELS.
            //
            rtMinMargin.left   = nOffsetWidth;
            rtMinMargin.top    = nOffsetHeight;
            rtMinMargin.right  = nPageWidth - nPrintWidth - nOffsetWidth;
            rtMinMargin.bottom = nPageHeight - nPrintHeight - nOffsetHeight;

            //
            //  Convert to 10ths of MMs.
            //
            if (nPerInchWidth && nPerInchHeight)
            {
                pPI->RtMinMarginMMs.left   = rtMinMargin.left * MMS_PER_INCH / nPerInchWidth / 10;
                pPI->RtMinMarginMMs.top    = rtMinMargin.top * MMS_PER_INCH / nPerInchHeight / 10;
                pPI->RtMinMarginMMs.right  = rtMinMargin.right * MMS_PER_INCH / nPerInchHeight / 10;
                pPI->RtMinMarginMMs.bottom = rtMinMargin.bottom * MMS_PER_INCH / nPerInchHeight / 10;
            }

            if (pPSD->Flags & PSD_INHUNDREDTHSOFMILLIMETERS)
            {
                //
                //  Convert to 100ths of MMs.
                //
                pPSD->rtMinMargin.left   = pPI->RtMinMarginMMs.left / 10;
                pPSD->rtMinMargin.top    = pPI->RtMinMarginMMs.top / 10;
                pPSD->rtMinMargin.right  = pPI->RtMinMarginMMs.right / 10;
                pPSD->rtMinMargin.bottom = pPI->RtMinMarginMMs.bottom / 10;
            }
            else           // PSD_INTHOUSANDTHSOFINCHES
            {
                //
                //  Convert to 1000ths of inches.
                //
                if (nPerInchWidth && nPerInchHeight)
                {
                    pPSD->rtMinMargin.left   = rtMinMargin.left * 1000 / nPerInchWidth;
                    pPSD->rtMinMargin.top    = rtMinMargin.top * 1000 / nPerInchHeight;
                    pPSD->rtMinMargin.right  = rtMinMargin.right * 1000 / nPerInchHeight;
                    pPSD->rtMinMargin.bottom = rtMinMargin.bottom * 1000 / nPerInchHeight;
                }
            }

            DeleteDC(hDC);
        }
    }
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintSetupMargins
//
////////////////////////////////////////////////////////////////////////////

VOID PrintSetupMargins(
    HWND hDlg,
    PPRINTINFO pPI)
{
    TCHAR szMars[32];
    TCHAR szText[16];
    int ids[4] = { ID_SETUP_E_LEFT,
                   ID_SETUP_E_TOP,
                   ID_SETUP_E_RIGHT,
                   ID_SETUP_E_BOTTOM };
    int i;
    HWND hEdt;


    //
    //  Margins are only available from the PageSetupDlg.
    //
    if (!(pPI->pPSD))
    {
        return;
    }

    for (i = 0; i < 4; i++)
    {
        if (hEdt = GetDlgItem(hDlg, ids[i]))
        {
            //
            //  "999999" is the maximum value.
            //
            SendMessage(hEdt, EM_LIMITTEXT, MARGIN_EDIT_SIZE, 0);

            lpEditMarginProc =
                (WNDPROC)SetWindowLongPtr( hEdt,
                                        GWLP_WNDPROC,
                                        (LONG_PTR)PrintEditMarginProc );

        }
    }

    if (!GetLocaleInfo( LOCALE_USER_DEFAULT,
                        LOCALE_SDECIMAL,
                        szText,
                        16 ))
    {
        cIntlDecimal = CHAR_DOT;
    }
    else
    {
        cIntlDecimal = szText[0];
    }

    switch (pPI->pPSD->Flags & (PSD_INTHOUSANDTHSOFINCHES |
                                PSD_INHUNDREDTHSOFMILLIMETERS))
    {
        case ( PSD_INHUNDREDTHSOFMILLIMETERS ) :
        {
            CDLoadString(g_hinst, iszMarginsMillimeters, szMars, 32);
            CDLoadString(g_hinst, iszMillimeters, cIntlMeasure, 5);

            break;
        }
        case ( PSD_INTHOUSANDTHSOFINCHES ) :
        {
            CDLoadString(g_hinst, iszMarginsInches, szMars, 32);
            CDLoadString(g_hinst, iszInches, cIntlMeasure, 5);

            break;
        }
    }

    cchIntlMeasure = lstrlen(cIntlMeasure);

    SetWindowText(GetDlgItem(hDlg, ID_SETUP_G_MARGINS), szMars);
    pPI->PtMargins.x = 2 * (IS_KEY_PRESSED(pPI->PtMargins.x / 4) &&
                            IS_KEY_PRESSED(pPI->PtMargins.y / 4)
                                ? sizeof(WCHAR)
                                : sizeof(CHAR));
    pPI->PtMargins.y = 2 * pPI->PtMargins.x;
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintSetMargin
//
////////////////////////////////////////////////////////////////////////////

VOID PrintSetMargin(
    HWND hDlg,
    PPRINTINFO pPI,
    UINT Id,
    LONG lValue)
{
    HWND hEdt;
    TCHAR szText[32];
    TCHAR szILZero[2];
    LONG lFract;


    if (hEdt = GetDlgItem(hDlg, Id))
    {
        switch (pPI->pPSD->Flags & (PSD_INTHOUSANDTHSOFINCHES |
                                    PSD_INHUNDREDTHSOFMILLIMETERS))
        {
            case ( PSD_INHUNDREDTHSOFMILLIMETERS ) :
            {
                lFract = lValue % 100;
                wsprintf( szText,
                          lFract ? TEXT("%lu%c%02lu") : TEXT("%lu"),
                          lValue / 100,
                          cIntlDecimal,
                          lFract );
                break;
            }
            case ( PSD_INTHOUSANDTHSOFINCHES ) :
            {
                lFract = lValue % 1000;
                wsprintf( szText,
                          lFract ? TEXT("%lu%c%03lu") : TEXT("%lu"),
                          lValue / 1000,
                          cIntlDecimal,
                          lFract );
                break;
            }
        }

        //
        //  Remove trailing zeros off of fraction.
        //
        if (lFract)
        {
            LPTSTR pStr = szText + lstrlen(szText) - 1;

            while (*pStr == TEXT('0'))
            {
                *pStr-- = TEXT('\0');
            }
        }

        //
        //  Determine if a leading zero is to be used and write the
        //  text to the edit window.
        //
        if (!GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ILZERO, szILZero, 2))
        {
            szILZero[0] = TEXT('0');
        }
        SetWindowText( hEdt,
                       szText + (szText[0] == TEXT('0') &&
                                 szText[1] == cIntlDecimal &&
                                 szILZero[0] == TEXT('0')) );
    }
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintGetMargin
//
////////////////////////////////////////////////////////////////////////////

VOID PrintGetMargin(
    HWND hEdt,
    PPRINTINFO pPI,
    LONG lMin,
    LONG *plMargin,
    LONG *plSample)
{
    TCHAR szText[16];
    TCHAR *pText;
    TCHAR *pFrac;

    GetWindowText(hEdt, szText, ARRAYSIZE(szText));
    szText[ARRAYSIZE(szText)-1] = 0;

    *plMargin = ConvertStringToInteger(szText);

    for (pText = szText; *pText;)
    {
        if (*pText++ == cIntlDecimal)
        {
            break;
        }
    }

    for (pFrac = pText; *pFrac; pFrac++)
    {
        if (*pFrac == cIntlMeasure[0])
        {
            *pFrac = CHAR_NULL;
            break;
        }

        if (*pFrac == cIntlDecimal)
        {
            *pFrac = CHAR_NULL;
            break;
        }
    }

    StrCatBuff(szText, TEXT("000"), ARRAYSIZE(szText));

    switch (pPI->pPSD->Flags & (PSD_INTHOUSANDTHSOFINCHES |
                                PSD_INHUNDREDTHSOFMILLIMETERS))
    {
        case ( PSD_INTHOUSANDTHSOFINCHES ) :
        {
            //
            //  In 1000ths of inches.
            //
            *plMargin *= 1000;
            pText[3] = CHAR_NULL;
            *plMargin += ConvertStringToInteger(pText);
            *plMargin = max(lMin, *plMargin);

            //
            //  In 10ths of MMs.
            //
            *plSample = *plMargin * MMS_PER_INCH / 1000;

            break;
        }
        case ( PSD_INHUNDREDTHSOFMILLIMETERS ) :
        {
            //
            //  In 100ths of MMs.
            //
            *plMargin *= 100 ;
            pText[2] = CHAR_NULL;
            *plMargin += ConvertStringToInteger(pText);
            *plMargin = max(lMin, *plMargin);

            //
            //  In 10ths of MMs.
            //
            *plSample = *plMargin / 10;

            break;
        }
    }
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintInitBannerAndQuality
//
//  Reset PRINT DLG items dependent upon which printer was selected.
//  Assumes that pPD->hDevNames is non-NULL.  pPD->hDevMode non-NULL.
//
////////////////////////////////////////////////////////////////////////////

BOOL PrintInitBannerAndQuality(
    HWND hDlg,
    PPRINTINFO pPI,
    LPPRINTDLG pPD)
{
    HWND hCtl;
    BOOL bResult = TRUE;
    LPDEVMODE pDM = NULL;
    LPDEVNAMES pDN = NULL;
    TCHAR szText[MAX_DEV_SECT];


    //
    //  ID_PRINT_S_DEFAULT is from one of the old templates.
    //
    if (GetDlgItem(hDlg, ID_PRINT_S_DEFAULT))
    {
        if (!pPD->hDevNames ||
            !(pDN = GlobalLock(pPD->hDevNames)))
        {
            StoreExtendedError(CDERR_MEMLOCKFAILURE);
            return (FALSE);
        }

        if (PrintCreateBanner(hDlg, pDN, szText, MAX_DEV_SECT))
        {
            SetDlgItemText(hDlg, ID_PRINT_S_DEFAULT, szText);
        }
        else
        {
            //
            //  PrintCreateBanner sets the extended error.
            //
            bResult = FALSE;
        }

        GlobalUnlock(pPD->hDevNames);
    }

    //
    //  If the driver says it can do copies, pay attention to what the
    //  app requested.  If it cannot do copies, check & disable the
    //  checkbox.
    //
    if (pPD->hDevMode)
    {
        if (!(pDM = GlobalLock(pPD->hDevMode)))
        {
            StoreExtendedError(CDERR_MEMLOCKFAILURE);
            return (FALSE);
        }

        //
        //  Enable print quality, if it exists.
        //
        if (hCtl = GetDlgItem(hDlg, ID_PRINT_S_QUALITY))
        {
            EnableWindow(hCtl, TRUE);
        }
        if (hCtl = GetDlgItem(hDlg, ID_PRINT_C_QUALITY))
        {
            EnableWindow(hCtl, TRUE);

            PrintInitQuality( hCtl,
                              pDM->dmSpecVersion <= 0x0300 ? 0L : pPD,
                              pDM->dmPrintQuality );
        }

        //
        //  If PD_USEDEVMODECOPIES(COLLATE), disable collate if the driver
        //  cannot collate.
        //
        if (hCtl = GetDlgItem(hDlg, ID_PRINT_X_COLLATE))
        {
            if ( pDM->dmFields & DM_COLLATE ||
                 !(pPD->Flags & PD_USEDEVMODECOPIES) )
            {
                EnableWindow(hCtl, TRUE);
                CheckDlgButton( hDlg,
                                ID_PRINT_X_COLLATE,
                                (pPI->Status & PI_COLLATE_REQUESTED)
                                    ? TRUE : FALSE );
            }
            else
            {
                EnableWindow(hCtl, FALSE);
                CheckDlgButton(hDlg, ID_PRINT_X_COLLATE, FALSE);
            }
        }

        //
        //  If PD_USEDEVMODECOPIES(COLLATE), disable copies if the driver
        //  cannot copy.
        //
        if (hCtl = GetDlgItem(hDlg, ID_PRINT_E_COPIES))
        {
            if ( pDM->dmFields & DM_COPIES ||
                 !(pPD->Flags & PD_USEDEVMODECOPIES) )
            {
                SetDlgItemInt(hDlg, ID_PRINT_E_COPIES, pPD->nCopies, FALSE);
                EnableWindow(hCtl, TRUE);
            }
            else
            {
                SetDlgItemInt(hDlg, ID_PRINT_E_COPIES, 1, FALSE);
                EnableWindow(hCtl, FALSE);
            }
        }

        //
        //  Display the appropriate collate icon.
        //
        if (hCtl = GetDlgItem(hDlg, ID_PRINT_I_COLLATE))
        {
            SetWindowLong( hCtl,
                           GWL_STYLE,
                           GetWindowLong(hCtl, GWL_STYLE) | SS_CENTERIMAGE );
            ShowWindow(hCtl, SW_HIDE);
            SendMessage( hCtl,
                         STM_SETICON,
                         IsDlgButtonChecked(hDlg, ID_PRINT_X_COLLATE)
                             ? (LONG_PTR)hIconCollate
                             : (LONG_PTR)hIconNoCollate,
                         0L );
            ShowWindow(hCtl, SW_SHOW);
        }

        GlobalUnlock(pPD->hDevMode);
    }
    else
    {
        //
        //  Disable the print quality, collate, and copies.
        //
        if (hCtl = GetDlgItem(hDlg, ID_PRINT_S_QUALITY))
        {
            EnableWindow(hCtl, FALSE);
        }
        if (hCtl = GetDlgItem(hDlg, ID_PRINT_C_QUALITY))
        {
            EnableWindow(hCtl, FALSE);
        }
        if (hCtl = GetDlgItem(hDlg, ID_PRINT_X_COLLATE))
        {
            EnableWindow(hCtl, FALSE);
        }
        if (hCtl = GetDlgItem(hDlg, ID_PRINT_E_COPIES))
        {
            EnableWindow(hCtl, FALSE);
        }
    }

    return (bResult);
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintCreateBanner
//
//  Create "Printer: Prn on Port" or "Printer:  System Printer (Prn)".
//
////////////////////////////////////////////////////////////////////////////

BOOL PrintCreateBanner(
    HWND hDlg,
    LPDEVNAMES pDN,
    LPTSTR psBanner,
    UINT cchBanner)
{
    if (GetDlgItem(hDlg, ID_BOTH_S_PRINTER))
    {
        psBanner[0] = CHAR_NULL;
    }
    else if (!CDLoadString( g_hinst,
                          iszPrinter,
                          psBanner,
                          cchBanner ))
    {
        goto LoadStrFailure;
    }

    if (pDN->wDefault & DN_DEFAULTPRN)
    {
        TCHAR szSysPrn[MAX_DEV_SECT];

        if (!CDLoadString(g_hinst, iszSysPrn, szSysPrn, MAX_DEV_SECT))
        {
            goto LoadStrFailure;
        }
        StrCatBuff(psBanner, (LPTSTR)szSysPrn, cchBanner);
        StrCatBuff(psBanner, (LPTSTR)pDN + pDN->wDeviceOffset, cchBanner);
        StrCatBuff(psBanner, (LPTSTR)TEXT(")"), cchBanner);
    }
    else
    {
        TCHAR szPrnOnPort[64];

        if (!CDLoadString(g_hinst, iszPrnOnPort, szPrnOnPort, 64))
        {
            goto LoadStrFailure;
        }
        StrCatBuff(psBanner, (LPTSTR)pDN + pDN->wDeviceOffset, cchBanner);
        StrCatBuff(psBanner, (LPTSTR)szPrnOnPort, cchBanner);
        StrCatBuff(psBanner, (LPTSTR)pDN + pDN->wOutputOffset, cchBanner);
    }

    return (TRUE);

LoadStrFailure:

    StoreExtendedError(CDERR_LOADSTRFAILURE);
    return (FALSE);
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintInitQuality
//
//  Initializes the Printer Quality Combobox.
//
//  Assumes pPD structure filled by caller.  If non-NULL, it's a 3.1 or
//  later driver.  If NULL, fill with default for 3.0.
//
////////////////////////////////////////////////////////////////////////////

VOID PrintInitQuality(
    HANDLE hCmb,
    LPPRINTDLG pPD,
    SHORT nQuality)
{
    SHORT nStringID;
    SHORT i;
    TCHAR szBuf[64];
    LPDEVMODE  pDM = NULL;
    LPDEVNAMES pDN = NULL;


    SendMessage(hCmb, CB_RESETCONTENT, 0, 0L);

    //
    //  Enum print qualities.
    //
    if (pPD && pPD->hDevMode && pPD->hDevNames)
    {
        HANDLE hPrnQ;                  // Memory handle for print qualities
        DWORD dw;                      // return from DC_ENUMRESOLUTIONS
        LPLONG pLong;                  // Pointer to pairs of longs
        LPTSTR psDevice;
        LPTSTR psPort;

        pDM = GlobalLock(pPD->hDevMode);
        pDN = GlobalLock(pPD->hDevNames);

        if (pDM->dmSpecVersion < 0x030A)
        {
            goto EnumResNotSupported;
        }

        psDevice = (LPTSTR)pDN + pDN->wDeviceOffset;
        psPort   = (LPTSTR)pDN + pDN->wOutputOffset;

        dw = DeviceCapabilities( psDevice,
                                 psPort,
                                 DC_ENUMRESOLUTIONS,
                                 NULL,
                                 NULL );
        if (!dw || (dw == (DWORD)(-1)))
        {
            goto EnumResNotSupported;
        }

        hPrnQ = GlobalAlloc(GHND, dw * 2 * sizeof(LONG));
        if (!hPrnQ)
        {
            goto EnumResNotSupported;
        }

        if (pLong = GlobalLock(hPrnQ))
        {
            dw = DeviceCapabilities( psDevice,
                                     psPort,
                                     DC_ENUMRESOLUTIONS,
                                     (LPTSTR)pLong,
                                     0 );

            for (nStringID = 0, i = (SHORT)(LOWORD(dw) - 1); i >= 0; i--)
            {
                DWORD xRes, yRes;

                if ((xRes = pLong[i * 2]) != (yRes = pLong[i * 2 + 1]) )
                {
                    wsprintf(szBuf, TEXT("%ld dpi x %ld dpi"), xRes, yRes);
                }
                else
                {
                    wsprintf(szBuf, TEXT("%ld dpi"), yRes);
                }

                SendMessage(hCmb, CB_INSERTSTRING, 0, (LONG_PTR)(LPTSTR)szBuf);
                SendMessage(hCmb, CB_SETITEMDATA, 0, xRes);

                if ( ((SHORT)xRes == nQuality) &&
                     ( (wWinVer < 0x030A) ||
                       !pDM->dmYResolution ||
                       (pDM->dmYResolution == (SHORT)yRes) ) )
                {
                    nStringID = i;
                }
            }
            GlobalUnlock(hPrnQ);
        }
        GlobalFree(hPrnQ);

        SendMessage(hCmb, CB_SETCURSEL, (WPARAM)nStringID, 0L);
    }
    else
    {
EnumResNotSupported:

        for ( i = -1, nStringID = iszDraftPrnQ;
              nStringID >= iszHighPrnQ;
              i--, nStringID-- )
        {
            if (!CDLoadString(g_hinst, nStringID, szBuf, 64))
            {
                return;
            }
            SendMessage(hCmb, CB_INSERTSTRING, 0, (LONG_PTR)(LPTSTR)szBuf);
            SendMessage(hCmb, CB_SETITEMDATA, 0, MAKELONG(i, 0));
        }

        if ((nQuality >= 0) || (nQuality < -4))
        {
            //
            //  Set to HIGH.
            //
            nQuality = -4;
        }
        SendMessage(hCmb, CB_SETCURSEL, (WPARAM)(nQuality + 4), 0L);
    }

    if (pDM)
    {
        GlobalUnlock(pPD->hDevMode);
    }
    if (pDN)
    {
        GlobalUnlock(pPD->hDevNames);
    }
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintChangeProperties
//
//  Puts up the dialog to modify the properties.
//
////////////////////////////////////////////////////////////////////////////

VOID PrintChangeProperties(
    HWND hDlg,
    UINT Id,
    PPRINTINFO pPI)
{
    LPPRINTDLG pPD = pPI->pPD;
    LPDEVMODE pDM;
    LONG cbNeeded;
    HANDLE hDevMode;
    WORD nCopies, nCollate;
    BOOL bTest;
    HWND hCtl;


    //
    //  There must be a devmode already.
    //
    if (!pPD->hDevMode)
    {
        return;
    }

    //
    //  Get the number of bytes needed for the devmode.
    //
    cbNeeded = DocumentProperties( hDlg,
                                   pPI->hCurPrinter,
                                   (pPI->pCurPrinter)
                                       ? pPI->pCurPrinter->pPrinterName
                                       : NULL,
                                   NULL,
                                   NULL,
                                   0 );

    //
    //  Reallocate the devmode to be sure there is enough room in it, and
    //  then put up the document properties dialog box.
    //
    if ( (cbNeeded > 0) &&
         (hDevMode = GlobalReAlloc(pPD->hDevMode, cbNeeded, GHND)) &&
         (pDM = GlobalLock(hDevMode)) )
    {
        //
        //  This is done here to make sure that the ReAlloc succeeded
        //  before trashing the old hDevMode.
        //
        pPD->hDevMode = hDevMode;

        //
        //  Set the number of copies and collation in the devmode before
        //  calling DocumentProperties, if appropriate.
        //
        nCopies = pDM->dmCopies;
        nCollate = pDM->dmCollate;
        if (Id == ID_PRINT_C_NAME)
        {
            //
            //  Get the number of copies from the edit control.
            //
            pDM->dmCopies = (WORD)GetDlgItemInt( hDlg,
                                                 ID_PRINT_E_COPIES,
                                                 &bTest,
                                                 FALSE );
            if ((!bTest) || (!pDM->dmCopies))
            {
                pDM->dmCopies = nCopies;
            }

            //
            //  Get the collation from the check box.
            //
            if ( (hCtl = GetDlgItem(hDlg, ID_PRINT_X_COLLATE)) &&
                 IsWindowEnabled(hCtl) )
            {
                SetField( pDM,
                          dmCollate,
                          (IsDlgButtonChecked(hDlg, ID_PRINT_X_COLLATE))
                              ? DMCOLLATE_TRUE
                              : DMCOLLATE_FALSE );
            }
        }
        else   // ID_SETUP_C_NAME
        {
            if ( (pDM->dmFields & DM_COPIES) &&
                 (pPI->ProcessVersion < 0x40000) &&
                 (!(pPD->Flags & PD_USEDEVMODECOPIES)) &&
                 (pPD->nCopies) )
            {
                pDM->dmCopies = pPD->nCopies;

                if (pDM->dmFields & DM_COLLATE)
                {
                    //
                    //  DM_COLLATE was specified, so dmCollate exists.
                    //
                    pDM->dmCollate = (pPD->Flags & PD_COLLATE)
                                         ? DMCOLLATE_TRUE
                                         : DMCOLLATE_FALSE;
                }
            }
        }

        //
        //  Put up the Document Properties dialog box.
        //
        if (DocumentProperties( hDlg,
                                pPI->hCurPrinter,
                                (pPI->pCurPrinter)
                                    ? pPI->pCurPrinter->pPrinterName
                                    : NULL,
                                pDM,
                                pDM,
                                DM_PROMPT | DM_MODIFY | DM_COPY ) == IDOK)
        {
            //
            //  Save the new number of copies and collation, if appropriate.
            //
            if (pDM->dmFields & DM_COPIES)
            {
                pPD->nCopies = pDM->dmCopies;
            }
            if (pDM->dmFields & DM_COLLATE)
            {
                if (pDM->dmCollate == DMCOLLATE_FALSE)
                {
                    pPD->Flags  &= ~PD_COLLATE;
                    pPI->Status &= ~PI_COLLATE_REQUESTED;
                }
                else
                {
                    pPD->Flags  |= PD_COLLATE;
                    pPI->Status |= PI_COLLATE_REQUESTED;
                }
            }

            //
            //  Update the dialog.
            //
            if (Id == ID_PRINT_C_NAME)
            {
                //
                //  Update the print dialog with the new info.
                //
                PrintInitBannerAndQuality(hDlg, pPI, pPD);
            }
            else   // ID_SETUP_C_NAME
            {
                //
                //  Update the print setup dialog with the new info.
                //
                PrintUpdateSetupDlg(hDlg, pPI, pDM, FALSE);
            }
        }
        else
        {
            //
            //  Operation cancelled.  Restore the number of copies
            //  and the collation in the devmode.
            //
            pDM->dmCopies = nCopies;
            SetField(pDM, dmCollate, nCollate);
        }

        GlobalUnlock(pPD->hDevMode);

        SendMessage( hDlg,
                     WM_NEXTDLGCTL,
                     (WPARAM)GetDlgItem(hDlg, IDOK),
                     1L );
    }
}

////////////////////////////////////////////////////////////////////////////
//
//  PrintPrinterChanged
//
////////////////////////////////////////////////////////////////////////////

VOID PrintPrinterChanged(
    HWND hDlg,
    UINT Id,
    PPRINTINFO pPI)
{
    LPPRINTDLG pPD = pPI->pPD;
    HANDLE hDM = NULL;
    LPDEVMODE pDM = NULL;
    LPDEVMODE pDMOld = NULL;
    HWND hCtl;
    UINT Orientation;
    LONG cbSize;
    DWORD dmSize;


    HourGlass(TRUE);

    //
    //  Close the old printer, if necessary.
    //
    if (pPI->hCurPrinter)
    {
        ClosePrinter(pPI->hCurPrinter);
        pPI->hCurPrinter = 0;
    }

    //
    //  Get the current printer from the combo box.
    //
    if (Id && (hCtl = GetDlgItem(hDlg, Id)))
    {
        TCHAR szPrinter[MAX_PRINTERNAME];
        DWORD ctr;

        SendMessage( hCtl,
                     CB_GETLBTEXT,
                     (WPARAM)SendMessage(hCtl, CB_GETCURSEL, 0, 0),
                     (LPARAM)(LPTSTR)szPrinter );

        pPI->pCurPrinter = NULL;
        for (ctr = 0; ctr < pPI->cPrinters; ctr++)
        {
            if (!lstrcmp(pPI->pPrinters[ctr].pPrinterName, szPrinter))
            {
                pPI->pCurPrinter = &pPI->pPrinters[ctr];
                break;
            }
        }
        if (!pPI->pCurPrinter)
        {
            HourGlass(FALSE);
            return;
        }
    }

    //
    //  Open the current printer.
    //
    OpenPrinter(pPI->pCurPrinter->pPrinterName, &pPI->hCurPrinter, NULL);

    //
    //  Build the device names.
    //
    PrintBuildDevNames(pPI);

    //
    //  Get the devmode information.
    //
    cbSize = DocumentProperties( hDlg,
                                 pPI->hCurPrinter,
                                 pPI->pCurPrinter->pPrinterName,
                                 NULL,
                                 NULL,
                                 0 );
    if (cbSize > 0)
    {
        hDM = GlobalAlloc(GHND, cbSize);

        //
        //  Get the default DevMode for the new printer.
        //
        if (hDM && (pDM = GlobalLock(hDM)) &&
            (DocumentProperties( hDlg,
                                 pPI->hCurPrinter,
                                 pPI->pCurPrinter->pPrinterName,
                                 pDM,
                                 NULL,
                                 DM_COPY ) == IDOK))
        {
            //
            //  See if we need to merge in old DevMode settings.
            //
            if (pPD->hDevMode && (pDMOld = GlobalLock(pPD->hDevMode)))
            {
                //
                //  Reset the PaperSource back to the Document Default.
                //
                if (pDM->dmFields & DM_DEFAULTSOURCE)
                {
                    pDMOld->dmFields |= DM_DEFAULTSOURCE;
                    pDMOld->dmDefaultSource = pDM->dmDefaultSource;
                }
                else
                {
                    pDMOld->dmFields &= ~DM_DEFAULTSOURCE;
                }

                //
                //  Copy relevant info from the old devmode to the new
                //  devmode.
                //
                dmSize = min(pDM->dmSize, pDMOld->dmSize);
                if (dmSize > FIELD_OFFSET(DEVMODE, dmFields))
                {
                    CopyMemory( &(pDM->dmFields),
                                &(pDMOld->dmFields),
                                dmSize - FIELD_OFFSET(DEVMODE, dmFields) );
                }

                //
                //  Free the old devmode.
                //
                GlobalUnlock(pPD->hDevMode);
                GlobalFree(pPD->hDevMode);
            }

            //
            //  Save the new DevMode in the pPD structure.
            //
            pPD->hDevMode = hDM;

            //
            //  Get the newly merged DevMode.
            //
            pDM->dmFields = pDM->dmFields & (DM_ORIENTATION | DM_PAPERSIZE  |
                                             DM_PAPERLENGTH | DM_PAPERWIDTH |
                                             DM_SCALE       | DM_COPIES     |
                                             DM_COLLATE     | DM_FORMNAME   |
                                             DM_DEFAULTSOURCE);
            DocumentProperties( hDlg,
                                pPI->hCurPrinter,
                                pPI->pCurPrinter->pPrinterName,
                                pDM,
                                pDM,
                                DM_MODIFY | DM_COPY );
            GlobalUnlock(hDM);
        }
        else if (hDM)
        {
            if (pDM)
            {
                GlobalUnlock(hDM);
            }
            GlobalFree(hDM);
        }
    }

    //
    //  Fill in the appropriate information for the rest of the
    //  Print or Print Setup dialog box.
    //
    if (Id == ID_PRINT_C_NAME)
    {
        PrintInitBannerAndQuality(hDlg, pPI, pPD);
    }
    else   // ID_SETUP_C_NAME
    {
        if (pPD->hDevMode && (pDM = GlobalLock(pPD->hDevMode)))
        {
            if (hCtl = GetDlgItem(hDlg, ID_SETUP_C_SIZE))
            {
                PrintInitPaperCombo( pPI,
                                     hCtl,
                                     GetDlgItem(hDlg, ID_SETUP_S_SIZE),
                                     pPI->pCurPrinter,
                                     pDM,
                                     DC_PAPERNAMES,
                                     CCHPAPERNAME,
                                     DC_PAPERS );
            }

            if (hCtl = GetDlgItem(hDlg, ID_SETUP_C_SOURCE))
            {
                PrintInitPaperCombo( pPI,
                                     hCtl,
                                     GetDlgItem(hDlg, ID_SETUP_S_SOURCE),
                                     pPI->pCurPrinter,
                                     pDM,
                                     DC_BINNAMES,
                                     CCHBINNAME,
                                     DC_BINS );
            }

            PrintInitOrientation(hDlg, pPI, pDM);
            Orientation = (pDM->dmOrientation == DMORIENT_PORTRAIT)
                              ? ID_SETUP_R_PORTRAIT
                              : ID_SETUP_R_LANDSCAPE;
            PrintSetOrientation(hDlg, pPI, pDM, Orientation, Orientation);

            PrintInitDuplex(hDlg, pDM);
            PrintSetDuplex( hDlg,
                            pDM,
                            pDM->dmDuplex + ID_SETUP_R_NONE - DMDUP_SIMPLEX );

            GlobalUnlock(pPD->hDevMode);
        }
    }

    //
    //  Update the status information.
    //
    PrintUpdateStatus(hDlg, pPI);

    HourGlass(FALSE);
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintCancelPrinterChanged
//
//  Opens the old printer since the user hit cancel.  The devmode and
//  devnames structures have already been set back to the old ones.
//
////////////////////////////////////////////////////////////////////////////

VOID PrintCancelPrinterChanged(
    PPRINTINFO pPI,
    LPTSTR pPrinterName)
{
    LPPRINTDLG pPD = pPI->pPD;
    PPRINTER_INFO_2 pCurPrinter;


    //
    //  Make sure we have a previous printer and a devmode.
    //
    if ((pPrinterName[0] == 0) || (!pPD->hDevMode))
    {
        return;
    }

    //
    //  Turn on the hour glass.
    //
    HourGlass(TRUE);

    //
    //  Find the current printer in the list.
    //
    pCurPrinter = PrintSearchForPrinter(pPI, pPrinterName);
    if (!pCurPrinter)
    {
        HourGlass(FALSE);
        return;
    }

    //
    //  Close the old printer, if necessary.
    //
    if (pPI->hCurPrinter)
    {
        ClosePrinter(pPI->hCurPrinter);
        pPI->hCurPrinter = 0;
    }

    //
    //  Save the current printer.
    //
    pPI->pCurPrinter = pCurPrinter;

    //
    //  Open the current printer.
    //
    OpenPrinter(pPI->pCurPrinter->pPrinterName, &pPI->hCurPrinter, NULL);

    //
    //  Turn off the hour glass.
    //
    HourGlass(FALSE);
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintUpdateStatus
//
////////////////////////////////////////////////////////////////////////////

VOID PrintUpdateStatus(
    HWND hDlg,
    PPRINTINFO pPI)
{
    TCHAR szSeparator[] = TEXT("; ");
    TCHAR szText[256];
    TCHAR szJobs[64];
    LPDEVMODE pDM;
    UINT Length;
    DWORD dwStatus;
    int ctr;
    TCHAR *ps;
    BOOL bFound;


    //
    //  Update the printer status information in the dialog.
    //
    if (!GetDlgItem(hDlg, ID_BOTH_S_STATUS) || (!pPI->pCurPrinter))
    {
        return;
    }

    //
    //  ----------------------  Update Status  ----------------------
    //
    szText[0] = CHAR_NULL;

    if (pPI->pCurPrinter->Attributes & PRINTER_ATTRIBUTE_DEFAULT)
    {
        CDLoadString(g_hinst, iszStatusDefaultPrinter, szText, 32);
    }

    Length = lstrlen(szText);
    dwStatus = pPI->pCurPrinter->Status;
    for (ctr = 0; ctr++ < 32; dwStatus = dwStatus >> 1)
    {
        if (dwStatus & 1)
        {
            CDLoadString( g_hinst,
                        iszStatusReady + ctr,
                        szText + lstrlen(szText),
                        32);
        }
    }

    if (szText[Length])
    {
        if (CDLoadString(g_hinst, iszStatusDocumentsWaiting, szJobs, 64))
        {
            wsprintf( szText + lstrlen(szText),
                      szJobs,
                      pPI->pCurPrinter->cJobs );
        }
    }
    else
    {
        CDLoadString(g_hinst, iszStatusReady, szText + Length, 32);
    }

    SetDlgItemText(hDlg, ID_BOTH_S_STATUS, szText);
    UpdateWindow(GetDlgItem(hDlg, ID_BOTH_S_STATUS));

    //
    //  ----------------------  Update Type  ----------------------
    //
    if (pPI->pCurPrinter->pDriverName)
    {
        lstrcpy(szText, pPI->pCurPrinter->pDriverName);
    }
    else
    {
        szText[0] = CHAR_NULL;
    }

    if (pPI->pPD->hDevMode && (pDM = GlobalLock(pPI->pPD->hDevMode)))
    {
        if (pDM->dmSpecVersion < 0x0400)
        {
            lstrcat(szText, TEXT(" (3.x)"));  // old driver designation
        }
        GlobalUnlock(pPI->pPD->hDevMode);
    }

    SetDlgItemText(hDlg, ID_BOTH_S_TYPE, szText);
    UpdateWindow(GetDlgItem(hDlg, ID_BOTH_S_TYPE));

    //
    //  ----------------------  Update Location  ----------------------
    //
    if (pPI->pCurPrinter->pLocation && pPI->pCurPrinter->pLocation[0])
    {
        bFound = FALSE;
        lstrcpy(szText, pPI->pCurPrinter->pLocation);
        for (ps = szText; *ps; ps++)
        {
            if (ps[0] == TEXT('\r') && ps[1] == TEXT('\n'))
            {
                *ps++ = CHAR_SEMICOLON;
                *ps   = CHAR_SPACE;
            }
            else
            {
                bFound = TRUE;
            }
        }
        if (!bFound)
        {
            goto ShowPortName;
        }
    }
    else
    {
ShowPortName:
        if (pPI->pCurPrinter->pPortName)
        {
            lstrcpy(szText, pPI->pCurPrinter->pPortName);
        }
        else
        {
            szText[0] = CHAR_NULL;
        }
    }

    EnableWindow(GetDlgItem(hDlg, ID_BOTH_S_WHERE), szText[0]);
    SetDlgItemText(hDlg, ID_BOTH_S_WHERE, szText);
    UpdateWindow(GetDlgItem(hDlg, ID_BOTH_S_WHERE));

    //
    //  ----------------------  Update Comment  ----------------------
    //
    if (pPI->pCurPrinter->pComment && pPI->pCurPrinter->pComment[0])
    {
        bFound = FALSE;
        lstrcpy(szText, pPI->pCurPrinter->pComment);
        for (ps = szText; *ps; ps++)
        {
            if (ps[0] == TEXT('\r') && ps[1] == TEXT('\n'))
            {
                *ps++ = CHAR_SEMICOLON;
                *ps   = CHAR_SPACE;
            }
            else
            {
                bFound = TRUE;
            }
        }
        if (!bFound)
        {
            //
            //  This is needed in case the comment field only has a
            //  carriage return in it.  Without this check, it will
            //  show a ";" in the comment field.  In this case, it
            //  should show "" in the comment field.
            //
            szText[0] = CHAR_NULL;
        }
    }
    else
    {
        szText[0] = CHAR_NULL;
    }

    EnableWindow(GetDlgItem(hDlg, ID_BOTH_S_COMMENT), szText[0]);
    SetDlgItemText(hDlg, ID_BOTH_S_COMMENT, szText);
    UpdateWindow(GetDlgItem(hDlg, ID_BOTH_S_COMMENT));
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintGetSetupInfo
//
//  Purpose:  Retrieve info from Print Setup dialog elements
//  Assumes:  hDevMode handle to valid DEVMODE structure
//  Returns:  TRUE if hDevMode valid, FALSE otherwise
//
////////////////////////////////////////////////////////////////////////////

BOOL PrintGetSetupInfo(
    HWND hDlg,
    LPPRINTDLG pPD)
{
    LPDEVMODE pDM = NULL;
    LPDEVNAMES pDN = NULL;
    HWND hCmb;
    int nInd;


    if ( !pPD->hDevMode ||
         !(pDM = GlobalLock(pPD->hDevMode)) )
    {
        return (FALSE);
    }

    // Don't need to do this - this is kept up to date.
    // pDM->dmFields |= DM_ORIENTATION;

    if (hCmb = GetDlgItem(hDlg, ID_SETUP_C_SIZE))
    {
        nInd = (int) SendMessage(hCmb, CB_GETCURSEL, 0, 0L);
        if (nInd != CB_ERR)
        {
        //  pDM->dmFields |= DM_PAPERSIZE;
            pDM->dmPaperSize = (SHORT)SendMessage( hCmb,
                                                   CB_GETITEMDATA,
                                                   nInd,
                                                   0 );
#ifndef WINNT
            if (pDM->dmSpecVersion >= 0x0400)
#endif
            {
            //  pDM->dmFields |= DM_FORMNAME;
                SendMessage( hCmb,
                             CB_GETLBTEXT,
                             nInd,
                             (LPARAM)pDM->dmFormName );
            }
        }
    }

    if (hCmb = GetDlgItem(hDlg, ID_SETUP_C_SOURCE))
    {
        nInd = (int) SendMessage(hCmb, CB_GETCURSEL, 0 , 0L);
        if (nInd != CB_ERR)
        {
        //  pDM->dmFields |= DM_DEFAULTSOURCE;
            pDM->dmDefaultSource = (SHORT)SendMessage( hCmb,
                                                       CB_GETITEMDATA,
                                                       nInd,
                                                       0 );
        }
    }

    if ( (pPD->hDevNames) &&
         (pDN = GlobalLock(pPD->hDevNames)) )
    {
        PrintReturnICDC(pPD, pDN, pDM);
        GlobalUnlock(pPD->hDevNames);
    }

    GlobalUnlock(pPD->hDevMode);

    return (TRUE);
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintSearchForPrinter
//
//  Returns the pointer to the PRINTER_INFO_2 structure for the printer
//  with the name pPrinterName.
//
////////////////////////////////////////////////////////////////////////////

PPRINTER_INFO_2 PrintSearchForPrinter(
    PPRINTINFO pPI,
    LPCTSTR lpsPrinterName)
{
    DWORD ctr;

    //
    //  Search for the printer.
    //
    for (ctr = 0; ctr < pPI->cPrinters; ctr++)
    {
        if (!lstrcmp(pPI->pPrinters[ctr].pPrinterName, lpsPrinterName))
        {
            //
            //  Found it.
            //
            return (&pPI->pPrinters[ctr]);
        }
    }

    //
    //  Did not find the printer.
    //
    return (NULL);
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintGetExtDeviceMode
//
////////////////////////////////////////////////////////////////////////////

#ifdef UNICODE

VOID PrintGetExtDeviceMode(
    HWND hDlg,
    PPRINTINFO pPI)
{
    DWORD ctr;
    LPDEVMODEA pDMA;
    LPDEVMODEW pDMW;
    int iResult;
    CHAR szPrinterNameA[MAX_PRINTERNAME];


    if (!pPI->bUseExtDeviceMode)
    {
        return;
    }

    //
    //  Allocate the array to hold whether or not a new devmode has been
    //  allocated for each of the printers.
    //
    //  This is necessary because if the call to ExtDeviceMode fails, then
    //  nothing was allocated.  The one that is currently in the pPrinters
    //  array is actually part of the big pPrinters array (from the call
    //  to GetPrinter - it wants one giant buffer).
    //
    if (pPI->cPrinters)
    {
        if (pPI->pAllocInfo)
        {
            GlobalFree(pPI->pAllocInfo);
        }
        pPI->pAllocInfo = (LPBOOL)GlobalAlloc( GPTR,
                                               pPI->cPrinters * sizeof(BOOL) );
    }

    if (pPI->pAllocInfo)
    {
        //
        //  If we were called from a WOW app with a NULL devmode,
        //  then call ExtDeviceMode to get a default devmode.
        //
        for (ctr = 0; ctr < pPI->cPrinters; ctr++)
        {
            //
            //  Convert the printer name from Unicode to ANSI.
            //
            SHUnicodeToAnsi(pPI->pPrinters[ctr].pPrinterName, szPrinterNameA, ARRAYSIZE(szPrinterNameA));

            //
            //  Call ExtDeviceMode with 0 flags to find out the
            //  size of the devmode structure we need.
            //
            iResult = ExtDeviceMode( hDlg,
                                     NULL,
                                     NULL,
                                     szPrinterNameA,
                                     NULL,
                                     NULL,
                                     NULL,
                                     0 );
            if (iResult < 0)
            {
                continue;
            }

            //
            //  Allocate the space.
            //
            pDMA = GlobalAlloc(GPTR, iResult);
            if (!pDMA)
            {
                continue;
            }

            //
            //  Call ExtDeviceMode to get the dummy devmode structure.
            //
            iResult = ExtDeviceMode( hDlg,
                                     NULL,
                                     pDMA,
                                     szPrinterNameA,
                                     NULL,
                                     NULL,
                                     NULL,
                                     DM_COPY );
            if (iResult < 0)
            {
                GlobalFree(pDMA);
                continue;
            }

            //
            //  Call AllocateUnicodeDevMode to allocate and copy the unicode
            //  version of this ANSI dev mode.
            //
            pDMW = AllocateUnicodeDevMode(pDMA);
            if (!pDMW)
            {
                GlobalFree(pDMA);
                continue;
            }

            //
            //  Store the pointer to the new devmode in the old pointer
            //  position.  We don't have to worry about freeing the
            //  current contents of pPrinter[ctr].pDevMode before sticking
            //  in the new pointer because in reality the pPrinter memory
            //  buffer is just one long allocation (the memory pDevmode
            //  points to is part of the pPrinters buffer).  So, when the
            //  buffer is freed at the end, the old devmode will be freed
            //  with it.
            //
            pPI->pPrinters[ctr].pDevMode = pDMW;
            pPI->pAllocInfo[ctr] = TRUE;

            //
            //  Free the ANSI dev mode.
            //
            GlobalFree(pDMA);
        }
    }
}
#endif


////////////////////////////////////////////////////////////////////////////
//
//  PrintEnumAndSelect
//
//  This routine enumerates the LOCAL and CONNECTED printers.
//  It is called at initialization and when a new printer is
//  added via the NETWORK... button.
//
//  If the second parameter is set, the first parameter is overridden.
//  When the second parameter is NULL, the first parameter is used.
//  In this case, if the first parameter is greater than the total
//  number of printers enumerated, then the last one in the list is
//  selected.
//
////////////////////////////////////////////////////////////////////////////

BOOL PrintEnumAndSelect(
    HWND hDlg,
    UINT Id,
    PPRINTINFO pPI,
    LPTSTR lpsPrinterToSelect,
    BOOL bEnumPrinters)
{
    HWND hCtl = ((hDlg && Id) ? GetDlgItem(hDlg, Id) : 0);
    LPPRINTDLG pPD = pPI->pPD;
    TCHAR szPrinter[MAX_PRINTERNAME];
    DWORD cbNeeded;
    DWORD cReturned;
    DWORD ctr;
    PPRINTER_INFO_2 pPrinters = NULL;


    //
    //  Enumerate the printers, if necessary.
    //
    if (bEnumPrinters)
    {
Print_Enumerate:
        //
        //  Save lpsPrinterToSelect in a local before it gets freed.
        //
        if (lpsPrinterToSelect)
        {
            lstrcpyn(szPrinter, lpsPrinterToSelect, ARRAYSIZE(szPrinter));
            lpsPrinterToSelect = szPrinter;
        }

        //
        //  Close and free any open printers.
        //
        PrintClosePrinters(pPI);

        //
        //  Clear out the error code.
        //
        StoreExtendedError(CDERR_GENERALCODES);

        //
        //  Enumerate the printers.
        //
        if (!EnumPrinters( PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS,
                           NULL,
                           2,
                           NULL,
                           0,
                           &cbNeeded,
                           &cReturned ))
        {
            if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
            {
                if (pPrinters = GlobalAlloc(GPTR, cbNeeded))
                {
                    if (EnumPrinters( PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS,
                                      NULL,
                                      2,
                                      (LPBYTE)pPrinters,
                                      cbNeeded,
                                      &cbNeeded,
                                      &cReturned ))
                    {
                        pPI->cPrinters = cReturned;
                        pPI->pPrinters =  pPrinters;
                        pPI->Status |= PI_PRINTERS_ENUMERATED;
                    }
                    else
                    {
                        StoreExtendedError(PDERR_NODEFAULTPRN);
                    }
                }
                else
                {
                    StoreExtendedError(CDERR_MEMALLOCFAILURE);
                }
            }
            else
            {
                StoreExtendedError(PDERR_NODEFAULTPRN);
            }
        }
        else
        {
            StoreExtendedError(PDERR_NODEFAULTPRN);
        }

        if (GetStoredExtendedError())
        {
            if (pPrinters)
            {
                GlobalFree(pPrinters);
            }
            return (FALSE);
        }

        //
        //  Make modifications for a WOW app.
        //
#ifdef UNICODE
        if (pPI->bUseExtDeviceMode)
        {
            PrintGetExtDeviceMode(hDlg, pPI);
        }
#endif

        //
        //  Try the selected printer.
        //
        if (lpsPrinterToSelect)
        {
            pPI->pCurPrinter = PrintSearchForPrinter(pPI, lpsPrinterToSelect);
        }

        //
        //  Open the current printer.
        //
        if (pPI->pCurPrinter)
        {
            //
            //  Open the current printer.
            //
            OpenPrinter(pPI->pCurPrinter->pPrinterName, &pPI->hCurPrinter, NULL);
        }
        else
        {
            //
            //  If there isn't a current printer, try the printers in
            //  the list until either one is found that can be opened or
            //  until there are no more printers in the list.
            //
            for (ctr = 0; ctr < pPI->cPrinters; ctr++)
            {
                pPI->pCurPrinter = &pPI->pPrinters[ctr];

                //
                //  Try to open the printer.
                //
                if (OpenPrinter( pPI->pCurPrinter->pPrinterName,
                                 &pPI->hCurPrinter,
                                 NULL ))
                {
                    break;
                }
            }
        }
    }
    else
    {
        //
        //  If there isn't a current printer, then try to enumerate.
        //  This means something isn't setup properly.
        //
        if ((!pPI->pCurPrinter) || (!pPI->pPrinters))
        {
            goto Print_Enumerate;
        }
    }

    if (hCtl)
    {
        //
        //  Reset the contents of the list box.
        //
        SendMessage(hCtl, CB_RESETCONTENT, 0, 0);

        //
        //  Add all of the printer name strings to the list box.
        //
        for (ctr = 0; ctr < pPI->cPrinters; ctr++)
        {
            SendMessage( hCtl,
                         CB_ADDSTRING,
                         0,
                         (LPARAM)pPI->pPrinters[ctr].pPrinterName );
        }

        //
        //  Set the current selection in the list box.
        //
        SendMessage( hCtl,
                     CB_SETCURSEL,
                     SendMessage( hCtl,
                                  CB_FINDSTRINGEXACT,
                                  (WPARAM)-1,
                                  (LPARAM)pPI->pCurPrinter->pPrinterName ),
                     0L );
    }

    return (TRUE);
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintBuildDevNames
//
////////////////////////////////////////////////////////////////////////////

VOID PrintBuildDevNames(
    PPRINTINFO pPI)
{
    LPPRINTDLG pPD = pPI->pPD;
    LPTSTR pPrinterName = NULL;
    LPTSTR pPortName = NULL;
    TCHAR szBuffer[MAX_PATH];
    TCHAR szPort[MAX_PATH];
    LPTSTR pStr;
    LPDEVNAMES pDN;
    DWORD cbDevNames;
    HANDLE hPrinter ;
    PPRINTER_INFO_2 pPrinter = NULL;


    //
    //  If this is called from PrintReturnDefault, there is no
    //  PrinterInfo (pPI->pCurPrinter) because the printers were not
    //  enumerated.  So, build the DEVNAME from win.ini.
    //
    pStr = szBuffer;
    if (!pPI->pCurPrinter)
    {
        //
        //  Get the default printer from the "Windows" section of win.ini.
        //      (eg. device=\\server\local,winspool,Ne00:)
        //
        if ( (pPD->Flags & PD_RETURNDEFAULT) &&
             GetProfileString( szTextWindows,
                               szTextDevice,
                               szTextNull,
                               szBuffer,
                               MAX_PATH ) )
        {
            //  Examples of szBuffer:
            //    "My Local Printer,winspool,LPT1:"   or
            //    "\\server\local,winspool,Ne00:"

            //
            //  Skip leading space (if any).
            //
            while (*pStr == CHAR_SPACE)
            {
                pStr++;
            }

            //
            //  First token is the printer name.
            //
            pPrinterName = pStr;

            while (*pStr && *pStr != CHAR_COMMA)
            {
                pStr++;
            }

            //
            //  NULL terminate the printer name.
            //
            *pStr++ = CHAR_NULL;

            // For Newer Apps  return the port name from the PRINT_INFO_2 structure.
            // For older apps  return the short port name give in the win.ini
            if (pPI->ProcessVersion >= 0x40000)
            {
                //Newer App
                if (OpenPrinter(pPrinterName, &hPrinter, NULL))
                {
                    if (pPrinter = PrintGetPrinterInfo2(hPrinter))
                    {
                        lstrcpy(szPort, pPrinter->pPortName);
                        pPortName = szPort;
                        GlobalFree(pPrinter);
                    }
                    ClosePrinter(hPrinter);

                 }
                 else
                 {
                     //Unable to Open Printer so return
                     return ;
                 }
            }
            else
            {

                //Old App

                //
                //  Skip the driver name (second token).
                //
                while (*pStr && *pStr++ != CHAR_COMMA)
                {
                    ;
                }

                //
                //  Skip leading space (if any).
                //
                while (*pStr == CHAR_SPACE)
                {
                    pStr++;
                }

                //
                //  Third (and last) token is the port name.
                //
                pPortName = pStr;
            }
        }
        else
        {
            return;
        }
    }
    else
    {
        //
        //  Get the printer name from the PrinterInfo2 structure
        //  for the current printer.
        //
        pPrinterName = pPI->pCurPrinter->pPrinterName;

        //
        //  Newer Apps:
        //    Get the port name from the PrinterInfo2 structure for the
        //    current printer.  Want to use the PrinterInfo2 structure
        //    for newer apps so that we can support multiple ports for
        //    one printer.
        //
        //  Older Apps:
        //    First try to get the port name from the "devices" section
        //    of win.ini.  If that fails, then use the PrinterInfo2
        //    structure for the current printer.
        //
        //    This needs to use the "devices" section first due to a bug
        //    in AutoCAD.  AutoCAD only allows 13 characters for the port
        //    name and it does not check the length when it tries to copy
        //    it to its own buffer.
        //
#ifdef WINNT
        if ( (pPI->ProcessVersion >= 0x40000) ||
             (!GetProfileString( szTextDevices,
                                 pPrinterName,
                                 szTextNull,
                                 szBuffer,
                                 MAX_PATH )) ||
             (!(pPortName = StrChr(szBuffer, CHAR_COMMA))) ||
             (!((++pPortName)[0])) )
#endif
        {
            //
            //  Get the port name from the PrinterInfo2 structure
            //  for the current printer.
            //
            pPortName = pPI->pCurPrinter->pPortName;
        }
    }

    //
    //  Compute the size of the DevNames structure.
    //
    cbDevNames = lstrlen(szDriver) + 1 +
                 lstrlen(pPortName) + 1 +
                 lstrlen(pPrinterName) + 1 +
                 DN_PADDINGCHARS;

    cbDevNames *= sizeof(TCHAR);
    cbDevNames += sizeof(DEVNAMES);

    //
    //  Allocate the new DevNames structure.
    //
    pDN = NULL;
    if (pPD->hDevNames)
    {
        HANDLE handle;

        handle = GlobalReAlloc(pPD->hDevNames, cbDevNames, GHND);

        //Make sure the Realloc succeeded.
        if (handle)
        {
            pPD->hDevNames = handle;
        }
        else
        {
            //Realloc didn't succeed.  Free the old the memory
            pPD->hDevNames = GlobalFree(pPD->hDevNames);
        }
    }
    else
    {
        pPD->hDevNames = GlobalAlloc(GHND, cbDevNames);
    }

    //
    //  Fill in the DevNames structure with the appropriate information.
    //
    if ( (pPD->hDevNames) &&
         (pDN = GlobalLock(pPD->hDevNames)) )
    {
        pDN->wDriverOffset = sizeof(DEVNAMES) / sizeof(TCHAR);
        lstrcpy((LPTSTR)pDN + pDN->wDriverOffset, szDriver);

        pDN->wDeviceOffset = pDN->wDriverOffset + lstrlen(szDriver) + 1;
        lstrcpy((LPTSTR)pDN + pDN->wDeviceOffset, pPrinterName);

        pDN->wOutputOffset = pDN->wDeviceOffset + lstrlen(pPrinterName) + 1;
        lstrcpy((LPTSTR)pDN + pDN->wOutputOffset, pPortName);

        if ( (pPD->Flags & PD_RETURNDEFAULT) ||
             !lstrcmp(pPrinterName, pPI->szDefaultPrinter) )
        {
            pDN->wDefault = DN_DEFAULTPRN;
        }
        else
        {
            pDN->wDefault = 0;
        }

        GlobalUnlock(pPD->hDevNames);
    }
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintGetDevMode
//
//  Create and/or fill DEVMODE structure.
//
////////////////////////////////////////////////////////////////////////////

HANDLE PrintGetDevMode(
    HWND hDlg,
    HANDLE hPrinter,
    LPTSTR lpsDeviceName,
    HANDLE hDevMode)
{
    LONG cbNeeded;
    LPDEVMODE pDM;


    cbNeeded = DocumentProperties( hDlg,
                                   hPrinter,
                                   lpsDeviceName,
                                   (PDEVMODE)NULL,
                                   (PDEVMODE)NULL,
                                   0 );

    if (cbNeeded > 0)
    {
        if (hDevMode)
        {
            HANDLE h = GlobalReAlloc(hDevMode, cbNeeded, GHND);

            //Make sure realloc succeeded.
            if (h)
            {
                hDevMode  = h;
            }
            else
            {
                //Realloc didn't succeed. Free the memory occupied
                GlobalFree(hDevMode);
                hDevMode = NULL;
            }

        }
        else
        {
            hDevMode = GlobalAlloc(GHND, cbNeeded);
        }

        if (hDevMode && (pDM = GlobalLock(hDevMode)))
        {
            if (DocumentProperties( hDlg,
                                    hPrinter,
                                    lpsDeviceName,
                                    pDM,
                                    NULL,
                                    DM_COPY ) != IDOK)
            {
                StoreExtendedError(PDERR_NODEFAULTPRN);
                GlobalUnlock(hDevMode);
                GlobalFree(hDevMode);
                return (NULL);
            }

            GlobalUnlock(hDevMode);
        }
        else
        {
            if (hDevMode)
            {
                StoreExtendedError(CDERR_MEMLOCKFAILURE);
                GlobalFree(hDevMode);
            }
            else
            {
                StoreExtendedError(CDERR_MEMALLOCFAILURE);
            }
            return (NULL);
        }
    }
    else
    {
        DWORD dwErrCode;

        hDevMode = NULL;
        dwErrCode = GetLastError();

        if ( (dwErrCode == ERROR_UNKNOWN_PRINTER_DRIVER) ||
             (dwErrCode == ERROR_MOD_NOT_FOUND) )
        {
            if (hDlg)
            {
                PrintEditError(hDlg, 0, iszUnknownDriver, lpsDeviceName);
            }
        }
    }

    return (hDevMode);
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintReturnICDC
//
//  Retrieve either the hDC or the hIC if either flag is set.
//  Assumes the PD_PRINTOFILE flag is appropriately set.
//
////////////////////////////////////////////////////////////////////////////

VOID PrintReturnICDC(
    LPPRINTDLG pPD,
    LPDEVNAMES pDN,
    LPDEVMODE pDM)
{
    if (pPD->Flags & PD_PRINTTOFILE)
    {
        lstrcpy((LPTSTR)pDN + pDN->wOutputOffset, szFilePort);
    }

#ifdef UNICODE
    //
    //  The dmCollate field wasn't part of the Win3.1 DevMode struct.  The way
    //  16-bit apps achieved collation was by checking the PD_COLLATE flag in
    //  the PrintDlg struct.  The app would then figure out the page printing
    //  order to achieve collation.  So what we're doing here is making sure
    //  that PD_COLLATE is the only collation mechanism for 16-bit apps.  If we
    //  let DM_COLLATE get into the DC we'd end up with the driver trying to
    //  collate a job that the app is already trying to collate!
    //
    if ((pPD->Flags & CD_WOWAPP) && pDM)
    {
        if (pDM->dmFields & DM_COLLATE)
        {
            pPD->Flags |= PD_COLLATE;
        }

        // these should always be off for WOW apps
        pDM->dmCollate = DMCOLLATE_FALSE;
        pDM->dmFields &= ~DM_COLLATE;
    }
#endif

    switch (pPD->Flags & (PD_RETURNDC | PD_RETURNIC))
    {
        case ( PD_RETURNIC ) :
        {
            pPD->hDC = CreateIC( (LPTSTR)pDN + pDN->wDriverOffset,
                                 (LPTSTR)pDN + pDN->wDeviceOffset,
                                 (LPTSTR)pDN + pDN->wOutputOffset,
                                 pDM);
            if (pPD->hDC)
            {
                break;
            }

            // else fall thru...
        }
        case ( PD_RETURNDC ) :
        case ( PD_RETURNDC | PD_RETURNIC ) :
        {
            //
            //  PD_RETURNDC has priority if they are both set.
            //
            pPD->hDC = CreateDC( (LPTSTR)pDN + pDN->wDriverOffset,
                                 (LPTSTR)pDN + pDN->wDeviceOffset,
                                 (LPTSTR)pDN + pDN->wOutputOffset,
                                 pDM );
            break;
        }
    }
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintMeasureItem
//
////////////////////////////////////////////////////////////////////////////

VOID PrintMeasureItem(
    HANDLE hDlg,
    LPMEASUREITEMSTRUCT mis)
{
    HDC hDC;
    TEXTMETRIC TM;
    HANDLE hFont;


    if (hDC = GetDC(hDlg))
    {
        hFont = (HANDLE)SendMessage(hDlg, WM_GETFONT, 0, 0L);
        if (!hFont)
        {
            hFont = GetStockObject(SYSTEM_FONT);
        }
        hFont = SelectObject(hDC, hFont);
        GetTextMetrics(hDC, &TM);
        mis->itemHeight = (WORD)TM.tmHeight;
        SelectObject(hDC, hFont);
        ReleaseDC(hDlg, hDC);
    }
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintInitOrientation
//
//  Enable/Disable Paper Orientation controls
//
//  NOTE: If the driver doesn't support orientation AND is smart
//  enough to tell us about it, disable the appropriate dialog items.
//  "Smart enough" means the driver must support DC_ORIENTATION in its
//  DeviceCapabilities routine.  This was introduced for 3.1, hence the
//  version test.  NotBadDriver() may need to be incorporated if a
//  problem driver is found in testing.
//
////////////////////////////////////////////////////////////////////////////

VOID PrintInitOrientation(
    HWND hDlg,
    PPRINTINFO pPI,
    LPDEVMODE pDM)
{
    BOOL bEnable = TRUE;
    HWND hCtl;
    HDC hDC;
    int iHeight;
    PPRINTER_INFO_2 pPrinter = pPI->pCurPrinter;


    if (!pPrinter)
    {
        return;
    }

    if (pDM->dmSpecVersion >= 0x030A)
    {
        pPI->dwRotation = DeviceCapabilities( pPrinter->pPrinterName,
                                              pPrinter->pPortName,
                                              DC_ORIENTATION,
                                              NULL,
                                              pDM );
        switch (pPI->dwRotation)
        {
            case ( ROTATE_LEFT ) :
            case ( ROTATE_RIGHT ) :
            {
                bEnable = TRUE;
                break;
            }
            default :
            {
                pPI->dwRotation = 0;
                bEnable = FALSE;
                pDM->dmOrientation = DMORIENT_PORTRAIT;
                CheckRadioButton( hDlg,
                                  ID_SETUP_R_PORTRAIT,
                                  ID_SETUP_R_LANDSCAPE,
                                  ID_SETUP_R_PORTRAIT );
                break;
            }
        }
    }

    if ( (pDM->dmOrientation != DMORIENT_PORTRAIT) &&
         (pDM->dmOrientation != DMORIENT_LANDSCAPE) )
    {
        pDM->dmOrientation  = DMORIENT_PORTRAIT;
    }

    if (hCtl = GetDlgItem(hDlg, ID_SETUP_R_LANDSCAPE))
    {
        //
        //  Landscape
        //
        if ( !( (pPI->pPSD) &&
                (pPI->pPSD->Flags & PSD_DISABLEORIENTATION) ) )
        {
            EnableWindow(hCtl, bEnable);
        }
    }
    if (hCtl = GetDlgItem(hDlg, ID_SETUP_I_ORIENTATION))
    {
        //
        //  Orientation of icon.
        //
        SetWindowLong( hCtl,
                       GWL_STYLE,
                       GetWindowLong(hCtl, GWL_STYLE) | SS_CENTERIMAGE );
    }

    if ( (!pPI->RtSampleXYWH.left) &&
         (hCtl = GetDlgItem(hDlg, ID_SETUP_W_SAMPLE)) )
    {
        GetWindowRect(hCtl, (LPRECT)&pPI->RtSampleXYWH);
        ScreenToClient(hDlg, (LPPOINT)&pPI->RtSampleXYWH.left);
        ScreenToClient(hDlg, (LPPOINT)&pPI->RtSampleXYWH.right);

        iHeight = pPI->RtSampleXYWH.bottom - pPI->RtSampleXYWH.top;
        pPI->RtSampleXYWH.bottom = iHeight;

        if (hDC = GetDC(0))
        {
            iHeight = iHeight * GetDeviceCaps(hDC, LOGPIXELSX) /
                                GetDeviceCaps(hDC, LOGPIXELSY);
            ReleaseDC(0, hDC);
        }

        pPI->RtSampleXYWH.left =
            (pPI->RtSampleXYWH.left + pPI->RtSampleXYWH.right - iHeight) / 2;
        pPI->RtSampleXYWH.right = iHeight;
    }
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintSetOrientation
//
//  Switch icon, check button, for Portrait or LandScape printing mode.
//
////////////////////////////////////////////////////////////////////////////

VOID PrintSetOrientation(
    HWND hDlg,
    PPRINTINFO pPI,
    LPDEVMODE pDM,
    UINT uiOldId,
    UINT uiNewId)
{
    BOOL bPortrait;
    HWND hIcn;


    bPortrait = (uiNewId == ID_SETUP_R_PORTRAIT);

    pDM->dmOrientation = ( bPortrait
                               ? DMORIENT_PORTRAIT
                               : DMORIENT_LANDSCAPE );

    CheckRadioButton(hDlg, ID_SETUP_R_PORTRAIT, ID_SETUP_R_LANDSCAPE, uiNewId);

    if (hIcn = GetDlgItem(hDlg, ID_SETUP_I_ORIENTATION))
    {
        ShowWindow(hIcn, SW_HIDE);
        SendMessage( hIcn,
                     STM_SETICON,
                     bPortrait ? (LONG_PTR)hIconPortrait : (LONG_PTR)hIconLandscape,
                     0L );
        ShowWindow(hIcn, SW_SHOW);
    }

    //
    //  Update the page setup dialog, if necessary.
    //
    if (pPI->pPSD)
    {
        PrintUpdatePageSetup(hDlg, pPI, pDM, uiOldId, uiNewId);
    }
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintUpdatePageSetup
//
//  Update the page setup information.
//
////////////////////////////////////////////////////////////////////////////

VOID PrintUpdatePageSetup(
    HWND hDlg,
    PPRINTINFO pPI,
    LPDEVMODE pDM,
    UINT uiOldId,
    UINT uiNewId)
{
    BOOL bPortrait = (uiNewId == ID_SETUP_R_PORTRAIT);
    LPPAGESETUPDLG pPSD = pPI->pPSD;
    LPPRINTDLG pPD = pPI->pPD;
    HWND hWndSample;
    HWND hWndShadowRight;
    HWND hWndShadowBottom;
    HWND hWndSize;
    LONG lTemp;


    if (!pPSD)
    {
        return;
    }

    if (uiOldId != uiNewId)
    {
        RECT aRtMinMargin = pPSD->rtMinMargin;
        RECT aRtMargin    = pPSD->rtMargin;
        HWND hWndLeft     = GetDlgItem(hDlg, ID_SETUP_E_LEFT);
        HWND hWndTop      = GetDlgItem(hDlg, ID_SETUP_E_TOP);
        HWND hWndRight    = GetDlgItem(hDlg, ID_SETUP_E_RIGHT);
        HWND hWndBottom   = GetDlgItem(hDlg, ID_SETUP_E_BOTTOM);
        TCHAR szLeft  [8];
        TCHAR szTop   [8];
        TCHAR szRight [8];
        TCHAR szBottom[8];

        GetWindowText(hWndLeft, szLeft, 8);
        GetWindowText(hWndTop, szTop, 8);
        GetWindowText(hWndRight, szRight, 8);
        GetWindowText(hWndBottom, szBottom, 8);

        switch (uiNewId + pPI->dwRotation)
        {
            case ( ID_SETUP_R_PORTRAIT  + ROTATE_RIGHT ) :  // HP PCL
            case ( ID_SETUP_R_LANDSCAPE + ROTATE_LEFT ) :   // dot-matrix
            {
                pPSD->rtMinMargin.left   = aRtMinMargin.top;
                pPSD->rtMinMargin.top    = aRtMinMargin.right;
                pPSD->rtMinMargin.right  = aRtMinMargin.bottom;
                pPSD->rtMinMargin.bottom = aRtMinMargin.left;

                pPSD->rtMargin.left   = aRtMargin.top;
                pPSD->rtMargin.top    = aRtMargin.right;
                pPSD->rtMargin.right  = aRtMargin.bottom;
                pPSD->rtMargin.bottom = aRtMargin.left;

                SetWindowText(hWndLeft, szTop);
                SetWindowText(hWndRight, szBottom);
                SetWindowText(hWndTop, szRight);
                SetWindowText(hWndBottom, szLeft);

                break;
            }
            case ( ID_SETUP_R_PORTRAIT  + ROTATE_LEFT ) :   // dot-matrix
            case ( ID_SETUP_R_LANDSCAPE + ROTATE_RIGHT ) :  // HP PCL
            {
                pPSD->rtMinMargin.left   = aRtMinMargin.bottom;
                pPSD->rtMinMargin.top    = aRtMinMargin.left;
                pPSD->rtMinMargin.right  = aRtMinMargin.top;
                pPSD->rtMinMargin.bottom = aRtMinMargin.right;

                pPSD->rtMargin.left   = aRtMargin.bottom;
                pPSD->rtMargin.top    = aRtMargin.left;
                pPSD->rtMargin.right  = aRtMargin.top;
                pPSD->rtMargin.bottom = aRtMargin.right;

                SetWindowText(hWndLeft, szBottom);
                SetWindowText(hWndRight, szTop);
                SetWindowText(hWndTop, szLeft);
                SetWindowText(hWndBottom, szRight);

                break;
            }
        }
    }
    pPI->uiOrientationID = uiNewId;

    //
    //  Update ptPaperSize.
    //
    pPI->PtPaperSizeMMs.x = 0;
    pPI->PtPaperSizeMMs.y = 0;
    pPD->Flags &= ~PI_WPAPER_ENVELOPE;

    if ((hWndSize = GetDlgItem(hDlg, ID_SETUP_C_SIZE)) && (pPI->pCurPrinter))
    {
        PPRINTER_INFO_2 pPrinter = pPI->pCurPrinter;
        DWORD dwNumber;
        LPWORD lpPapers;
        LPPOINT lpPaperSize;
        int nInd;
        DWORD i;

        dwNumber = DeviceCapabilities( pPrinter->pPrinterName,
                                       pPrinter->pPortName,
                                       DC_PAPERS,
                                       NULL,
                                       pDM );
        if ( dwNumber &&
             (dwNumber != (DWORD)-1) &&
             (lpPapers = LocalAlloc( LPTR,
                                     dwNumber *
                                         (sizeof(WORD) + sizeof(POINT)) * 2 )) )
        {
            lpPaperSize = (LPPOINT)(lpPapers + dwNumber * 2);

            DeviceCapabilities( pPrinter->pPrinterName,
                                pPrinter->pPortName,
                                DC_PAPERS,
                                (LPTSTR)lpPapers,
                                pDM );
            DeviceCapabilities( pPrinter->pPrinterName,
                                pPrinter->pPortName,
                                DC_PAPERSIZE,
                                (LPTSTR)lpPaperSize,
                                pDM );

            if ((nInd = (int) SendMessage(hWndSize, CB_GETCURSEL, 0, 0)) != CB_ERR)
            {
                pPI->wPaper = (WORD)SendMessage( hWndSize,
                                                 CB_GETITEMDATA,
                                                 nInd,
                                                 0 );
                pDM->dmPaperSize = pPI->wPaper;
            }
            else
            {
                pPI->wPaper = pDM->dmPaperSize;
            }

#ifndef WINNT
            if (pDM->dmSpecVersion >= 0x0400)
#endif
            {
                SendMessage( hWndSize,
                             CB_GETLBTEXT,
                             nInd,
                             (LPARAM)pDM->dmFormName );
            }

            switch (pPI->wPaper)
            {
                case ( DMPAPER_ENV_9 ) :
                case ( DMPAPER_ENV_10 ) :
                case ( DMPAPER_ENV_11 ) :
                case ( DMPAPER_ENV_12 ) :
                case ( DMPAPER_ENV_14 ) :
                case ( DMPAPER_ENV_DL ) :
                case ( DMPAPER_ENV_C5 ) :
                case ( DMPAPER_ENV_C3 ) :
                case ( DMPAPER_ENV_C4 ) :
                case ( DMPAPER_ENV_C6 ) :
                case ( DMPAPER_ENV_C65 ) :
                case ( DMPAPER_ENV_B4 ) :
                case ( DMPAPER_ENV_B5 ) :
                case ( DMPAPER_ENV_B6 ) :
                case ( DMPAPER_ENV_ITALY ) :
                case ( DMPAPER_ENV_MONARCH ) :
                case ( DMPAPER_ENV_PERSONAL ) :
                case ( DMPAPER_ENV_INVITE ) :
                case ( DMPAPER_JENV_KAKU2 ) :
                case ( DMPAPER_JENV_KAKU3 ) :
                case ( DMPAPER_JENV_CHOU3 ) :
                case ( DMPAPER_JENV_CHOU4 ) :
                case ( DMPAPER_JENV_KAKU2_ROTATED ) :
                case ( DMPAPER_JENV_KAKU3_ROTATED ) :
                case ( DMPAPER_JENV_CHOU3_ROTATED ) :
                case ( DMPAPER_JENV_CHOU4_ROTATED ) :
                case ( DMPAPER_JENV_YOU4 ) :
                case ( DMPAPER_JENV_YOU4_ROTATED ) :
                case ( DMPAPER_PENV_1 ) :
                case ( DMPAPER_PENV_2 ) :
                case ( DMPAPER_PENV_3 ) :
                case ( DMPAPER_PENV_4 ) :
                case ( DMPAPER_PENV_5 ) :
                case ( DMPAPER_PENV_6 ) :
                case ( DMPAPER_PENV_7 ) :
                case ( DMPAPER_PENV_8 ) :
                case ( DMPAPER_PENV_9 ) :
                case ( DMPAPER_PENV_10 ) :
                case ( DMPAPER_PENV_1_ROTATED ) :
                case ( DMPAPER_PENV_2_ROTATED ) :
                case ( DMPAPER_PENV_3_ROTATED ) :
                case ( DMPAPER_PENV_4_ROTATED ) :
                case ( DMPAPER_PENV_5_ROTATED ) :
                case ( DMPAPER_PENV_6_ROTATED ) :
                case ( DMPAPER_PENV_7_ROTATED ) :
                case ( DMPAPER_PENV_8_ROTATED ) :
                case ( DMPAPER_PENV_9_ROTATED ) :
                case ( DMPAPER_PENV_10_ROTATED ) :
                {
                    pPD->Flags |= PI_WPAPER_ENVELOPE;
                    break;
                }
            }

            for (i = 0; i < dwNumber; i++)
            {
                if (lpPapers[i] == pPI->wPaper)
                {
                    //
                    //  In tenths of MMs.
                    //
                    *(LPPOINT)&pPI->PtPaperSizeMMs = lpPaperSize[i];
                    break;
                }
            }

            LocalFree(lpPapers);
        }
    }

    //
    //  If the paper size could not be found, use something reasonable
    //  (eg. letter).
    //
    if (!pPI->PtPaperSizeMMs.x)
    {
        pPI->PtPaperSizeMMs.x = 85 * MMS_PER_INCH / 10;
    }
    if (!pPI->PtPaperSizeMMs.y)
    {
        pPI->PtPaperSizeMMs.y = 11 * MMS_PER_INCH;
    }

    //
    //  Rotate envelopes as needed.
    //
    if ( (pPD->Flags & PI_WPAPER_ENVELOPE) &&
         (!pPI->dwRotation) &&
         (pPI->PtPaperSizeMMs.x < pPI->PtPaperSizeMMs.y) )
    {
        lTemp = pPI->PtPaperSizeMMs.x;
        pPI->PtPaperSizeMMs.x = pPI->PtPaperSizeMMs.y;
        pPI->PtPaperSizeMMs.y = lTemp;
    }

    //
    //  Maintain everything in accordance with the orientation
    //  so that apps have to do as little work as possible.
    //
    if (!bPortrait)
    {
        lTemp = pPI->PtPaperSizeMMs.x;
        pPI->PtPaperSizeMMs.x = pPI->PtPaperSizeMMs.y;
        pPI->PtPaperSizeMMs.y = lTemp;
    }

    //
    //  Set up return ptPaperSize value.
    //
    if (pPSD->Flags & PSD_INTHOUSANDTHSOFINCHES)
    {
        pPSD->ptPaperSize.x = pPI->PtPaperSizeMMs.x * 1000 / MMS_PER_INCH;
        pPSD->ptPaperSize.y = pPI->PtPaperSizeMMs.y * 1000 / MMS_PER_INCH;
    }
    else           // PSD_INHUNDREDTHSOFMILLIMETERS
    {
        pPSD->ptPaperSize.x = pPI->PtPaperSizeMMs.x * 10;
        pPSD->ptPaperSize.y = pPI->PtPaperSizeMMs.y * 10;
    }

    //
    //  Update RtMinMarginMMs and rtMinMargin for new papersize/orientation.
    //
    PrintSetMinMargins(hDlg, pPI, pDM);

    //
    //  Don't let margins overlap (page might have shrunk).
    //
    if (pPSD->rtMargin.left + pPSD->rtMargin.right > pPSD->ptPaperSize.x)
    {
        lTemp = (pPD->Flags & PSD_INTHOUSANDTHSOFINCHES) ? 1000 : MMS_PER_INCH;
        pPSD->rtMargin.left  = (pPSD->ptPaperSize.x - lTemp) / 2;
        pPSD->rtMargin.right = (pPSD->ptPaperSize.x - lTemp) / 2;
    }
    if (pPSD->rtMargin.top + pPSD->rtMargin.bottom > pPSD->ptPaperSize.y)
    {
        lTemp = (pPD->Flags & PSD_INTHOUSANDTHSOFINCHES) ? 1000 : MMS_PER_INCH;
        pPSD->rtMargin.top    = (pPSD->ptPaperSize.y - lTemp) / 2;
        pPSD->rtMargin.bottom = (pPSD->ptPaperSize.y - lTemp) / 2;
    }

    //
    //  There are new minimal margins, so adjust rtMargin
    //  (min margins might have grown).
    //
    if (pPSD->rtMargin.left < pPSD->rtMinMargin.left)
        pPSD->rtMargin.left = pPSD->rtMinMargin.left;
    if (pPSD->rtMargin.top < pPSD->rtMinMargin.top)
        pPSD->rtMargin.top = pPSD->rtMinMargin.top;
    if (pPSD->rtMargin.right < pPSD->rtMinMargin.right)
        pPSD->rtMargin.right = pPSD->rtMinMargin.right;
    if (pPSD->rtMargin.bottom < pPSD->rtMinMargin.bottom)
        pPSD->rtMargin.bottom = pPSD->rtMinMargin.bottom;

    //
    //  The margins were adjusted, so update the ui.
    //
    PrintSetMargin(hDlg, pPI, ID_SETUP_E_LEFT, pPSD->rtMargin.left);
    PrintSetMargin(hDlg, pPI, ID_SETUP_E_TOP, pPSD->rtMargin.top);
    PrintSetMargin(hDlg, pPI, ID_SETUP_E_RIGHT, pPSD->rtMargin.right);
    PrintSetMargin(hDlg, pPI, ID_SETUP_E_BOTTOM, pPSD->rtMargin.bottom);

    //
    //  Update the sample window size & shadow.
    //
    if ( (hWndSample = GetDlgItem(hDlg, ID_SETUP_W_SAMPLE)) &&
         (hWndShadowRight = GetDlgItem(hDlg, ID_SETUP_W_SHADOWRIGHT)) &&
         (hWndShadowBottom = GetDlgItem(hDlg, ID_SETUP_W_SHADOWBOTTOM)) )
    {
        int iWidth = pPI->PtPaperSizeMMs.x;
        int iLength = pPI->PtPaperSizeMMs.y;
        int iExtent;
        RECT aRtSampleXYWH = pPI->RtSampleXYWH;
        int iX = aRtSampleXYWH.right  / 16;
        int iY = aRtSampleXYWH.bottom / 16;

        if (iWidth > iLength)
        {
            iExtent = aRtSampleXYWH.bottom * iLength / iWidth;
            aRtSampleXYWH.top += (aRtSampleXYWH.bottom - iExtent) / 2;
            aRtSampleXYWH.bottom = iExtent;
        }
        else
        {
            iExtent = aRtSampleXYWH.right * iWidth / iLength;
            aRtSampleXYWH.left += (aRtSampleXYWH.right - iExtent) / 2;
            aRtSampleXYWH.right = iExtent;
        }

        SetWindowPos( hWndSample,
                      0,
                      aRtSampleXYWH.left,
                      aRtSampleXYWH.top,
                      aRtSampleXYWH.right,
                      aRtSampleXYWH.bottom,
                      SWP_NOZORDER );

        SetWindowPos( hWndShadowRight,
                      0,
                      aRtSampleXYWH.left + aRtSampleXYWH.right,
                      aRtSampleXYWH.top + iY,
                      iX,
                      aRtSampleXYWH.bottom,
                      SWP_NOZORDER );

        SetWindowPos( hWndShadowBottom,
                      0,
                      aRtSampleXYWH.left + iX,
                      aRtSampleXYWH.top + aRtSampleXYWH.bottom,
                      aRtSampleXYWH.right,
                      iY,
                      SWP_NOZORDER );

        InvalidateRect(hWndSample, NULL, TRUE);
        UpdateWindow(hDlg);
        UpdateWindow(hWndSample);
        UpdateWindow(hWndShadowRight);
        UpdateWindow(hWndShadowBottom);
    }
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintInitDuplex
//
//  Enable/Disable Paper Duplexing controls.
//
//  Returns TRUE iff buttons used to be disabled, now enabled.
//  Returns FALSE otherwise.
//
////////////////////////////////////////////////////////////////////////////

VOID PrintInitDuplex(
    HWND hDlg,
    LPDEVMODE pDM)
{
    BOOL bEnable;
    HWND hCtl;


    bEnable = (pDM->dmFields & DM_DUPLEX);

    if (hCtl = GetDlgItem(hDlg, ID_SETUP_G_DUPLEX))
    {
        EnableWindow(hCtl, bEnable);
    }
    if (hCtl = GetDlgItem(hDlg, ID_SETUP_R_NONE))
    {
        EnableWindow(hCtl, bEnable);
    }
    if (hCtl = GetDlgItem(hDlg, ID_SETUP_R_LONG))
    {
        EnableWindow(hCtl, bEnable);
    }
    if (hCtl = GetDlgItem(hDlg, ID_SETUP_R_SHORT))
    {
        EnableWindow(hCtl, bEnable);
    }

    if (hCtl = GetDlgItem(hDlg, ID_SETUP_I_DUPLEX))
    {
        SetWindowLong( hCtl,
                       GWL_STYLE,
                       GetWindowLong(hCtl, GWL_STYLE) | SS_CENTERIMAGE );
        if (!bEnable)
        {
            ShowWindow(hCtl, SW_HIDE);
            SendMessage(hCtl, STM_SETICON, (LONG_PTR)hIconPDuplexNone, 0L);
            ShowWindow(hCtl, SW_SHOW);
        }
    }
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintSetDuplex
//
//  This routine will operate on pDocDetails->pDMInput PSDEVMODE structure,
//  making sure that is a structure we know about and can handle.
//
//  If the pd doesn't have DM_DUPLEX caps then just display the appropriate
//  paper icon for DMDUP_SIMPLEX (case where nRad = ID_SETUP_R_NONE).
//
//  If nRad = 0, update icon but don't change radio button.
//
////////////////////////////////////////////////////////////////////////////

VOID PrintSetDuplex(
    HWND hDlg,
    LPDEVMODE pDM,
    UINT nRad)
{
    BOOL bPortrait;
    HANDLE hDuplexIcon;
    HWND hCtl;


    bPortrait = (pDM->dmOrientation == DMORIENT_PORTRAIT);

    if (!(pDM->dmFields & DM_DUPLEX))
    {
        nRad = ID_SETUP_R_NONE;
    }

    //
    //  Boundary checking - default to ID_SETUP_R_NONE.
    //
    if (GetDlgItem(hDlg, ID_SETUP_R_NONE))
    {
        if ((nRad < ID_SETUP_R_NONE) || (nRad > ID_SETUP_R_SHORT))
        {
            if (IsDlgButtonChecked(hDlg, ID_SETUP_R_SHORT))
            {
                nRad = ID_SETUP_R_SHORT;
            }
            else if (IsDlgButtonChecked(hDlg, ID_SETUP_R_LONG))
            {
                nRad = ID_SETUP_R_LONG;
            }
            else
            {
                nRad = ID_SETUP_R_NONE;
            }
        }
        else
        {
            CheckRadioButton(hDlg, ID_SETUP_R_NONE, ID_SETUP_R_SHORT, nRad);
        }
    }

    if (hCtl = GetDlgItem(hDlg, ID_SETUP_I_DUPLEX))
    {
        switch (nRad)
        {
            case ( ID_SETUP_R_LONG ) :      // Long Side - 2 sided printing
            {
                pDM->dmDuplex = DMDUP_VERTICAL;
                hDuplexIcon = bPortrait ? hIconPDuplexNoTumble : hIconLDuplexTumble;

                break;
            }
            case ( ID_SETUP_R_SHORT ) :     // Short Side - 2 sided printing
            {
                pDM->dmDuplex = DMDUP_HORIZONTAL;
                hDuplexIcon = bPortrait ? hIconPDuplexTumble : hIconLDuplexNoTumble;

                break;
            }
            default :                       // None - 2 sided printing
            {
                pDM->dmDuplex = DMDUP_SIMPLEX;
                hDuplexIcon = bPortrait ? hIconPDuplexNone : hIconLDuplexNone;

                break;
            }
        }

        //
        //  Set the appropriate icon.
        //
        ShowWindow(hCtl, SW_HIDE);
        SendMessage(hCtl, STM_SETICON, (LONG_PTR)hDuplexIcon, 0L);
        ShowWindow(hCtl, SW_SHOW);
    }
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintInitPaperCombo
//
////////////////////////////////////////////////////////////////////////////

VOID PrintInitPaperCombo(
    PPRINTINFO pPI,
    HWND hCmb,
    HWND hStc,
    PPRINTER_INFO_2 pPrinter,
    LPDEVMODE pDM,
    WORD fwCap1,
    WORD cchSize1,
    WORD fwCap2)
{
    DWORD cStr1, cStr2, cRet1, cRet2, i;
    LPTSTR lpsOut1;
    LPWORD lpwOut2;
    BOOL fFill;


    HourGlass(TRUE);

    SendMessage(hCmb, CB_RESETCONTENT, 0, 0L);

    cStr1 = DeviceCapabilities( pPrinter->pPrinterName,
                                pPrinter->pPortName,
                                fwCap1,
                                NULL,
                                pDM );

    cStr2 = DeviceCapabilities( pPrinter->pPrinterName,
                                pPrinter->pPortName,
                                fwCap2,
                                NULL,
                                pDM );

    //
    //  Check for error from DeviceCapabilities calls.  If either
    //  call failed, simply set cStr1 to be 0 so that the windows will be
    //  disabled and nothing will be initialized.
    //
    if ((cStr1 == (DWORD)(-1)) || (cStr2 == (DWORD)(-1)))
    {
        cStr1 = 0;
    }

    fFill = (cStr1 > 0) && (cStr1 == cStr2);

    if (!((pPI->pPSD) && (pPI->pPSD->Flags & PSD_DISABLEPAPER)))
    {
        //
        //  If no entries, disable hCmb and hStc.
        //
        EnableWindow(hCmb, fFill);
        EnableWindow(hStc, fFill);
    }

    if (fFill)
    {
        lpsOut1 = LocalAlloc(LPTR, cStr1 * cchSize1 * sizeof(TCHAR));

        lpwOut2 = LocalAlloc(LPTR, cStr2 * sizeof(WORD));

        if (lpsOut1 && lpwOut2)
        {
            cRet1 = DeviceCapabilities( pPrinter->pPrinterName,
                                        pPrinter->pPortName,
                                        fwCap1,
                                        (LPTSTR)lpsOut1,
                                        pDM );

            cRet2 = DeviceCapabilities( pPrinter->pPrinterName,
                                        pPrinter->pPortName,
                                        fwCap2,
                                        (LPTSTR)lpwOut2,
                                        pDM );

            if ((pPI->dwRotation =
                    DeviceCapabilities( pPrinter->pPrinterName,
                                        pPrinter->pPortName,
                                        DC_ORIENTATION,
                                        NULL,
                                        pDM )) == (DWORD)(-1))
            {
                pPI->dwRotation = 0;
            }

            if ((cRet1 == cStr1) && (cRet2 == cStr2))
            {
                LPTSTR lpsT1 = lpsOut1;
                LPWORD lpwT2 = lpwOut2;
                int nInd;
                LPTSTR lpFound = NULL;
                LPTSTR lpFirst = NULL;

                for (i = 0; i < cRet1; i++, lpsT1 += cchSize1, lpwT2++)
                {
#ifndef WINNT
                    LPTSTR lpsT3;
                    LPWORD lpwT4;

                    //
                    //  Various checks for HP LaserJet and PostScript
                    //  driver bugs.
                    //
                    //
                    //  Look for duplicate names.
                    //
                    for (lpsT3 = lpsOut1; lpsT3 <= lpsT1; lpsT3 += cchSize1)
                    {
                        if (!lstrcmp(lpsT3, lpsT1))
                        {
                            break;
                        }
                    }
                    if (lpsT3 != lpsT1)
                    {
                        //
                        //  Duplicate found, so ignore.
                        //
                        continue;
                    }

                    //
                    //  Look for duplicate values.
                    //
                    for (lpwT4 = lpwOut2; lpwT4 <= lpwT2; lpwT4++)
                    {
                        if (*lpwT4 == *lpwT2)
                        {
                            break;
                        }
                    }
                    if (lpwT4 != lpwT2)
                    {
                        //
                        //  Duplicate found, so ignore.
                        //
                        continue;
                    }
#endif
                    //
                    //  Look for a blank name entry.
                    //
                    if (!*lpsT1)
                    {
                        //
                        //  Blank entry, so ignore.
                        //
                        continue;
                    }

                    //
                    //  Add the string to the list box.
                    //
                    nInd = (int) SendMessage( hCmb,
                                              CB_ADDSTRING,
                                              0,
                                              (LPARAM)lpsT1 );
                    if (nInd != CB_ERR)
                    {
                        //
                        //  Set the data associated with the string that
                        //  was just added to the list box.
                        //
                        SendMessage( hCmb,
                                     CB_SETITEMDATA,
                                     nInd,
                                     (LPARAM)*lpwT2 );

                        //
                        //  See if this item should be selected.
                        //
                        if (!lpFound)
                        {
                            if (!lpFirst)
                            {
                                lpFirst = lpsT1;
                            }

                            if ( (fwCap1 == DC_PAPERNAMES) &&
                                 (pDM->dmFields & DM_PAPERSIZE) &&
                                 (pDM->dmPaperSize == (SHORT)*lpwT2) )
                            {
                                lpFound = lpsT1;
                            }
                            else if ( (fwCap1 == DC_BINNAMES) &&
                                      (pDM->dmFields & DM_DEFAULTSOURCE) &&
                                      (pDM->dmDefaultSource == (SHORT)*lpwT2) )
                            {
                               lpFound = lpsT1;
                            }
                        }
                    }
                }

                //
                //  Set the appropriate selection.
                //
                if (lpFound)
                {
                    SendMessage( hCmb,
                                 CB_SETCURSEL,
                                 SendMessage( hCmb,
                                              CB_FINDSTRINGEXACT,
                                              (WPARAM)-1,
                                              (LPARAM)lpFound ),
                                 0 );
                }
                else
                {
                    if (fwCap1 == DC_PAPERNAMES)
                    {
                        //
                        //  Check for a default FORM name.
                        //
                        if (!( (pDM->dmFields & DM_FORMNAME) &&
                               ((nInd = (int)
                                   SendMessage( hCmb,
                                                CB_SELECTSTRING,
                                                (WPARAM)-1,
                                                (LPARAM)pDM->dmFormName )) != CB_ERR) ))
                        {
                            //
                            //  Always select the first *enumerated* entry
                            //  if no other selection was found.
                            //
                            SendMessage( hCmb,
                                         CB_SETCURSEL,
                                         (lpFirst)
                                           ? SendMessage( hCmb,
                                                          CB_FINDSTRINGEXACT,
                                                          (WPARAM)-1,
                                                          (LPARAM)lpFirst )
                                           : 0,
                                         0 );
                        }
                        else
                        {
                            //
                            //  Save the paper size since the form name exists
                            //  in the list box.
                            //
                        //  pDM->dmFields |= DM_PAPERSIZE;
                            pDM->dmPaperSize =
                                (SHORT)SendMessage( hCmb,
                                                    CB_GETITEMDATA,
                                                    nInd,
                                                    0 );
                        }
                    }
                    else
                    {
                        //
                        //  Set the SOURCE to the Default if it exists.
                        //
                        nInd = (int) SendMessage( hCmb,
                                                  CB_SELECTSTRING,
                                                  (WPARAM)-1,
                                                  (LPARAM)szDefaultSrc );
                        if (nInd != CB_ERR)
                        {
                        //  pDM->dmFields |= DM_DEFAULTSOURCE;
                            pDM->dmDefaultSource =
                                (SHORT)SendMessage( hCmb,
                                                    CB_GETITEMDATA,
                                                    nInd,
                                                    0 );
                        }
                        else
                        {
                            //
                            //  Always select the first *enumerated* entry
                            //  if no other selection was found.
                            //
                            SendMessage( hCmb,
                                         CB_SETCURSEL,
                                         (lpFirst)
                                           ? SendMessage( hCmb,
                                                          CB_FINDSTRINGEXACT,
                                                          (WPARAM)-1,
                                                          (LPARAM)lpFirst )
                                           : 0,
                                         0 );
                        }
                    }
                }
            }
        }
        if (lpsOut1)
        {
            LocalFree((HLOCAL)lpsOut1);
        }

        if (lpwOut2)
        {
            LocalFree((HLOCAL)lpwOut2);
        }
    }

    HourGlass(FALSE);
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintEditError
//
//  Set focus to an edit control and select the entire contents.
//  This is generally used when an improper value was found at OK time.
//
//  Assumes edit control not disabled.
//
////////////////////////////////////////////////////////////////////////////

VOID PrintEditError(
    HWND hDlg,
    int Id,
    UINT MessageId,
    ...)
{
    HWND hEdit;
    TCHAR pszTitle[MAX_PATH];
    TCHAR pszFormat[MAX_PATH];
    TCHAR pszMessage[MAX_PATH];


    //
    //  Put up the error message box.
    //
    if ( GetWindowText(hDlg, pszTitle, ARRAYSIZE(pszTitle)) &&
         CDLoadString(g_hinst, MessageId, pszFormat, ARRAYSIZE(pszFormat)) )
    {
        va_list ArgList;

        va_start(ArgList, MessageId);
        wvnsprintf(pszMessage, ARRAYSIZE(pszMessage), pszFormat, ArgList);
        va_end(ArgList);
        MessageBeep(MB_ICONEXCLAMATION);
        MessageBox(hDlg, pszMessage, pszTitle, MB_ICONEXCLAMATION | MB_OK);
    }

    //
    //  Highlight the invalid value.
    //
    if (hEdit = ((Id == 0) ? NULL : GetDlgItem(hDlg, Id)))
    {
        SendMessage(hDlg, WM_NEXTDLGCTL, (WPARAM)hEdit, 1L);
        SendMessage(hEdit, EM_SETSEL, (WPARAM)0, (LPARAM)-1);
    }
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintOpenPrinter
//
//  If the OpenPrinter call is successful, this sets hPrinter, pPrinter,
//  cPrinters, and pCurPrinter.
//
////////////////////////////////////////////////////////////////////////////

VOID PrintOpenPrinter(
    PPRINTINFO pPI,
    LPTSTR pPrinterName)
{
    if (OpenPrinter(pPrinterName, &pPI->hCurPrinter, NULL))
    {
        if (pPI->pPrinters = PrintGetPrinterInfo2(pPI->hCurPrinter))
        {
            pPI->cPrinters = 1;

#ifdef UNICODE
            if (pPI->bUseExtDeviceMode)
            {
                PrintGetExtDeviceMode(NULL, pPI);
            }
#endif
        }
        pPI->pCurPrinter = pPI->pPrinters;
    }
    else
    {
        //
        //  Cannot trust the OpenPrinter call.
        //
        pPI->hCurPrinter = NULL;
    }
}


////////////////////////////////////////////////////////////////////////////
//
//  PrintClosePrinters
//
////////////////////////////////////////////////////////////////////////////

BOOL PrintClosePrinters(
    PPRINTINFO pPI)
{
    if (pPI->hCurPrinter)
    {
        ClosePrinter(pPI->hCurPrinter);
        pPI->hCurPrinter = 0;
    }
    pPI->pCurPrinter = NULL;

    FreePrinterArray(pPI);

    return (TRUE);
}


////////////////////////////////////////////////////////////////////////////
//
//  UpdateSpoolerInfo
//
////////////////////////////////////////////////////////////////////////////

#ifdef UNICODE

VOID UpdateSpoolerInfo(
    PPRINTINFO pPI)
{
    LPDEVMODEA pDMA;
    CHAR szPrinterNameA[33];
    LPDEVMODEW pDMW;


    //
    //  Get a pointer to the devmode structure.
    //
    pDMW = GlobalLock(pPI->pPD->hDevMode);
    if ((!pDMW) || (!pPI->pCurPrinter))
    {
        return;
    }

    //
    //  Convert the printer name from Unicode to ANSI.
    //
    SHUnicodeToAnsi(pPI->pCurPrinter->pPrinterName, szPrinterNameA, ARRAYSIZE(szPrinterNameA));

    //
    //  Allocate and convert the Unicode devmode to ANSI.
    //
    pDMA = AllocateAnsiDevMode(pDMW);
    if (!pDMA)
    {
        GlobalUnlock(pPI->pPD->hDevMode);
        return;
    }

    //
    //  Update the spooler's information.
    //
    ExtDeviceMode( NULL,
                   NULL,
                   NULL,
                   szPrinterNameA,
                   NULL,
                   pDMA,
                   NULL,
                   DM_UPDATE | DM_MODIFY );

    //
    //  Free the buffer.
    //
    GlobalFree(pDMA);
    GlobalUnlock(pPI->pPD->hDevMode);
}
#endif


////////////////////////////////////////////////////////////////////////////
//
//  PrintGetPrinterInfo2
//
////////////////////////////////////////////////////////////////////////////

PPRINTER_INFO_2 PrintGetPrinterInfo2(
    HANDLE hPrinter)
{
    PPRINTER_INFO_2 pPrinter = NULL;
    DWORD cbPrinter = 0;


    StoreExtendedError(CDERR_GENERALCODES);

    if (!GetPrinter(hPrinter, 2, NULL, 0, &cbPrinter) &&
        (GetLastError() == ERROR_INSUFFICIENT_BUFFER))
    {
        if (pPrinter = GlobalAlloc(GPTR, cbPrinter))
        {
            if (!GetPrinter( hPrinter,
                             2,
                             (LPBYTE)pPrinter,
                             cbPrinter,
                             &cbPrinter ))
            {
                GlobalFree(pPrinter);
                pPrinter = NULL;
                StoreExtendedError(PDERR_PRINTERNOTFOUND);
            }
        }
        else
        {
            StoreExtendedError(CDERR_MEMALLOCFAILURE);
        }
    }
    else
    {
        StoreExtendedError(PDERR_SETUPFAILURE);
    }

    return (pPrinter);
}


////////////////////////////////////////////////////////////////////////////
//
//  ConvertStringToInteger
//
//  Converts a string to an integer.  Stops at the first non digit.
//
////////////////////////////////////////////////////////////////////////////

int ConvertStringToInteger(
    LPCTSTR pSrc)
{
    int Number = 0;
    BOOL bNeg = FALSE;


    if (*pSrc == TEXT('-'))
    {
        bNeg = TRUE;
        pSrc++;
    }

    while (ISDIGIT(*pSrc))
    {
        Number *= 10;
        Number += *pSrc - TEXT('0');
        pSrc++;
    }

    return ( bNeg ? -Number : Number );
}


////////////////////////////////////////////////////////////////////////////
//
//  FreePrinterArray
//
//  Purpose:    Frees the buffer allocated to store printers.
//
//  Parameters: PPRINTINFO pPI
//
//  Return:     void
//
////////////////////////////////////////////////////////////////////////////

VOID FreePrinterArray(
    PPRINTINFO pPI)
{
    PPRINTER_INFO_2 pPrinters = pPI->pPrinters;
#ifdef UNICODE
    DWORD dwCount;
#endif
    //
    //  If NULL, we can return now.
    //
    if (!pPrinters)
    {
        return;
    }

#ifdef UNICODE
    //
    //  If we made calls to ExtDeviceMode, then we need to
    //  free the buffers allocated for each devmode.
    //
    if (pPI->bUseExtDeviceMode)
    {
        if (pPI->pAllocInfo)
        {
            //
            //  Loop through each of the printers.
            //
            for (dwCount = 0; dwCount < pPI->cPrinters; dwCount++)
            {
                //
                //  If pDevMode exists, free it.
                //
                if ((pPrinters[dwCount].pDevMode) &&
                    (pPI->pAllocInfo[dwCount]))
                {
                    GlobalFree(pPrinters[dwCount].pDevMode);
                    pPrinters[dwCount].pDevMode = NULL;
                }
            }
            GlobalFree(pPI->pAllocInfo);
            pPI->pAllocInfo = NULL;
        }
    }
#endif

    //
    //  Free the entire block.
    //
    GlobalFree(pPI->pPrinters);
    pPI->pPrinters = NULL;
    pPI->cPrinters = 0;
}


////////////////////////////////////////////////////////////////////////////
//
//  TermPrint
//
////////////////////////////////////////////////////////////////////////////

VOID TermPrint(void)
{
#ifdef WINNT
    Print_UnloadLibraries();           // printnew.cpp
#endif
}





/*========================================================================*/
/*                   Page Setup <-> Print Dialog                          */
/*========================================================================*/


////////////////////////////////////////////////////////////////////////////
//
//  TransferPSD2PD
//
////////////////////////////////////////////////////////////////////////////

VOID TransferPSD2PD(
    PPRINTINFO pPI)
{
    if (pPI->pPSD && pPI->pPD)
    {
        pPI->pPD->hDevMode  = pPI->pPSD->hDevMode;
        pPI->pPD->hDevNames = pPI->pPSD->hDevNames;
    }
}


////////////////////////////////////////////////////////////////////////////
//
//  TransferPD2PSD
//
////////////////////////////////////////////////////////////////////////////

VOID TransferPD2PSD(
    PPRINTINFO pPI)
{
    if (pPI->pPSD && pPI->pPD)
    {
        pPI->pPSD->hDevMode  = pPI->pPD->hDevMode;
        pPI->pPSD->hDevNames = pPI->pPD->hDevNames;
    }
}


#ifdef UNICODE

////////////////////////////////////////////////////////////////////////////
//
//  TransferPSD2PDA
//
////////////////////////////////////////////////////////////////////////////

VOID TransferPSD2PDA(
    PPRINTINFO pPI)
{
    if (pPI->pPSD && pPI->pPDA)
    {
        pPI->pPDA->hDevMode  = pPI->pPSD->hDevMode;
        pPI->pPDA->hDevNames = pPI->pPSD->hDevNames;
    }
}


////////////////////////////////////////////////////////////////////////////
//
//  TransferPDA2PSD
//
////////////////////////////////////////////////////////////////////////////

VOID TransferPDA2PSD(
    PPRINTINFO pPI)
{
    if (pPI->pPSD && pPI->pPDA)
    {
        pPI->pPSD->hDevMode  = pPI->pPDA->hDevMode;
        pPI->pPSD->hDevNames = pPI->pPDA->hDevNames;
    }
}

#endif





/*========================================================================*/
/*                 Ansi->Unicode Thunk routines                           */
/*========================================================================*/

#ifdef UNICODE

////////////////////////////////////////////////////////////////////////////
//
//  ThunkPageSetupDlg
//
////////////////////////////////////////////////////////////////////////////

BOOL ThunkPageSetupDlg(
    PPRINTINFO pPI,
    LPPAGESETUPDLGA pPSDA)
{
    LPPRINTDLGA pPDA;


    if (!pPSDA)
    {
        StoreExtendedError(CDERR_INITIALIZATION);
        return (FALSE);
    }

    if (pPSDA->lStructSize != sizeof(PAGESETUPDLGA))
    {
        StoreExtendedError(CDERR_STRUCTSIZE);
        return (FALSE);
    }

    if ((pPSDA->Flags & PSD_RETURNDEFAULT) &&
        (pPSDA->hDevNames || pPSDA->hDevMode))
    {
        StoreExtendedError(PDERR_RETDEFFAILURE);
        return (FALSE);
    }

    //
    //  Reset the size of the pPSD structure to the UNICODE size and
    //  save it in the pPI structure.
    //
    //  NOTE:  This must be reset back to the ANSI size before
    //         returning to the caller.
    //
    pPSDA->lStructSize = sizeof(PAGESETUPDLGW);
    pPI->pPSD = (LPPAGESETUPDLG)pPSDA;
    pPI->ApiType = COMDLG_ANSI;

    //
    //  Create the ANSI version of the print dialog structure.
    //
    if (pPDA = GlobalAlloc(GPTR, sizeof(PRINTDLGA)))
    {
        pPI->pPDA = pPDA;

        pPDA->lStructSize         = sizeof(PRINTDLGA);
        pPDA->hwndOwner           = pPSDA->hwndOwner;
        pPDA->Flags               = PD_PAGESETUP |
                                      (pPSDA->Flags &
                                        (PSD_NOWARNING |
                                         PSD_SHOWHELP |
                                         PSD_ENABLEPAGESETUPHOOK |
                                         PSD_ENABLEPAGESETUPTEMPLATE |
                                         PSD_ENABLEPAGESETUPTEMPLATEHANDLE |
                                         CD_WX86APP |
                                         PSD_NONETWORKBUTTON));
        pPDA->hInstance           = pPSDA->hInstance;
        pPDA->lCustData           = pPSDA->lCustData;
        pPDA->lpfnSetupHook       = pPSDA->lpfnPageSetupHook;
        pPDA->lpSetupTemplateName = pPSDA->lpPageSetupTemplateName;
        pPDA->hSetupTemplate      = pPSDA->hPageSetupTemplate;

        pPDA->hDevMode            = pPSDA->hDevMode;
        pPDA->hDevNames           = pPSDA->hDevNames;
    }
    else
    {
        StoreExtendedError(CDERR_MEMALLOCFAILURE);
        return (FALSE);
    }

    return (TRUE);
}


////////////////////////////////////////////////////////////////////////////
//
//  FreeThunkPageSetupDlg
//
////////////////////////////////////////////////////////////////////////////

VOID FreeThunkPageSetupDlg(
    PPRINTINFO pPI)
{
    //
    //  Reset the size of the pPSD structure to the correct value.
    //
    if (pPI->pPSD)
    {
        pPI->pPSD->lStructSize = sizeof(PAGESETUPDLGA);
    }

    //
    //  Free the ANSI print dialog structure.
    //
    if (pPI->pPDA)
    {
        GlobalFree(pPI->pPDA);
        pPI->pPDA = NULL;
    }
}


////////////////////////////////////////////////////////////////////////////
//
//  ThunkPrintDlg
//
////////////////////////////////////////////////////////////////////////////

BOOL ThunkPrintDlg(
    PPRINTINFO pPI,
    LPPRINTDLGA pPDA)
{
    LPPRINTDLGW pPDW;
    LPDEVMODEA pDMA;
    DWORD cbLen;


    if (!pPDA)
    {
        StoreExtendedError(CDERR_INITIALIZATION);
        return (FALSE);
    }

    if (pPDA->lStructSize != sizeof(PRINTDLGA))
    {
        StoreExtendedError(CDERR_STRUCTSIZE);
        return (FALSE);
    }

    if (!(pPDW = GlobalAlloc(GPTR, sizeof(PRINTDLGW))))
    {
        StoreExtendedError(CDERR_MEMALLOCFAILURE);
        return (FALSE);
    }

    //
    //  IN-only constant stuff.
    //
    pPDW->lStructSize    = sizeof(PRINTDLGW);
    pPDW->hwndOwner      = pPDA->hwndOwner;
    pPDW->hInstance      = pPDA->hInstance;
    pPDW->lpfnPrintHook  = pPDA->lpfnPrintHook;
    pPDW->lpfnSetupHook  = pPDA->lpfnSetupHook;
    pPDW->hPrintTemplate = pPDA->hPrintTemplate;
    pPDW->hSetupTemplate = pPDA->hSetupTemplate;

    //
    //  IN-OUT Variable Structs.
    //
    if ((pPDA->hDevMode) && (pDMA = GlobalLock(pPDA->hDevMode)))
    {
        //
        //  Make sure the device name in the devmode is not too long such that
        //  it has overwritten the other devmode fields.
        //
        if ((pDMA->dmSize < MIN_DEVMODE_SIZEA) ||
            (lstrlenA(pDMA->dmDeviceName) > CCHDEVICENAME))
        {
            pPDW->hDevMode = NULL;
        }
        else
        {
            pPDW->hDevMode = GlobalAlloc( GHND,
                                          sizeof(DEVMODEW) + pDMA->dmDriverExtra );
        }
        GlobalUnlock(pPDA->hDevMode);
    }
    else
    {
        pPDW->hDevMode = NULL;
    }

    //
    //  Thunk Device Names A => W
    //
    pPDW->hDevNames = NULL;
    if (pPDA->hDevNames)
    {
        // ignore the error case since we can't handle it either way.
        HRESULT hr = ThunkDevNamesA2W(pPDA->hDevNames, &pPDW->hDevNames);
        ASSERT(SUCCEEDED(hr));
    }

    //
    //  IN-only constant strings.
    //
    //  Init Print TemplateName constant.
    //
    if ((pPDA->Flags & PD_ENABLEPRINTTEMPLATE) && (pPDA->lpPrintTemplateName))
    {
        //
        //  See if it's a string or an integer.
        //
        if (!IS_INTRESOURCE(pPDA->lpPrintTemplateName))
        {
            //
            //  String.
            //
            cbLen = lstrlenA(pPDA->lpPrintTemplateName) + 1;
            if (!(pPDW->lpPrintTemplateName =
                     GlobalAlloc( GPTR,
                                  (cbLen * sizeof(WCHAR)) )))
            {
                StoreExtendedError(CDERR_MEMALLOCFAILURE);
                return (FALSE);
            }
            else
            {
                pPI->fPrintTemplateAlloc = TRUE;
                SHAnsiToUnicode(pPDA->lpPrintTemplateName,(LPWSTR)pPDW->lpPrintTemplateName, cbLen);
            }
        }
        else
        {
            //
            //  Integer.
            //
            (DWORD_PTR)pPDW->lpPrintTemplateName = (DWORD_PTR)pPDA->lpPrintTemplateName;
        }
    }
    else
    {
        pPDW->lpPrintTemplateName = NULL;
    }

    //
    //  Init Print Setup TemplateName constant.
    //
    if ((pPDA->Flags & PD_ENABLESETUPTEMPLATE) && (pPDA->lpSetupTemplateName))
    {
        //
        //  See if it's a string or an integer.
        //
        if (!IS_INTRESOURCE(pPDA->lpSetupTemplateName))
        {
            //
            //  String.
            //
            cbLen = lstrlenA(pPDA->lpSetupTemplateName) + 1;
            if (!(pPDW->lpSetupTemplateName =
                      GlobalAlloc( GPTR,
                                   (cbLen * sizeof(WCHAR)) )))
            {
                StoreExtendedError(CDERR_MEMALLOCFAILURE);
                return (FALSE);
            }
            else
            {
                pPI->fSetupTemplateAlloc = TRUE;
                SHAnsiToUnicode(pPDA->lpSetupTemplateName,(LPWSTR)pPDW->lpSetupTemplateName,cbLen);
            }
        }
        else
        {
            //
            //  Integer.
            //
            (DWORD_PTR)pPDW->lpSetupTemplateName = (DWORD_PTR)pPDA->lpSetupTemplateName;
        }
    }
    else
    {
        pPDW->lpSetupTemplateName = NULL;
    }

    pPI->pPD = pPDW;
    pPI->pPDA = pPDA;
    pPI->ApiType = COMDLG_ANSI;

    return (TRUE);
}


////////////////////////////////////////////////////////////////////////////
//
//  FreeThunkPrintDlg
//
////////////////////////////////////////////////////////////////////////////

VOID FreeThunkPrintDlg(
    PPRINTINFO pPI)
{
    LPPRINTDLGW pPDW = pPI->pPD;


    if (!pPDW)
    {
        return;
    }

    if (pPDW->hDevNames)
    {
        GlobalFree(pPDW->hDevNames);
    }

    if (pPDW->hDevMode)
    {
        GlobalFree(pPDW->hDevMode);
    }

    if (pPI->fPrintTemplateAlloc)
    {
        GlobalFree((LPWSTR)(pPDW->lpPrintTemplateName));
    }

    if (pPI->fSetupTemplateAlloc)
    {
        GlobalFree((LPWSTR)(pPDW->lpSetupTemplateName));
    }

    GlobalFree(pPDW);
    pPI->pPD = NULL;
}


////////////////////////////////////////////////////////////////////////////
//
//  ThunkPrintDlgA2W
//
////////////////////////////////////////////////////////////////////////////

VOID ThunkPrintDlgA2W(
    PPRINTINFO pPI)
{
    LPPRINTDLGW pPDW = pPI->pPD;
    LPPRINTDLGA pPDA = pPI->pPDA;


    //
    //  Copy info A => W
    //
    pPDW->hDC           = pPDA->hDC;
    pPDW->Flags         = pPDA->Flags;
    pPDW->nFromPage     = pPDA->nFromPage;
    pPDW->nToPage       = pPDA->nToPage;
    pPDW->nMinPage      = pPDA->nMinPage;
    pPDW->nMaxPage      = pPDA->nMaxPage;
    pPDW->nCopies       = pPDA->nCopies;
    pPDW->lCustData     = pPDA->lCustData;
    pPDW->lpfnPrintHook = pPDA->lpfnPrintHook;
    pPDW->lpfnSetupHook = pPDA->lpfnSetupHook;

    //
    //  Thunk Device Names A => W
    //
    if (pPDA->hDevNames)
    {
        // ignore the error case since we can't handle it either way.
        HRESULT hr = ThunkDevNamesA2W(pPDA->hDevNames, &pPDW->hDevNames);
        ASSERT(SUCCEEDED(hr));
    }

    //
    //  Thunk Device Mode A => W
    //
    if (pPDA->hDevMode && pPDW->hDevMode)
    {
        LPDEVMODEW pDMW = GlobalLock(pPDW->hDevMode);
        LPDEVMODEA pDMA = GlobalLock(pPDA->hDevMode);

        ThunkDevModeA2W(pDMA, pDMW);

        GlobalUnlock(pPDW->hDevMode);
        GlobalUnlock(pPDA->hDevMode);
    }
}


////////////////////////////////////////////////////////////////////////////
//
//  ThunkPrintDlgW2A
//
////////////////////////////////////////////////////////////////////////////

VOID ThunkPrintDlgW2A(
    PPRINTINFO pPI)
{
    LPPRINTDLGA pPDA = pPI->pPDA;
    LPPRINTDLGW pPDW = pPI->pPD;

    //
    //  Copy info W => A
    //
    pPDA->hDC           = pPDW->hDC;
    pPDA->Flags         = pPDW->Flags;
    pPDA->nFromPage     = pPDW->nFromPage;
    pPDA->nToPage       = pPDW->nToPage;
    pPDA->nMinPage      = pPDW->nMinPage;
    pPDA->nMaxPage      = pPDW->nMaxPage;
    pPDA->nCopies       = pPDW->nCopies;
    pPDA->lCustData     = pPDW->lCustData;
    pPDA->lpfnPrintHook = pPDW->lpfnPrintHook;
    pPDA->lpfnSetupHook = pPDW->lpfnSetupHook;

    //
    //  Thunk Device Names W => A
    //
    if (pPDW->hDevNames)
    {
        // ignore the error case since we can't handle it either way.
        HRESULT hr = ThunkDevNamesW2A(pPDW->hDevNames, &pPDA->hDevNames);
        ASSERT(SUCCEEDED(hr));
    }

    //
    //  Thunk Device Mode W => A
    //
    if (pPDW->hDevMode)
    {
        LPDEVMODEW pDMW = GlobalLock(pPDW->hDevMode);
        LPDEVMODEA pDMA;

        if (pPDA->hDevMode)
        {
            HANDLE  handle;
            handle = GlobalReAlloc( pPDA->hDevMode,
                                            sizeof(DEVMODEA) + pDMW->dmDriverExtra,
                                            GHND );
            //Make sure realloc succeeded.
            if (handle)
            {
                pPDA->hDevMode = handle;
            }
            else
            {
                //Realloc didn't succeed. Free the memory occupied
                pPDA->hDevMode = GlobalFree(pPDA->hDevMode);
            }

        }
        else
        {
            pPDA->hDevMode = GlobalAlloc( GHND,
                                          sizeof(DEVMODEA) + pDMW->dmDriverExtra );
        }
        if (pPDA->hDevMode)
        {
            pDMA = GlobalLock(pPDA->hDevMode);
            ThunkDevModeW2A(pDMW, pDMA);
            GlobalUnlock(pPDA->hDevMode);
        }
        GlobalUnlock(pPDW->hDevMode);
    }
}

////////////////////////////////////////////////////////////////////////////
//
//  ThunkDevModeA2W
//
////////////////////////////////////////////////////////////////////////////

VOID ThunkDevModeA2W(
    LPDEVMODEA pDMA,
    LPDEVMODEW pDMW)
{
    LPDEVMODEA pDevModeA;

    if (!pDMA || !pDMW)
    {
        return;
    }

    //
    //  Make sure the device name in the devmode is not too long such that
    //  it has overwritten the other devmode fields.
    //
    if ((pDMA->dmSize < MIN_DEVMODE_SIZEA) ||
        (lstrlenA(pDMA->dmDeviceName) > CCHDEVICENAME))
    {
        return;
    }

    //
    //  We need to create a temporary ANSI that is a known size.
    //  The problem is if we are being called from WOW, the WOW
    //  app could be either a Windows 3.1 or 3.0 app.  The size
    //  of the devmode structure was different for both of these
    //  versions compared to the DEVMODE structure in NT.
    //  By copying the ANSI devmode to one we allocate, then we
    //  can access all of the fields (26 currently) without causing
    //  an access violation.
    //
    pDevModeA = GlobalAlloc(GPTR, sizeof(DEVMODEA) + pDMA->dmDriverExtra);
    if (!pDevModeA)
    {
        return;
    }

    CopyMemory( (LPBYTE)pDevModeA,
                (LPBYTE)pDMA,
                min(sizeof(DEVMODEA), pDMA->dmSize) );

    CopyMemory( (LPBYTE)pDevModeA + sizeof(DEVMODEA),
                (LPBYTE)pDMA + pDMA->dmSize,
                pDMA->dmDriverExtra );

    //
    //  Now we can thunk the contents of the ANSI structure to the
    //  Unicode structure.
    //
    SHAnsiToUnicode((LPSTR)pDevModeA->dmDeviceName,(LPWSTR)pDMW->dmDeviceName,CCHDEVICENAME );

    pDMW->dmSpecVersion   = pDevModeA->dmSpecVersion;
    pDMW->dmDriverVersion = pDevModeA->dmDriverVersion;
    pDMW->dmSize          = sizeof(DEVMODEW);
    pDMW->dmDriverExtra   = pDevModeA->dmDriverExtra;
    pDMW->dmFields        = pDevModeA->dmFields;
    pDMW->dmOrientation   = pDevModeA->dmOrientation;
    pDMW->dmPaperSize     = pDevModeA->dmPaperSize;
    pDMW->dmPaperLength   = pDevModeA->dmPaperLength;
    pDMW->dmPaperWidth    = pDevModeA->dmPaperWidth;
    pDMW->dmScale         = pDevModeA->dmScale;
    pDMW->dmCopies        = pDevModeA->dmCopies;
    pDMW->dmDefaultSource = pDevModeA->dmDefaultSource;
    pDMW->dmPrintQuality  = pDevModeA->dmPrintQuality;
    pDMW->dmColor         = pDevModeA->dmColor;
    pDMW->dmDuplex        = pDevModeA->dmDuplex;
    pDMW->dmYResolution   = pDevModeA->dmYResolution;
    pDMW->dmTTOption      = pDevModeA->dmTTOption;
    pDMW->dmCollate       = pDevModeA->dmCollate;

    SHAnsiToUnicode((LPSTR)pDevModeA->dmFormName,(LPWSTR)pDMW->dmFormName,CCHFORMNAME );

    pDMW->dmLogPixels        = pDevModeA->dmLogPixels;
    pDMW->dmBitsPerPel       = pDevModeA->dmBitsPerPel;
    pDMW->dmPelsWidth        = pDevModeA->dmPelsWidth;
    pDMW->dmPelsHeight       = pDevModeA->dmPelsHeight;
    pDMW->dmDisplayFlags     = pDevModeA->dmDisplayFlags;
    pDMW->dmDisplayFrequency = pDevModeA->dmDisplayFrequency;

    pDMW->dmICMMethod        = pDevModeA->dmICMMethod;
    pDMW->dmICMIntent        = pDevModeA->dmICMIntent;
    pDMW->dmMediaType        = pDevModeA->dmMediaType;
    pDMW->dmDitherType       = pDevModeA->dmDitherType;

    pDMW->dmReserved1        = pDevModeA->dmReserved1;
    pDMW->dmReserved2        = pDevModeA->dmReserved2;

    pDMW->dmPanningWidth     = pDevModeA->dmPanningWidth;
    pDMW->dmPanningHeight    = pDevModeA->dmPanningHeight;

    CopyMemory( (pDMW + 1),
                (pDevModeA + 1),
                pDevModeA->dmDriverExtra );

    //
    //  Free the memory we allocated.
    //
    GlobalFree(pDevModeA);
}


////////////////////////////////////////////////////////////////////////////
//
//  ThunkDevModeW2A
//
////////////////////////////////////////////////////////////////////////////

VOID ThunkDevModeW2A(
    LPDEVMODEW pDMW,
    LPDEVMODEA pDMA)
{
    if (!pDMW || !pDMA)
    {
        return;
    }


    SHUnicodeToAnsi((LPWSTR)pDMW->dmDeviceName,(LPSTR)pDMA->dmDeviceName,CCHDEVICENAME);

    pDMA->dmSpecVersion   = pDMW->dmSpecVersion;
    pDMA->dmDriverVersion = pDMW->dmDriverVersion;
    pDMA->dmSize          = sizeof(DEVMODEA);
    pDMA->dmDriverExtra   = pDMW->dmDriverExtra;
    pDMA->dmFields        = pDMW->dmFields;
    pDMA->dmOrientation   = pDMW->dmOrientation;
    pDMA->dmPaperSize     = pDMW->dmPaperSize;
    pDMA->dmPaperLength   = pDMW->dmPaperLength;
    pDMA->dmPaperWidth    = pDMW->dmPaperWidth;
    pDMA->dmScale         = pDMW->dmScale;
    pDMA->dmCopies        = pDMW->dmCopies;
    pDMA->dmDefaultSource = pDMW->dmDefaultSource;
    pDMA->dmPrintQuality  = pDMW->dmPrintQuality;
    pDMA->dmColor         = pDMW->dmColor;
    pDMA->dmDuplex        = pDMW->dmDuplex;
    pDMA->dmYResolution   = pDMW->dmYResolution;
    pDMA->dmTTOption      = pDMW->dmTTOption;
    pDMA->dmCollate       = pDMW->dmCollate;

    SHUnicodeToAnsi((LPWSTR)pDMW->dmFormName,(LPSTR)pDMA->dmFormName,CCHFORMNAME);

    pDMA->dmLogPixels        = pDMW->dmLogPixels;
    pDMA->dmBitsPerPel       = pDMW->dmBitsPerPel;
    pDMA->dmPelsWidth        = pDMW->dmPelsWidth;
    pDMA->dmPelsHeight       = pDMW->dmPelsHeight;
    pDMA->dmDisplayFlags     = pDMW->dmDisplayFlags;
    pDMA->dmDisplayFrequency = pDMW->dmDisplayFrequency;

    pDMA->dmICMMethod        = pDMW->dmICMMethod;
    pDMA->dmICMIntent        = pDMW->dmICMIntent;
    pDMA->dmMediaType        = pDMW->dmMediaType;
    pDMA->dmDitherType       = pDMW->dmDitherType;

    pDMA->dmReserved1        = pDMW->dmReserved1;
    pDMA->dmReserved2        = pDMW->dmReserved2;

    pDMA->dmPanningWidth     = pDMW->dmPanningWidth;
    pDMA->dmPanningHeight    = pDMW->dmPanningHeight;

    CopyMemory( (pDMA + 1),
                (pDMW + 1),
                pDMA->dmDriverExtra );
}


////////////////////////////////////////////////////////////////////////////
//
//  AllocateUnicodeDevMode
//
//  Purpose:    Allocates a Unicode devmode structure, and calls
//              the thunk function to fill it in.
//
//  Parameters: LPDEVMODEA pANSIDevMode
//
//  Return:     LPDEVMODEW - pointer to new devmode if successful
//                           NULL if not.
//
////////////////////////////////////////////////////////////////////////////

LPDEVMODEW AllocateUnicodeDevMode(
    LPDEVMODEA pANSIDevMode)
{
    int iSize;
    LPDEVMODEW pUnicodeDevMode;

    //
    //  Check for NULL pointer.
    //
    if (!pANSIDevMode)
    {
        return (NULL);
    }

    //
    //  Determine output structure size.  This has two components:  the
    //  DEVMODEW structure size,  plus any private data area.  The latter
    //  is only meaningful when a structure is passed in.
    //
    iSize = sizeof(DEVMODEW);

    iSize += pANSIDevMode->dmDriverExtra;

    pUnicodeDevMode = GlobalAlloc(GPTR, iSize);

    if (!pUnicodeDevMode)
    {
        return (NULL);
    }

    //
    //  Now call the thunk routine to copy the ANSI devmode to the
    //  Unicode devmode.
    //
    ThunkDevModeA2W(pANSIDevMode, pUnicodeDevMode);

    //
    //  Return the pointer.
    //
    return (pUnicodeDevMode);
}


////////////////////////////////////////////////////////////////////////////
//
//  AllocateAnsiDevMode
//
//  Purpose:    Allocates a Ansi devmode structure, and calls
//              the thunk function to fill it in.
//
//  Parameters: LPDEVMODEW pUnicodeDevMode
//
//  Return:     LPDEVMODEA - pointer to new devmode if successful
//                           NULL if not.
//
////////////////////////////////////////////////////////////////////////////

LPDEVMODEA AllocateAnsiDevMode(
    LPDEVMODEW pUnicodeDevMode)
{
    int iSize;
    LPDEVMODEA pANSIDevMode;

    //
    //  Check for NULL pointer.
    //
    if (!pUnicodeDevMode)
    {
        return (NULL);
    }

    //
    //  Determine output structure size.  This has two components:  the
    //  DEVMODEW structure size,  plus any private data area.  The latter
    //  is only meaningful when a structure is passed in.
    //
    iSize = sizeof(DEVMODEA);

    iSize += pUnicodeDevMode->dmDriverExtra;

    pANSIDevMode = GlobalAlloc(GPTR, iSize);

    if (!pANSIDevMode)
    {
        return (NULL);
    }

    //
    //  Now call the thunk routine to copy the Unicode devmode to the
    //  ANSI devmode.
    //
    ThunkDevModeW2A(pUnicodeDevMode, pANSIDevMode);

    //
    //  Return the pointer.
    //
    return (pANSIDevMode);
}


#ifdef WINNT

////////////////////////////////////////////////////////////////////////////
//
//  Ssync_ANSI_UNICODE_PD_For_WOW
//
//  Function to allow NT WOW to keep the ANSI & UNICODE versions of
//  the CHOOSEFONT structure in ssync as required by many 16-bit apps.
//  See notes for Ssync_ANSI_UNICODE_Struct_For_WOW() in dlgs.c.
//
////////////////////////////////////////////////////////////////////////////

VOID Ssync_ANSI_UNICODE_PD_For_WOW(
    HWND hDlg,
    BOOL f_ANSI_to_UNICODE)
{
    PPRINTINFO pPI;

    if (pPI = (PPRINTINFO)GetProp(hDlg, PRNPROP))
    {
        if (pPI->pPD && pPI->pPDA)
        {
            if (f_ANSI_to_UNICODE)
            {
                ThunkPrintDlgA2W(pPI);
            }
            else
            {
                ThunkPrintDlgW2A(pPI);
            }
        }
    }
}

#endif

#endif

////////////////////////////////////////////////////////////////////////////
//
//  SetCopiesEditWidth
//
// Adjust the width of the copies edit control using the current
// font and the scroll bar width.  This is necessary to handle the
// the up down control from encroching on the space in the edit
// control when we are in High Contrast (extra large) mode.
//
////////////////////////////////////////////////////////////////////////////

VOID SetCopiesEditWidth(
    HWND hDlg,
    HWND hControl)
{
    HDC hDC                 = NULL;
    LONG MaxDigitExtant     = 0;
    LONG EditControlWidth   = 0;
    LONG CurrentWidth       = 0;
    UINT i                  = 0;
    INT aDigitWidth[10];
    WINDOWPLACEMENT WndPl;

    //
    // Acquire the edit controls device context.
    //
    hDC = GetDC( hControl );

    if (hDC)
    {
        //
        // Determine max width of the digit group.
        //
        if (GetCharWidth32( hDC, TEXT('0'), TEXT('9'), aDigitWidth))
        {
            for (i = 0; i < ARRAYSIZE(aDigitWidth); i++)
            {
                if (aDigitWidth[i] > MaxDigitExtant)
                {
                    MaxDigitExtant = aDigitWidth[i];
                }
            }

            //
            // Get the edit control placement.
            //
            WndPl.length = sizeof( WndPl );

            if (GetWindowPlacement( hControl, &WndPl ))
            {
                //
                // Calculate the edit control current width.
                //
                EditControlWidth = MaxDigitExtant * COPIES_EDIT_SIZE;

                //
                // Calculate the current width of the edit control.
                //
                CurrentWidth = WndPl.rcNormalPosition.right - WndPl.rcNormalPosition.left;

                //
                // Set the new position of the edit control.
                //
                WndPl.rcNormalPosition.left = WndPl.rcNormalPosition.left - (EditControlWidth - CurrentWidth);

                //
                // Place the control.
                //
                SetWindowPlacement( hControl, &WndPl );
            }
        }

        //
        // Release the device context.
        //
        ReleaseDC( hControl, hDC );
    }
}

////////////////////////////////////////////////////////////////////////////
//
// CountDigits
//
// Count how many digits is needed for a specific number
//
////////////////////////////////////////////////////////////////////////////

WORD
CountDigits(
    DWORD dwNumber)
{
    WORD    cDigits = 0;

    if(dwNumber == 0)
    {
        return 1;
    }

    while(dwNumber > 0)
    {
        cDigits++;
        dwNumber /= 10;
    }

    return cDigits++;
}