#include "pch.h"
#pragma hdrstop

#include <initguid.h>
#include "winprtp.h"

/*-----------------------------------------------------------------------------
/ Local functions / data
/----------------------------------------------------------------------------*/

static TCHAR c_szColor[]                    = TEXT("color");
static TCHAR c_szDuplex[]                   = TEXT("duplex");
static TCHAR c_szStaple[]                   = TEXT("stapling");
static TCHAR c_szResolution[]               = TEXT("resolution");
static TCHAR c_szSpeed[]                    = TEXT("speed");
static TCHAR c_szPaperSize[]                = TEXT("size");
static TCHAR c_szPrintPaperSize[]           = TEXT("(printMediaReady=%s*)");
static TCHAR c_szPrintResolution[]          = TEXT("(printMaxResolutionSupported>=%s)");
static TCHAR c_szPrintSpeed[]               = TEXT("(printPagesPerMinute>=%d)");
static TCHAR c_szLocationQuery[]            = TEXT("(location=%s*)");
static TCHAR c_szLocationQueryComplex[]     = TEXT("(|(location=%s/*)(location=%s))");
static TCHAR c_szBlank[]                    = TEXT("");
static TCHAR c_szLocationTag[]              = TEXT("Location");
static TCHAR c_szDynamicTag[]               = TEXT("$DynamicLocation$");
static TCHAR c_szPrinterPolicy[]            = TEXT("Software\\Policies\\Microsoft\\Windows NT\\Printers");
static TCHAR c_szPhysicalLocationFeature[]  = TEXT("PhysicalLocationSupport");

static WCHAR c_szPrinterName[]              = L"printerName";
static WCHAR c_szServerName[]               = L"serverName";
static WCHAR c_szQueryPrefix[]              = L"(uncName=*)(objectCategory=printQueue)";
static WCHAR c_szPrintColor[]               = L"(printColor=TRUE)";
static WCHAR c_szPrintDuplex[]              = L"(printDuplexSupported=TRUE)";
static WCHAR c_szPrintStapling[]            = L"(printStaplingSupported=TRUE)";
static WCHAR c_szPrintModelProp[]           = L"driverName";

#define MAX_LOCATION_WAIT_TIME              30000
#define MAX_LOCATION_MSG_WAIT_TIME          60000
#define MAX_LOCATION                        MAX_PATH

static LPWSTR c_szClassList[] =
{
    L"printQueue",
};

static PAGECTRL ctrls1[] =
{
    IDC_PRINTNAME,     c_szPrinterName,     FILTER_CONTAINS,
    IDC_PRINTMODEL,    c_szPrintModelProp,  FILTER_CONTAINS,
};

static COLUMNINFO columns[] =
{
    0, 0, IDS_CN,          0, c_szPrinterName,
    0, 0, IDS_LOCATION,    0, c_szLocation,
    0, 0, IDS_MODEL,       0, c_szPrintModelProp,
    0, 0, IDS_SERVERNAME,  0, c_szServerName,
    0, DEFAULT_WIDTH_DESCRIPTION, IDS_COMMENT, 0, c_szDescription,
};

static struct
{
    INT     idString;
    LPCTSTR szString;
}
Resolutions [] =
{
    IDS_ANY,        NULL,
    IDS_72,         TEXT("72"),
    IDS_144,        TEXT("144"),
    IDS_300,        TEXT("300"),
    IDS_600,        TEXT("600"),
    IDS_1200,       TEXT("1200"),
    IDS_2400,       TEXT("2400"),
    IDS_4800,       TEXT("4800"),
    IDS_9600,       TEXT("9600"),
    IDS_32000,      TEXT("32000"),
};

#define IDH_NOHELP                      ((DWORD)-1) // Disables Help for a control
static const DWORD aFormHelpIDs[]=
{
    IDC_PRINTNAME,      IDH_PRINTER_NAME,
    IDC_PRINTLOCATION,  IDH_PRINTER_LOCATION,
    IDC_PRINTBROWSE,    IDH_PRINTER_LOCATION,
    IDC_PRINTMODEL,     IDH_PRINTER_MODEL,
    IDC_PRINTDUPLEX,    IDH_DOUBLE_SIDED,
    IDC_PRINTSTAPLE,    IDH_STAPLE,
    IDC_PRINTCOLOR,     IDH_PRINT_COLOR,
    IDC_PRINTPAGESIZE,  IDH_PAPER_SIZE,
    IDC_PRINTRES,       IDH_RESOLUTION,
    IDC_PRINTRES_POSTFIX, IDH_RESOLUTION,
    IDC_PRINTSPEED,     IDH_SPEED,
    IDC_PRINTSPEED_UPDN,IDH_SPEED,
    IDC_PRINTSPEED_POSTFIX, IDH_SPEED,
    IDC_SEPLINE,        IDH_NOHELP,
    0, 0,
};

/*-----------------------------------------------------------------------------
/ CPrintQueryPage class
/----------------------------------------------------------------------------*/
class CPrintQueryPage
{
public:

    CPrintQueryPage( HWND hwnd );
    ~CPrintQueryPage();
    HRESULT Initialize( HWND hwnd, BOOL bSynchronous );
    LPCTSTR GetSearchText( VOID );
    UINT AddRef( VOID );
    UINT Release( VOID );
    VOID TimerExpire();
    VOID EnableLocationEditText( HWND hwnd, BOOL bEnable );
    VOID LocationEditTextChanged( HWND hwnd );
    VOID BrowseForLocation( HWND hwnd );
    HRESULT PersistLocation(HWND hwnd, IPersistQuery* pPersistQuery, BOOL fRead);
    VOID OnInitDialog( HWND hwnd );

private:

    CPrintQueryPage( CPrintQueryPage &rhs );
    CPrintQueryPage & operator=( CPrintQueryPage &rhs );

    VOID WaitForLocation( HWND hwnd );
    DWORD Discovery( VOID );
    VOID TimerCreate( VOID );
    VOID TimerRelease( VOID );
    VOID SetLocationText( HWND hCtrl, LPCTSTR pszString, BOOL fReadOnly, BOOL fIgnoreWorkingText );
    static DWORD WINAPI _PhysicalLocationThread( PVOID pVoid );

    IPhysicalLocation *m_pPhysicalLocation;
    LPTSTR             m_pszPhysicalLocation;
    LONG               m_cRef;
    HWND               m_hCtrl;
    BOOL               m_fThreadCreated;
    BOOL               m_fComplete;
    BOOL               m_fLocationEnableState;
    BOOL               m_fLocationUserModified;
    BOOL               m_bValid;
    HWND               m_hwnd;
    UINT_PTR           m_hTimer;
    HANDLE             m_hComplete;
    LPTSTR             m_pszWorkingText;
};

/*-----------------------------------------------------------------------------
/ CPrintQueryPage
/ ---------------------
/   Constructor, creates the IPhysicalLocation object.  If we are returned
/   a good interface pointer indicates the class is valid.
/
/ In:
/   None.
/
/ Out:
/   Nothing.
/----------------------------------------------------------------------------*/
CPrintQueryPage::CPrintQueryPage( HWND hwnd )
    : m_pPhysicalLocation( NULL ),
      m_pszPhysicalLocation( NULL ),
      m_cRef( 1 ),
      m_hCtrl( NULL ),
      m_fThreadCreated( FALSE ),
      m_fComplete( FALSE ),
      m_hwnd( hwnd ),
      m_hTimer( NULL ),
      m_fLocationEnableState( TRUE ),
      m_fLocationUserModified( FALSE ),
      m_hComplete( NULL ),
      m_pszWorkingText( NULL ),
      m_bValid( FALSE )
{
    TraceEnter(TRACE_FORMS, "CPrintQueryPage::CPrintQueryPage");

    //
    // The physical location feature can be disable using a group
    // policy setting.  If the feature is disabled we will just
    // fail to aquire the physical location interface and continue
    // operation with out pre-populating the location edit control.
    //
    HRESULT hr = CoCreateInstance( CLSID_PrintUIShellExtension,
                                   0,
                                   CLSCTX_INPROC_SERVER,
                                   IID_IPhysicalLocation,
                                   (VOID**)&m_pPhysicalLocation );
    if (SUCCEEDED( hr ))
    {
        //
        // Check if the physical location policy is enabled.
        //
        if (SUCCEEDED(m_pPhysicalLocation->ShowPhysicalLocationUI()))
        {
            TimerCreate();

            m_hComplete = CreateEvent( NULL, TRUE, FALSE, NULL );

            if (m_hComplete)
            {
                //
                // Attempt to fetch the working text from the resource file.
                //
                TCHAR szBuffer[MAX_PATH] = {0};

                if (LoadString(GLOBAL_HINSTANCE, IDS_PRINT_WORKING_TEXT, szBuffer, ARRAYSIZE(szBuffer)))
                {
                    hr = LocalAllocString (&m_pszWorkingText, szBuffer);
                }
                else
                {
                    TraceAssert(FALSE);
                }

                //
                // Indicate the class is in a valid state, i.e. usable.
                //
                m_bValid = TRUE;
            }
        }
    }

    TraceLeave();
}

/*-----------------------------------------------------------------------------
/ ~CPrintQueryPage
/ ---------------------
/   Destructor, release the IPhysicalLocation object and the location string.
/
/ In:
/   None.
/
/ Out:
/   Nothing.
/----------------------------------------------------------------------------*/
CPrintQueryPage::~CPrintQueryPage()
{
    TraceEnter(TRACE_FORMS, "CPrintQueryPage::~CPrintQueryPage");

    if (m_pPhysicalLocation)
    {
        m_pPhysicalLocation->Release();
    }

    LocalFreeString(&m_pszPhysicalLocation);

    //
    // Only release the string if it was allocated and it is not the null string.
    //
    if (m_pszWorkingText && (m_pszWorkingText != c_szBlank))
    {
        LocalFreeString(&m_pszWorkingText);
    }

    TimerRelease();

    if (m_hComplete)
    {
        CloseHandle( m_hComplete );
    }

    TraceLeave();
}

/*-----------------------------------------------------------------------------
/ AddRef
/ ---------------------
/   Increases the reference count of this object.   This is method is used to
/   control the life time of this class when a backgroud thread is used to fetch
/   the physical location string.
/
/ In:
/   None.
/
/ Out:
/   New object refrence count.
/----------------------------------------------------------------------------*/
UINT CPrintQueryPage::AddRef( VOID )
{
    return InterlockedIncrement(&m_cRef);
}

/*-----------------------------------------------------------------------------
/ Release
/ ---------------------
/   Decreases the reference count of this object.   This is method is used to
/   control the life time of this class when a backgroud thread is used to fetch
/   the physical location string.
/
/ In:
/   None.
/
/ Out:
/   New object refrence count.
/----------------------------------------------------------------------------*/
UINT CPrintQueryPage::Release (VOID)
{
    if (!InterlockedDecrement(&m_cRef))
    {
        delete this;
        return 0;
    }
    return m_cRef;
}

/*-----------------------------------------------------------------------------
/ GetSearchText
/ ---------------------
/   Returns a pointer to the current search text.  The search text is the
/   physical location path returned from the IPhysicalLocation object.  If either
/   the search text does not exist or not found this routine will return a
/   NULL string.
/
/ In:
/   None.
/
/ Out:
/   Ponter to the search text or the NULL string.
/----------------------------------------------------------------------------*/
LPCTSTR CPrintQueryPage::GetSearchText( VOID )
{
    return m_pszPhysicalLocation ? m_pszPhysicalLocation : c_szBlank;
}

/*-----------------------------------------------------------------------------
/ Initialize
/ ---------------------
/   Creates the background thread and calls the physical location discovery
/   method.
/
/ In:
/   Edit control window handle where to place text when done.
/   bSynchronous flag TRUE use backgroud thread, FALSE call synchronously.
/
/ Out:
/   HRESULT hr.
/----------------------------------------------------------------------------*/
HRESULT CPrintQueryPage::Initialize( HWND hwnd, BOOL bSynchronous )
{
    TraceEnter(TRACE_FORMS, "CPrintQueryPage::Initialize");

    HRESULT hr          = S_OK;
    DWORD   dwThreadID  = 0;
    HANDLE  hThread     = NULL;

    //
    // If we have a valid physical location interface and the thread was not created,
    // then create it now and call the discovery method.
    //
    if (m_bValid && !m_fThreadCreated)
    {
        //
        // Bump the objects refrence count, needed for the async thread.
        //
        AddRef();

        //
        // Save the window handle in the class so the background thread
        // knows what window to set the location text to.
        //
        m_hCtrl = hwnd;

        //
        // Increase this libraries object refcount, ole will not unload until
        // we hit zeor and DllCanUnloadNow returns true.
        //
        DllAddRef();

        //
        // Only create the thread once.
        //
        m_fThreadCreated = TRUE;

        //
        // If we are requested to do a synchronous call then just call the
        // thread proc directly.
        //
        if (bSynchronous)
        {
            hr = (_PhysicalLocationThread( this ) == ERROR_SUCCESS) ? S_OK : E_FAIL;
        }
        else
        {
            //
            // Create the background thread.
            //
            hThread = CreateThread( NULL,
                                    0,
                                    reinterpret_cast<LPTHREAD_START_ROUTINE>(CPrintQueryPage::_PhysicalLocationThread),
                                    reinterpret_cast<LPVOID>( this ),
                                    0,
                                    &dwThreadID);

            TraceAssert(hThread);

            //
            // If the thread failed creation clean up the dll refrence count
            // and the object refrence and the thread created flag.
            //
            if (!hThread)
            {
                m_fThreadCreated = FALSE;
                DllRelease();
                Release();
                hr = E_FAIL;
            }
            else
            {
                //
                // Thread is running just close the handle, we let the thread die
                // on its own normally.
                //
                CloseHandle(hThread);

                //
                // Indicate the request is pending.
                //
                hr = HRESULT_FROM_WIN32 (ERROR_IO_PENDING);
            }
        }
    }

    //
    // If we have a valid interface pointer and the background thread
    // has not completed then indicated the data is still pending.
    //
    else if(m_bValid && !m_fComplete)
    {
        //
        // Indicate the request is pending.
        //
        hr = HRESULT_FROM_WIN32 (ERROR_IO_PENDING);
    }

    //
    // If we failed with IO_PENDING then set the working text.
    //
    if (FAILED(hr) && HRESULT_CODE(hr) == ERROR_IO_PENDING)
    {
        //
        // Set the new location text.
        //
        SetLocationText (hwnd, m_pszWorkingText, TRUE, TRUE);
        PostMessage (m_hCtrl, EM_SETSEL, 0, 0);
    }

    TraceLeaveResult(hr);
}

/*-----------------------------------------------------------------------------
/ _PhysicalLocationThread
/ ---------------------
/   This routine is the backgroud thread thunk.  It accepts the CPrintQueryPage
/   this pointer and then calles the actual discovery method.  The purpose of
/   this routine is simple to capture the this pointer after the thread was
/   created and then invoke a method.
/
/ In:
/   Pointer to PrintQueryPage class.
/
/ Out:
/   TRUE success, FALSE error occurred.
/----------------------------------------------------------------------------*/
DWORD WINAPI CPrintQueryPage::_PhysicalLocationThread( PVOID pVoid )
{
    DWORD dwRetval = ERROR_OUTOFMEMORY;

    if ( SUCCEEDED(CoInitialize(NULL)) )
    {
        //
        // Get a pointer to this class.
        //
        CPrintQueryPage *pPrintQueryPage = reinterpret_cast<CPrintQueryPage *>( pVoid );

        //
        // Invoke the location discovery process.
        //
        dwRetval = pPrintQueryPage->Discovery();

        //
        // Set the completion event, in case someone is waiting.
        //
        SetEvent(pPrintQueryPage->m_hComplete);

        //
        // Indicate the discovery process completed.
        //
        pPrintQueryPage->m_fComplete = TRUE;

        //
        // Release the timer
        //
        pPrintQueryPage->TimerRelease();

        //
        // Release the refrence to the PrintQueryPage class.
        //
        pPrintQueryPage->Release();

        //
        // COM no longer needed
        //

        CoUninitialize();
    }

    DllRelease();
    return dwRetval;
}

/*-----------------------------------------------------------------------------
/ Discovery
/ ---------
/   This routine is the backgroud thread discovery process.  Since the act
/   of figuring out the physical location of this machin must hit the net
/   it can take a significant amount of time.  Hence we do this in a separate
/   thread.
/
/ In:
/   Nothing.
/
/ Out:
/   TRUE success, FALSE error occurred.
/----------------------------------------------------------------------------*/
DWORD CPrintQueryPage::Discovery( VOID )
{
    TraceEnter(TRACE_FORMS, "CPrintQueryPage::Discovery");

    //
    // Start the discovery process for finding the physical location search text
    // for this machine.
    //
    HRESULT hr = m_pPhysicalLocation->DiscoverPhysicalLocation();

    if (SUCCEEDED( hr ))
    {
        BSTR pbsPhysicalLocation = NULL;

        //
        // Get the physical location search text.
        //
        hr = m_pPhysicalLocation->GetSearchPhysicalLocation( &pbsPhysicalLocation );

        //
        // If the error indicates the length was returned then allocate the text buffer.
        //
        if (SUCCEEDED( hr ) && pbsPhysicalLocation)
        {
            //
            // Release the previous string if any.
            //
            if (m_pszPhysicalLocation)
            {
                LocalFreeString(&m_pszPhysicalLocation);
            }

            //
            // Convert the BSTR location string to a TSTR string.
            //
            hr = LocalAllocStringW2T( &m_pszPhysicalLocation, pbsPhysicalLocation );
        }

        //
        // Release the physical location string if it was allocated.
        //
        if( pbsPhysicalLocation )
        {
            SysFreeString( pbsPhysicalLocation );
        }
    }

    //
    // Set the new location text.
    //
    SetLocationText( m_hCtrl, GetSearchText(), FALSE, FALSE );

    TraceLeaveValue(SUCCEEDED( hr ) ? ERROR_SUCCESS : ERROR_OUTOFMEMORY);
}

/*-----------------------------------------------------------------------------
/ WaitForLocation
/ ---------------------
/   Wait for the printer location information.
/
/ In:
/   hwnd parent window handle.
/
/ Out:
/   BOOL TRUE if success, FALSE if error.
/----------------------------------------------------------------------------*/
VOID CPrintQueryPage::WaitForLocation( HWND hwnd )
{
    TraceEnter(TRACE_FORMS, "CPrintQueryPage::WaitForLocation");

    //
    // Only wait if we have a valid location interface pointer and
    // completion event handle was created and the thread is running.
    //
    if (m_bValid && m_hComplete && m_fThreadCreated)
    {
        //
        // Keep waiting until the physical location is avaialble or a timeout.
        //
        for (BOOL fExit = FALSE; !fExit; )
        {
            switch (MsgWaitForMultipleObjects(1, &m_hComplete, FALSE, MAX_LOCATION_MSG_WAIT_TIME, QS_ALLINPUT))
            {
            case WAIT_OBJECT_0:
                fExit = TRUE;
                break;

            case WAIT_TIMEOUT:
                fExit = TRUE;
                break;

            default:
                {
                    //
                    // Process any message now.
                    //
                    MSG msg;

                    while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
                    {
                        TranslateMessage(&msg);
                        DispatchMessage(&msg);
                    }
                    break;
                }
            }
        }
    }

    TraceLeave();
}

/*-----------------------------------------------------------------------------
/ TimerCreate
/ ---------------------
/ Create the timer event to detect if the discovery method is taking too long.
/
/ In:
/
/ Out:
/----------------------------------------------------------------------------*/
VOID CPrintQueryPage::TimerCreate( VOID )
{
    TraceEnter(TRACE_FORMS, "CPrintQueryPage::TimerCreate");

    if (!m_hTimer)
    {
        m_hTimer = SetTimer(m_hwnd, WM_USER, MAX_LOCATION_WAIT_TIME, NULL);
    }

    TraceLeave();
}

/*-----------------------------------------------------------------------------
/ TimerRelease
/ ---------------------
/ Release the timer event.
/
/ In:
/
/ Out:
/----------------------------------------------------------------------------*/
VOID CPrintQueryPage::TimerRelease( VOID )
{
    TraceEnter(TRACE_FORMS, "CPrintQueryPage::TimerRelease");

    if (m_hTimer)
    {
        KillTimer(m_hwnd, m_hTimer);
        m_hTimer = NULL;
    }

    TraceLeave();
}

/*-----------------------------------------------------------------------------
/ TimerExpire
/ ---------------------
/
/ In:
/
/ Out:
/----------------------------------------------------------------------------*/
VOID CPrintQueryPage::TimerExpire( VOID )
{
    TraceEnter(TRACE_FORMS, "CPrintQueryPage::TimerExpire");

    //
    // The search data is not complete
    //
    if (!m_fComplete)
    {
        //
        // Blank out the location text, it took too long to find.
        //
        SetLocationText(m_hCtrl, c_szBlank, FALSE, TRUE);

        //
        // Set the completion event, in case someone is waiting.
        //
        SetEvent(m_hComplete);

        //
        // Indicate the discovery process completed.
        //
        m_fComplete = TRUE;
    }

    //
    // Release the timer, the time is a one shot notification.
    //
    TimerRelease();

    TraceLeave();
}


/*-----------------------------------------------------------------------------
/ EnableLocationEditText
/ ---------------------
/   Enabled or disable the location edit text only if it is does not contain
/   the pending text.
/
/ In:
/   hwnd parent window handle.
/
/ Out:
/   BOOL TRUE if success, FALSE if error.
/----------------------------------------------------------------------------*/
VOID CPrintQueryPage::EnableLocationEditText( HWND hwnd, BOOL bEnable )
{
    TraceEnter(TRACE_FORMS, "CPrintQueryPage::EnableLocationEditText");

    HWND hCtrl          = GetDlgItem(hwnd, IDC_PRINTLOCATION);
    HWND hBrowseCtrl    = GetDlgItem(hwnd, IDC_PRINTBROWSE);

    //
    // If the CPrintQueryPage is valid then handle the location
    // edit control differently.
    //
    if (m_bValid)
    {
        TCHAR szBuffer[MAX_LOCATION] = {0};

        //
        // Save the previous location enable state.
        //
        m_fLocationEnableState = bEnable;

        //
        // Get the current location text.
        //
        GetWindowText(hCtrl, szBuffer, ARRAYSIZE(szBuffer));

        //
        // Do not change the location edit control enable state when the
        // working text is there.  The reason for this is the text
        // is hard to read when the control is disabled, but when the
        // control is just read only the text is black not gray hence
        // eaiser to read.
        //
        if (!_tcscmp(szBuffer, m_pszWorkingText))
        {
            //
            // For an unknown reason the control with the location
            // text has the input focus, the default input focus
            // should be on the printer name therefore I will
            // set the focus here.
            //
            SetFocus(GetDlgItem(hwnd, IDC_PRINTNAME));
        }
        else
        {
            EnableWindow(hBrowseCtrl, bEnable);
            EnableWindow(hCtrl, bEnable);
        }
    }
    else
    {
        EnableWindow(hBrowseCtrl, bEnable);
        EnableWindow(hCtrl, bEnable);
    }

    TraceLeave();
}


/*-----------------------------------------------------------------------------
/ LocationEditTextChanged
/ ---------------------
/
/ In:
/   hwnd parent window handle.
/
/ Out:
/   BOOL TRUE if success, FALSE if error.
/----------------------------------------------------------------------------*/
VOID CPrintQueryPage::LocationEditTextChanged( HWND hwnd )
{
    TraceEnter(TRACE_FORMS, "CPrintQueryPage::LocationEditTextChanged");

    //
    // The search data is complete
    //
    if (m_fComplete)
    {
        m_fLocationUserModified = TRUE;
    }

    TraceLeave();
}

/*-----------------------------------------------------------------------------
/ PersistLocation
/ ---------------------
/
/ In:
/   hwnd parent window handle.
/
/ Out:
/   BOOL TRUE if success, FALSE if error.
/----------------------------------------------------------------------------*/
HRESULT CPrintQueryPage::PersistLocation(HWND hwnd, IPersistQuery* pPersistQuery, BOOL fRead)
{
    TraceEnter(TRACE_FORMS, "CPrintQueryPage::PersistLocation");

    HRESULT hr                      = S_OK;
    TCHAR   szBuffer[MAX_LOCATION]  = {0};

    //
    // Get the control handle for the location edit control
    //
    HWND hCtrl = GetDlgItem(hwnd, IDC_PRINTLOCATION);

    //
    // Are we to read the persisted query string.
    //
    if (fRead)
    {
        //
        // Read the persisted location string.
        //
        hr = pPersistQuery->ReadString( c_szMsPrintersMore, c_szLocationTag, szBuffer, ARRAYSIZE( szBuffer ) );
        FailGracefully(hr, "Failed to read location state");

        //
        // Assume this is the exact string.
        //
        LPCTSTR pLocation = szBuffer;

        //
        // If the dynamic sentinal was found then wait for the dynamic location
        // text to be avaiable.
        //
        if (!_tcscmp(szBuffer, c_szDynamicTag))
        {
            WaitForLocation(hwnd);
            pLocation = GetSearchText();
        }

        //
        // Set the persisted location string in the query form.
        //
        SetLocationText(hCtrl, pLocation, FALSE, TRUE);
    }
    else
    {
        //
        // If the user modified the location text then save this text, otherwize
        // save a sentinal string which indicates we are to determine the location
        // dynamically when the persisted query is read back.
        //
        if (m_fLocationUserModified)
        {
            GetWindowText(hCtrl, szBuffer, ARRAYSIZE(szBuffer));
            hr = pPersistQuery->WriteString( c_szMsPrintersMore, c_szLocationTag, szBuffer );
            FailGracefully(hr, "Failed to write location state");
        }
        else
        {
            hr = pPersistQuery->WriteString( c_szMsPrintersMore, c_szLocationTag, c_szDynamicTag );
            FailGracefully(hr, "Failed to write location working state");
        }
    }

exit_gracefully:

    TraceLeaveResult(hr);
}

/*-----------------------------------------------------------------------------
/ SetLocationText
/ ---------------------
/
/ In:
/   hwnd parent window handle.
/
/ Out:
/   BOOL TRUE if success, FALSE if error.
/----------------------------------------------------------------------------*/
VOID CPrintQueryPage::SetLocationText( HWND hCtrl, LPCTSTR pszString, BOOL fReadOnly, BOOL fIgnoreWorkingText )
{
    TraceEnter(TRACE_FORMS, "CPrintQueryPage::SetLocationText");

    if (IsWindow(hCtrl))
    {
        //
        // Is the CPrintQueryPage in a valid state.
        //
        if (m_bValid)
        {
            TCHAR szBuffer[MAX_LOCATION];

            //
            // Read the current location text.
            //
            GetWindowText(hCtrl, szBuffer, ARRAYSIZE(szBuffer));

            //
            // Stick the location string in the edit control if it contains working.
            //
            if (!_tcscmp(szBuffer, m_pszWorkingText) || fIgnoreWorkingText)
            {
                SetWindowText(hCtrl, pszString);
            }

            //
            // Reset the control to non read only state.
            //
            SendMessage(hCtrl, EM_SETREADONLY, fReadOnly, 0);

            //
            // Enable the control if the read only is disabled.
            //
            if (!fReadOnly)
            {
                //
                // Enable the edit control.
                //
                EnableWindow(hCtrl, m_fLocationEnableState);
            }

            //
            // Only enable the browse button when we have a location string
            // and the then control is not in read only mode.
            //
            EnableWindow(GetDlgItem(m_hwnd, IDC_PRINTBROWSE), !fReadOnly && m_fLocationEnableState);
        }
        else
        {
            //
            // If we are not using the location interface, just set the location text.
            //
            SetWindowText(hCtrl, pszString);
        }
    }

    TraceLeave();
}


/*-----------------------------------------------------------------------------
/ BrowseForLocation
/ ---------------------
/   Starts the browse for location tree view and populates the edit control
/   with a valid selection.
/
/ In:
/   hwnd parent window handle.
/
/ Out:
/   Nothing.
/----------------------------------------------------------------------------*/
VOID CPrintQueryPage::BrowseForLocation( HWND hwnd )
{
    TraceEnter(TRACE_FORMS, "CPrintQueryPage::BrowseForLocation");

    if (m_bValid)
    {
        BSTR    pbPhysicalLocation  = NULL;
        BSTR    pbDefaultLocation   = NULL;
        LPTSTR  pszPhysicalLocation = NULL;
        HRESULT hr                  = E_FAIL;
        TCHAR   szText[MAX_LOCATION]= {0};

        //
        // Convert the physical location to a BSTR for the IPhysicalLocation
        // object can pre-expand the browse tree.
        //
        if (GetWindowText(GetDlgItem(hwnd, IDC_PRINTLOCATION), szText, ARRAYSIZE(szText)))
        {
            pbDefaultLocation = T2BSTR(szText);
        }
        else
        {
            pbDefaultLocation = T2BSTR(m_pszPhysicalLocation);
        }

        //
        // Display the location tree.
        //
        hr = m_pPhysicalLocation->BrowseForLocation(hwnd, pbDefaultLocation, &pbPhysicalLocation);

        if(SUCCEEDED(hr) && pbPhysicalLocation)
        {
            //
            // Convert the BSTR location string to a TSTR string.
            //
            hr = LocalAllocStringW2T(&pszPhysicalLocation, pbPhysicalLocation);

            if(SUCCEEDED(hr))
            {
                //
                // Set the location text.
                //
                SetLocationText(m_hCtrl, pszPhysicalLocation, FALSE, TRUE);
            }

            //
            // Release the TCHAR physical location string.
            //
            LocalFreeString(&pszPhysicalLocation);

            //
            // Release the physical location string.
            //
            SysFreeString(pbPhysicalLocation);
        }

        //
        // Release the default locatin string.
        //
        SysFreeString(pbDefaultLocation);
    }

    TraceLeave();
}

/*-----------------------------------------------------------------------------
/ OnInitDialog
/ ---------------------
/   Set the UI's initial state, on down level machines the browse button is
/   removed and the edit control is stetched to match the size of the other
/   edit controls, i.e. name, model.
/
/ In:
/   hwnd parent window handle.
/
/ Out:
/   Nothing.
/----------------------------------------------------------------------------*/
VOID CPrintQueryPage::OnInitDialog( HWND hwnd )
{
    TraceEnter(TRACE_FORMS, "CPrintQueryPage::OnInitDialog");

    if (!m_bValid)
    {
        //
        // If the IPhysicalLocation interface is not available, hide the browse
        // button and extend the location edit control appropriately
        //
        RECT rcName     = {0};
        RECT rcLocation = {0};

        GetWindowRect (GetDlgItem (hwnd, IDC_PRINTNAME), &rcName);

        GetWindowRect (GetDlgItem (hwnd, IDC_PRINTLOCATION), &rcLocation);

        SetWindowPos (GetDlgItem (hwnd, IDC_PRINTLOCATION),
                      NULL,
                      0,0,
                      rcName.right - rcName.left,
                      rcLocation.bottom - rcLocation.top,
                      SWP_NOMOVE|SWP_NOZORDER);

        ShowWindow (GetDlgItem (hwnd, IDC_PRINTBROWSE), SW_HIDE);
    }

    TraceLeave();
}

/*-----------------------------------------------------------------------------
/ PopulateLocationEditText
/ ---------------------
/   Populates the location edit control with the default location of this
/   machine.
/
/ In:
/   hwnd parent window handle.
/
/ Out:
/   BOOL TRUE if success, FALSE if error.
/----------------------------------------------------------------------------*/
BOOL PopulateLocationEditText( HWND hwnd, BOOL bClearField )
{
    TraceEnter(TRACE_FORMS, "PopulateLocationEditText");

    CPrintQueryPage *pPrintQueryPage = reinterpret_cast<CPrintQueryPage *>(GetWindowLongPtr(hwnd, DWLP_USER));

    if (pPrintQueryPage)
    {
        HWND hCtrl = GetDlgItem(hwnd, IDC_PRINTLOCATION);

        HRESULT hr = pPrintQueryPage->Initialize( hCtrl, FALSE );

        if (SUCCEEDED( hr ))
        {
            if( bClearField )
            {
                SetWindowText( hCtrl, c_szBlank);
            }
            else
            {
                SetWindowText( hCtrl, pPrintQueryPage->GetSearchText( ));
            }
        }
    }

    TraceLeaveValue(TRUE);
}


/*-----------------------------------------------------------------------------
/ bEnumForms
/ ----------
/   Enumerates the forms on the printer identified by the handle.
/
/ In:
/   IN HANDLE   hPrinter,
/   IN DWORD    dwLevel,
/   IN PBYTE   *ppBuff,
/   IN PDWORD   pcReturned
/
/ Out:
/   Pointer to forms array and count of forms in the array if
/   success, NULL ponter and zero number of forms if failure.
/   BOOL TRUE if success, FALSE if error.
/----------------------------------------------------------------------------*/
BOOL
bEnumForms(
    IN HANDLE       hPrinter,
    IN DWORD        dwLevel,
    IN PFORM_INFO_1 *ppFormInfo,
    IN PDWORD       pcReturned
    )
{
    BOOL            bReturn     = FALSE;
    DWORD           dwReturned  = 0;
    DWORD           dwNeeded    = 0;
    PBYTE           p           = NULL;
    BOOL            bStatus     = FALSE;

    //
    // Get buffer size for enum forms.
    //
    bStatus = EnumForms( hPrinter, dwLevel, NULL, 0, &dwNeeded, &dwReturned );

    //
    // Check if the function returned the buffer size.
    //
    if( GetLastError() != ERROR_INSUFFICIENT_BUFFER )
    {
        goto Cleanup;
    }

    //
    // If buffer allocation fails.
    //
    p = (PBYTE)LocalAlloc( LPTR, dwNeeded );

    if( p ==  NULL )
    {
        goto Cleanup;
    }

    //
    // Get the forms enumeration
    //
    bStatus = EnumForms( hPrinter, dwLevel, p, dwNeeded, &dwNeeded, &dwReturned );

    //
    // Copy back the buffer pointer and count.
    //
    if( bStatus )
    {
        bReturn     = TRUE;
        *ppFormInfo = (PFORM_INFO_1)p;
        *pcReturned = dwReturned;
    }

Cleanup:

    if( bReturn == FALSE )
    {
        //
        // Indicate failure.
        //
        *ppFormInfo = NULL;
        *pcReturned = 0;

        //
        // Release any allocated memory.
        //
        if ( p )
        {
            LocalFree( p );
        }
    }

    return bReturn;
}

/*-----------------------------------------------------------------------------
/ PopulatePrintPageSize
/ ----------------
/   Eumerates all the pages size from this machine's print spooler.  This allows
/   a user to choose from a list of available forms rather than remembering the
/   name of the particular form.
/
/ In:
/   hwnd parent window handle.
/
/ Out:
/   BOOL TRUE if success, FALSE if error.
/----------------------------------------------------------------------------*/
BOOL PopulatePrintPageSize( HWND hwnd )
{
    HANDLE          hServer     = NULL;
    PFORM_INFO_1    pFormInfo   = NULL;
    DWORD           FormCount   = 0;
    BOOL            bRetval     = FALSE;
    TCHAR           szBuffer[MAX_PATH];

    //
    // Open the local print server with default access.
    //
    BOOL bStatus = OpenPrinter( NULL, &hServer, NULL );

    if( bStatus )
    {
        //
        // Enumerate the forms.
        //
        bStatus = bEnumForms( hServer, 1, &pFormInfo, &FormCount );
    }

    if( bStatus && pFormInfo )
    {
        //
        // Fill the combo box.
        //
        for( UINT i = 0; i < FormCount; i++ )
        {
            ComboBox_AddString( GetDlgItem( hwnd, IDC_PRINTPAGESIZE ), pFormInfo[i].pName );
        }

        //
        // Set the limit text in the form name edit control
        //
        ComboBox_LimitText( GetDlgItem( hwnd, IDC_PRINTPAGESIZE ), CCHFORMNAME-1 );

        //
        // Return success.
        //
        bRetval = TRUE;
    }

    if( pFormInfo )
    {
        //
        // Release the forms buffer if it was allocated.
        //
        LocalFree( pFormInfo );
    }

    if ( hServer )
    {
        ClosePrinter(hServer);
    }

    return bRetval;
}


/*-----------------------------------------------------------------------------
/ PopulatePrintSpeed
/ ----------------
/ Set the print speed up down arrow control with an upper and lower
/ bound range.
/
/ In:
/   hwnd parent window handle
/
/ Out:
/   BOOL TRUE if success, FALSE if error.
/----------------------------------------------------------------------------*/
BOOL PopulatePrintSpeed( HWND hwnd )
{
    //
    // Set the print speed up down arrow range.
    //
    SendMessage( GetDlgItem( hwnd, IDC_PRINTSPEED_UPDN ), UDM_SETRANGE, 0, MAKELPARAM( 9999, 1 ) );
    Edit_LimitText(GetDlgItem(hwnd, IDC_PRINTSPEED), 4);
    return TRUE;
}


/*-----------------------------------------------------------------------------
/ PopulateResolution
/ ----------------
/ Fill the print resolution contrl with valid resolution information.
/
/ In:
/   hwnd
/
/ Out:
/   BOOL TRUE if success, FALSE if error.
/----------------------------------------------------------------------------*/
BOOL PopulatePrintResolution( HWND hwnd )
{
    TCHAR szBuffer[MAX_PATH];

    //
    // Fill in the print resolution combo-box.
    //
    for( INT i = 0; i < ARRAYSIZE( Resolutions ); i++ )
    {
        if( !LoadString(GLOBAL_HINSTANCE, Resolutions[i].idString, szBuffer, ARRAYSIZE(szBuffer)))
        {
            TraceAssert(FALSE);
        }
        ComboBox_AddString( GetDlgItem( hwnd, IDC_PRINTRES ), szBuffer );
    }

    return TRUE;
}

/*-----------------------------------------------------------------------------
/ GetPrinterMoreParameters.
/ ----------------
/ Build the query string from the controls on the printer more page.
/
/ In:
/   hwnd parent window handle.
/   pLen pointer to length of query string.
/   pszBuffer pointer to buffer where to return the query string.
/
/ Out:
/   Nothing.
/----------------------------------------------------------------------------*/
VOID GetPrinterMoreParameters( HWND hwnd, UINT *puLen, LPWSTR pszBuffer )
{
    USES_CONVERSION;
    TCHAR   szScratch[MAX_PATH] = {0};
    TCHAR   szText[MAX_PATH]    = {0};
    INT     i                   = 0;

    //
    // Read the check box states and build the query string.
    //
    if( Button_GetCheck( GetDlgItem( hwnd, IDC_PRINTDUPLEX ) ) == BST_CHECKED )
        PutStringElementW(pszBuffer, puLen, c_szPrintDuplex);

    if( Button_GetCheck( GetDlgItem( hwnd, IDC_PRINTCOLOR ) ) == BST_CHECKED )
        PutStringElementW(pszBuffer, puLen, c_szPrintColor);

    if( Button_GetCheck( GetDlgItem( hwnd, IDC_PRINTSTAPLE ) ) == BST_CHECKED )
        PutStringElementW(pszBuffer, puLen, c_szPrintStapling);

    //
    // Read the paper size setting.
    //
    ComboBox_GetText( GetDlgItem( hwnd, IDC_PRINTPAGESIZE ), szText, ARRAYSIZE( szText ) );

    if( lstrlen( szText ) )
    {
        wsprintf( szScratch, c_szPrintPaperSize, szText );
        PutStringElementW(pszBuffer, puLen, T2W(szScratch));
    }

    //
    // Read the printer resolution setting
    //
    i = ComboBox_GetCurSel( GetDlgItem( hwnd, IDC_PRINTRES ) );

    if( i > 0 && i < ARRAYSIZE( Resolutions ) )
    {
        wsprintf( szScratch, c_szPrintResolution, Resolutions[i].szString );
        PutStringElementW(pszBuffer, puLen, T2W(szScratch));
    }

    //
    // Read the printer speed setting
    //
    i = (LONG)SendMessage( GetDlgItem( hwnd, IDC_PRINTSPEED_UPDN ), UDM_GETPOS, 0, 0 );

    if( LOWORD(i) > 1 && i != -1 )
    {
        wsprintf( szScratch, c_szPrintSpeed, i );
        PutStringElementW(pszBuffer, puLen, T2W(szScratch));
    }
}

/*-----------------------------------------------------------------------------
/ GetPrinterLocationParameter.
/ ----------------
/ Build the query string from the location control on the printer page.
/
/ In:
/   hwnd parent window handle.
/   pLen pointer to length of query string.
/   pszBuffer pointer to buffer where to return the query string.
/
/ Out:
/   Nothing.
/----------------------------------------------------------------------------*/
VOID GetPrinterLocationParameter( HWND hwnd, UINT *puLen, LPWSTR pszBuffer )
{
    USES_CONVERSION;
    TCHAR   szScratch[MAX_PATH*2]   = {0};
    TCHAR   szText[MAX_PATH]        = {0};
    TCHAR   szWorkingText[MAX_PATH] = {0};
    DWORD   dwLocationLength        = 0;

    HWND hCtrl = GetDlgItem(hwnd, IDC_PRINTLOCATION);

    if ( hCtrl != NULL )
    {
        dwLocationLength = GetWindowText(hCtrl, szText, ARRAYSIZE(szText));
        if (dwLocationLength != 0)
        {
            if (LoadString(GLOBAL_HINSTANCE, IDS_PRINT_WORKING_TEXT, szWorkingText, ARRAYSIZE(szWorkingText)))
            {
                if (_tcscmp(szText, szWorkingText))
                {
                    BOOL fUseMoreComplexSearch = FALSE;

                    //
                    // If we have a location that ends in a forward slash,
                    // we'll trim that off and use a slightly more complex
                    // search parameter so that we can pick up locations
                    // that either match the location parameter exactly or
                    // start with the parameter and have a slash immediately
                    // following.
                    //
                    if ( dwLocationLength > 1 ) 
                    {
                        if ( szText[dwLocationLength-1] == TEXT('/') ) 
                        {
                            szText[dwLocationLength-1] = TEXT('\0');
                            fUseMoreComplexSearch = TRUE;
                        }
                    }
                    
                    if ( fUseMoreComplexSearch ) 
                    {
                        wsprintf(szScratch, c_szLocationQueryComplex, szText, szText);
                    }
                    else
                    {
                        wsprintf(szScratch, c_szLocationQuery, szText);
                    }

                    PutStringElementW(pszBuffer, puLen, T2W(szScratch));
                }
                else
                {
                    //
                    // We are not going to wait the location field if the search process
                    // has been kicked off. Just hit the expire timer to cancel the location
                    // thread. This will ensure that the result list and the query params 
                    // will be consistent.
                    //
                    CPrintQueryPage *pPrintQueryPage = reinterpret_cast<CPrintQueryPage *>(GetWindowLongPtr(hwnd, DWLP_USER));
                    if (pPrintQueryPage)
                    {
                        pPrintQueryPage->TimerExpire();
                    }
                }
            }
            else
            {
                TraceAssert(FALSE);
            }
        }
    }
    else
    {
        // GetDlgItem() returned NULL for the location control.
        TraceAssert(FALSE);
    }
}

/*-----------------------------------------------------------------------------
/ Query Page: Printers
/----------------------------------------------------------------------------*/

/*-----------------------------------------------------------------------------
/ PageProc_Printer
/ ----------------
/   PageProc for handling the messages for this object.
/
/ In:
/   pPage -> instance data for this form
/   hwnd = window handle for the form dialog
/   uMsg, wParam, lParam = message parameters
/
/ Out:
/   HRESULT (E_NOTIMPL) if not handled
/----------------------------------------------------------------------------*/
HRESULT CALLBACK PageProc_Printers(LPCQPAGE pPage, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    HRESULT hr = S_OK;
    LPWSTR pQuery = NULL;
    UINT uLen = 0;

    TraceEnter(TRACE_FORMS, "PageProc_Printers");

    switch ( uMsg )
    {
        case CQPM_INITIALIZE:
        case CQPM_RELEASE:
            break;

        case CQPM_ENABLE:
        {
            CPrintQueryPage *pPrintQueryPage = reinterpret_cast<CPrintQueryPage *>(GetWindowLongPtr(hwnd, DWLP_USER));

            if (pPrintQueryPage)
            {
                pPrintQueryPage->EnableLocationEditText( hwnd, (BOOL)wParam );
            }

            // Enable the form controls,
            EnablePageControls(hwnd, ctrls1, ARRAYSIZE(ctrls1), (BOOL)wParam);
            break;
        }

        case CQPM_GETPARAMETERS:
        {
            //
            // Get the printer name and model paramters.
            //
            hr = GetQueryString(&pQuery, c_szQueryPrefix, hwnd, ctrls1, ARRAYSIZE(ctrls1));

            if ( SUCCEEDED(hr) )
            {
                hr = QueryParamsAlloc((LPDSQUERYPARAMS*)lParam, pQuery, GLOBAL_HINSTANCE, ARRAYSIZE(columns), columns);
                LocalFreeStringW(&pQuery);
            }

            //
            // Get the location parameter.
            //
            GetPrinterLocationParameter( hwnd, &uLen, NULL );

            if (uLen)
            {
                hr = LocalAllocStringLenW(&pQuery, uLen);

                if ( SUCCEEDED(hr) )
                {
                    GetPrinterLocationParameter( hwnd, &uLen, pQuery );
                    hr = QueryParamsAddQueryString((LPDSQUERYPARAMS*)lParam, pQuery );
                    LocalFreeStringW(&pQuery);
                }
            }

            FailGracefully(hr, "PageProc_Printers: Failed to build DS argument block");

            break;
        }

        case CQPM_CLEARFORM:
        {
            // Reset the form controls.
            PopulateLocationEditText( hwnd, TRUE );
            ResetPageControls(hwnd, ctrls1, ARRAYSIZE(ctrls1));
            break;
        }

        case CQPM_PERSIST:
        {
            BOOL fRead = (BOOL)wParam;
            IPersistQuery* pPersistQuery = (IPersistQuery*)lParam;

            CPrintQueryPage *pPrintQueryPage = reinterpret_cast<CPrintQueryPage *>(GetWindowLongPtr(hwnd, DWLP_USER));

            if (pPrintQueryPage)
            {
                hr = pPrintQueryPage->PersistLocation(hwnd, pPersistQuery, fRead);
            }

            if (SUCCEEDED(hr))
            {
                // Read the standard controls from the page,
                hr = PersistQuery(pPersistQuery, fRead, c_szMsPrinters, hwnd, ctrls1, ARRAYSIZE(ctrls1));
            }
            FailGracefully(hr, "Failed to persist page");
            break;
        }

        case CQPM_SETDEFAULTPARAMETERS:
        {
            //
            // so that the caller can pass parameters to the form we support an IPropertyBag in the
            // OPENQUERYWINDOW structure.   If wParam == TRUE, and lParam is non-zero then we
            // assume we should decode this structure to get the information we need from it.
            //

            if ( wParam && lParam )
            {
                OPENQUERYWINDOW *poqw = (OPENQUERYWINDOW*)lParam;
                if ( poqw->dwFlags & OQWF_PARAMISPROPERTYBAG )
                {
                    IPropertyBag *ppb = poqw->ppbFormParameters;
                    SetDlgItemFromProperty(ppb, L"printName", hwnd, IDC_PRINTNAME, NULL);
                    SetDlgItemFromProperty(ppb, L"printLocation", hwnd, IDC_PRINTLOCATION, NULL);
                    SetDlgItemFromProperty(ppb, L"printModel", hwnd, IDC_PRINTMODEL, NULL);
                }
            }

            break;
        }

        case CQPM_HELP:
        {
            LPHELPINFO pHelpInfo = (LPHELPINFO)lParam;
            WinHelp((HWND)pHelpInfo->hItemHandle,
                    DSQUERY_HELPFILE,
                    HELP_WM_HELP,
                    (DWORD_PTR)aFormHelpIDs);
            break;
        }

        case DSQPM_GETCLASSLIST:
        {
            hr = ClassListAlloc((LPDSQUERYCLASSLIST*)lParam, c_szClassList, ARRAYSIZE(c_szClassList));
            FailGracefully(hr, "Failed to allocate class list");
            break;
        }

        case DSQPM_HELPTOPICS:
        {
            HWND hwndFrame = (HWND)lParam;
            TraceMsg("About to display help topics for find printers - ocm.chm");
            HtmlHelp(hwndFrame, TEXT("omc.chm"), HH_HELP_FINDER, 0);
            break;
        }

        default:
            hr = E_NOTIMPL;
            break;
    }

exit_gracefully:

    TraceLeaveResult(hr);
}


/*-----------------------------------------------------------------------------
/ DlgProc_Printers
/ ----------------
/   Standard dialog proc for the form, handle any special buttons and other
/   such nastyness we must here.
/
/ In:
/   hwnd, uMsg, wParam, lParam = standard parameters
/
/ Out:
/   BOOL
/----------------------------------------------------------------------------*/
INT_PTR CALLBACK DlgProc_Printers(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    INT_PTR fResult = TRUE;

    CPrintQueryPage *pPrintQueryPage = reinterpret_cast<CPrintQueryPage *>(GetWindowLongPtr(hwnd, DWLP_USER));

    switch ( uMsg )
    {
        case WM_INITDIALOG:
        {
            Edit_LimitText(GetDlgItem(hwnd, IDC_PRINTNAME),     MAX_PATH-1);
            Edit_LimitText(GetDlgItem(hwnd, IDC_PRINTLOCATION), MAX_LOCATION-1);
            Edit_LimitText(GetDlgItem(hwnd, IDC_PRINTMODEL),    MAX_PATH-1);

            pPrintQueryPage = new CPrintQueryPage(hwnd);

            if (pPrintQueryPage)
            {
                SetWindowLongPtr(hwnd, DWLP_USER, reinterpret_cast<LONG_PTR>(pPrintQueryPage));
                pPrintQueryPage->OnInitDialog(hwnd);
                PopulateLocationEditText(hwnd, FALSE);
            }
            else
            {
                fResult = FALSE;
            }
            break;
        }

        case WM_CONTEXTMENU:
        {
            WinHelp((HWND)wParam, DSQUERY_HELPFILE, HELP_CONTEXTMENU, (DWORD_PTR)aFormHelpIDs);
            break;
        }

        case WM_NCDESTROY:
        {
            if (pPrintQueryPage)
            {
                pPrintQueryPage->Release();
            }
            SetWindowLongPtr(hwnd, DWLP_USER, NULL);
            break;
        }

        case WM_TIMER:
        {
            if (pPrintQueryPage)
            {
                pPrintQueryPage->TimerExpire();
            }
            break;
        }

        case WM_COMMAND:
        {
            if((GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE) &&
               (GET_WM_COMMAND_ID(wParam, lParam) == IDC_PRINTLOCATION))
            {
                if (pPrintQueryPage)
                {
                    pPrintQueryPage->LocationEditTextChanged(hwnd);
                }
            }
            else if((GET_WM_COMMAND_ID(wParam, lParam) == IDC_PRINTBROWSE))
            {
                if (pPrintQueryPage)
                {
                    pPrintQueryPage->BrowseForLocation(hwnd);
                }
            }
            else
            {
                fResult = FALSE;
            }
            break;
        }

        default:
        {
            fResult = FALSE;
            break;
        }
    }

    return fResult;
}

/*-----------------------------------------------------------------------------
/ PageProc_PrintersMore
/ ---------------------
/   PageProc for handling the messages for this object.
/
/ In:
/   pPage -> instance data for this form
/   hwnd = window handle for the form dialog
/   uMsg, wParam, lParam = message parameters
/
/ Out:
/   HRESULT (E_NOTIMPL) if not handled
/----------------------------------------------------------------------------*/
HRESULT CALLBACK PageProc_PrintersMore(LPCQPAGE pPage, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    HRESULT     hr          = S_OK;

    TraceEnter(TRACE_FORMS, "PageProc_PrintersMore");

    switch ( uMsg )
    {
        case CQPM_INITIALIZE:
        case CQPM_RELEASE:
            break;

        case CQPM_ENABLE:
            EnableWindow( GetDlgItem( hwnd, IDC_PRINTPAGESIZE ),    (BOOL)wParam );
            EnableWindow( GetDlgItem( hwnd, IDC_PRINTRES ),         (BOOL)wParam );
            EnableWindow( GetDlgItem( hwnd, IDC_PRINTSPEED ),       (BOOL)wParam );
            EnableWindow( GetDlgItem( hwnd, IDC_PRINTSPEED_UPDN ),  (BOOL)wParam );
            EnableWindow( GetDlgItem( hwnd, IDC_PRINTDUPLEX ),      (BOOL)wParam );
            EnableWindow( GetDlgItem( hwnd, IDC_PRINTCOLOR ),       (BOOL)wParam );
            EnableWindow( GetDlgItem( hwnd, IDC_PRINTSTAPLE ),      (BOOL)wParam );
            break;

        case CQPM_GETPARAMETERS:
        {
            LPWSTR pszBuffer = NULL;
            UINT uLen = 0;

            // Format the parameters for the 2nd page of the query form, this builds
            // an LDAP string and then appends it to the string we have in the
            // existing query parameter block.

            GetPrinterMoreParameters( hwnd, &uLen, NULL );

            if ( uLen )
            {
                hr = LocalAllocStringLenW(&pszBuffer, uLen);

                if ( SUCCEEDED(hr) )
                {
                    GetPrinterMoreParameters( hwnd, &uLen, pszBuffer );
                    hr = QueryParamsAddQueryString((LPDSQUERYPARAMS*)lParam, pszBuffer );
                    LocalFreeStringW(&pszBuffer);
                }

                FailGracefully(hr, "PageProc_PrintersMore: Failed to build DS argument block");
            }

            break;
        }

        case CQPM_CLEARFORM:
            SetDlgItemText( hwnd, IDC_PRINTPAGESIZE, TEXT("") );
            ComboBox_SetCurSel( GetDlgItem( hwnd, IDC_PRINTRES ), 0 );
            SendMessage( GetDlgItem( hwnd, IDC_PRINTSPEED_UPDN ), UDM_SETPOS, 0, MAKELPARAM( 1, 0 ) );
            Button_SetCheck( GetDlgItem( hwnd, IDC_PRINTDUPLEX ), BST_UNCHECKED );
            Button_SetCheck( GetDlgItem( hwnd, IDC_PRINTCOLOR ),  BST_UNCHECKED );
            Button_SetCheck( GetDlgItem( hwnd, IDC_PRINTSTAPLE ), BST_UNCHECKED );
            break;

        case CQPM_PERSIST:
        {
            IPersistQuery*  pPersistQuery   = (IPersistQuery*)lParam;
            BOOL            fRead           = (BOOL)wParam;
            INT             i               = 0;
            TCHAR           szBuffer[MAX_PATH];

            if ( fRead )
            {
                hr = pPersistQuery->ReadInt( c_szMsPrintersMore, c_szColor, &i );
                FailGracefully(hr, "Failed to read color state");
                Button_SetCheck( GetDlgItem( hwnd, IDC_PRINTCOLOR ), i ? BST_CHECKED : BST_UNCHECKED );

                hr = pPersistQuery->ReadInt( c_szMsPrintersMore, c_szDuplex, &i );
                FailGracefully(hr, "Failed to read duplex state");
                Button_SetCheck( GetDlgItem( hwnd, IDC_PRINTDUPLEX ), i ? BST_CHECKED : BST_UNCHECKED );

                hr = pPersistQuery->ReadInt( c_szMsPrintersMore, c_szStaple, &i );
                FailGracefully(hr, "Failed to read staple state");
                Button_SetCheck( GetDlgItem( hwnd, IDC_PRINTSTAPLE ), i ? BST_CHECKED : BST_UNCHECKED );

                hr = pPersistQuery->ReadInt( c_szMsPrintersMore, c_szResolution, &i );
                FailGracefully(hr, "Failed to read resolution state");
                ComboBox_SetCurSel( GetDlgItem( hwnd, IDC_PRINTRES ), i );

                hr = pPersistQuery->ReadInt( c_szMsPrintersMore, c_szSpeed, &i );
                FailGracefully(hr, "Failed to read speed state");
                SendMessage( GetDlgItem( hwnd, IDC_PRINTSPEED_UPDN ), UDM_SETPOS, 0, MAKELPARAM( i, 0 ) );

                hr = pPersistQuery->ReadString( c_szMsPrintersMore, c_szPaperSize, szBuffer, ARRAYSIZE( szBuffer ) );
                FailGracefully(hr, "Failed to read paper size state");
                ComboBox_SetText( GetDlgItem( hwnd, IDC_PRINTPAGESIZE ), szBuffer );

            }
            else
            {
                i = Button_GetCheck( GetDlgItem( hwnd, IDC_PRINTCOLOR ) ) == BST_CHECKED ? TRUE : FALSE;
                hr = pPersistQuery->WriteInt( c_szMsPrintersMore, c_szColor, i );
                FailGracefully(hr, "Failed to write color state");

                i = Button_GetCheck( GetDlgItem( hwnd, IDC_PRINTDUPLEX ) ) == BST_CHECKED ? TRUE : FALSE;
                hr = pPersistQuery->WriteInt( c_szMsPrintersMore, c_szDuplex, i );
                FailGracefully(hr, "Failed to write duplex state");

                i = Button_GetCheck( GetDlgItem( hwnd, IDC_PRINTSTAPLE ) ) == BST_CHECKED ? TRUE : FALSE;
                hr = pPersistQuery->WriteInt( c_szMsPrintersMore, c_szStaple, i );
                FailGracefully(hr, "Failed to write staple state");

                i = (INT)ComboBox_GetCurSel( GetDlgItem( hwnd, IDC_PRINTRES ) );
                hr = pPersistQuery->WriteInt( c_szMsPrintersMore, c_szResolution, i );
                FailGracefully(hr, "Failed to write resolution state");

                i = (INT)SendMessage( GetDlgItem( hwnd, IDC_PRINTSPEED_UPDN ), UDM_GETPOS, 0, 0 );
                hr = pPersistQuery->WriteInt( c_szMsPrintersMore, c_szSpeed, LOWORD(i) );
                FailGracefully(hr, "Failed to write speed state");

                ComboBox_GetText( GetDlgItem( hwnd, IDC_PRINTPAGESIZE ), szBuffer, ARRAYSIZE( szBuffer ) );
                hr = pPersistQuery->WriteString( c_szMsPrintersMore, c_szPaperSize, szBuffer );
                FailGracefully(hr, "Failed to write paper size state");

            }

            FailGracefully(hr, "Failed to persist page");

            break;
        }

        case CQPM_HELP:
        {
            LPHELPINFO pHelpInfo = (LPHELPINFO)lParam;
            WinHelp((HWND)pHelpInfo->hItemHandle,
                    DSQUERY_HELPFILE,
                    HELP_WM_HELP,
                    (DWORD_PTR)aFormHelpIDs);
            break;
        }

        case DSQPM_GETCLASSLIST:
            // the PageProc_Printers will have already handled this, no need to do it again! (daviddv, 19jun98)
            break;

        default:
            hr = E_NOTIMPL;
            break;
    }

exit_gracefully:

    TraceLeaveResult(hr);
}


/*-----------------------------------------------------------------------------
/ DlgProc_Printers
/ ----------------
/   Standard dialog proc for the form, handle any special buttons and other
/   such nastyness we must here.
/
/ In:
/   hwnd, uMsg, wParam, lParam = standard parameters
/
/ Out:
/   INT_PTR
/----------------------------------------------------------------------------*/
INT_PTR CALLBACK DlgProc_PrintersMore(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    INT_PTR     fResult     = FALSE;
    LPCQPAGE    pQueryPage  = NULL;

    if ( uMsg == WM_INITDIALOG )
    {
        pQueryPage = (LPCQPAGE)lParam;

        SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)pQueryPage);

        //
        // Fill in the printer forms combo-box.
        //
        PopulatePrintPageSize( hwnd );

        //
        // Fill in the print speed combo-box.
        //
        PopulatePrintSpeed( hwnd );

        //
        // Fill in the print speed combo-box.
        //
        PopulatePrintResolution( hwnd );
    }
    else if ( uMsg == WM_CONTEXTMENU )
    {
        WinHelp((HWND)wParam, DSQUERY_HELPFILE, HELP_CONTEXTMENU, (DWORD_PTR)aFormHelpIDs);
        fResult = TRUE;
    }

    return fResult;
}
