//____________________________________________________________________________
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1995 - 1996.
//
//  File:       general.cxx
//
//  Contents:
//
//  Classes:
//
//  Functions:
//
//  Notes:      For the first release of the scheduling agent, all security
//              operations are disabled under Win95, even Win95 to NT.
//
//  History:    3/4/1996   RaviR   Created
//              1-30-1997   DavidMun   params edit becomes working dir
//              02/29/01   JBenton BUG 280401 app icon was not being updated
//                                 on prop sheet in case of service not running
//
//____________________________________________________________________________


#include "..\pch\headers.hxx"
#pragma hdrstop

#include "..\inc\dll.hxx"
#include "..\folderui\dbg.h"
#include "..\folderui\macros.h"
#include "dlg.hxx"

#include "..\folderui\jobicons.hxx"
#include "..\inc\resource.h"
#include "..\inc\defines.hxx"
#include "..\inc\common.hxx"
#include "..\inc\misc.hxx"
#include "..\inc\policy.hxx"

#if !defined(_CHICAGO_)

#define  SECURITY_WIN32
#include <security.h>            // GetUserNameEx
#undef   SECURITY_WIN32

#include "..\inc\network.hxx"
#endif // !defined(_CHICAGO_)
#include "rc.h"
#include "defines.h"
#include <mstask.h>
#include "uiutil.hxx"
#include "commdlg.h"
#include "helpids.h"
#include "iconhlpr.hxx"
#include "schedui.hxx"
#include "sageset.hxx"

//
//  extern
//

extern HINSTANCE g_hInstance;

#if !defined(_CHICAGO_)
//
// Forward references
//
// CenterDialog - Centers a dialog on screen. Used by the security-related
//                  subdialogs.
//

void
CenterDialog(HWND hDlg);

void
GetDefaultDomainAndUserName(
    LPTSTR ptszDomainAndUserName,
    ULONG  cchBuf);


//
// Launches the modal set password dialog.
//

INT_PTR
LaunchSetPasswordDlg(
    HWND          hWnd,
    AccountInfo * pAccountInfo);

INT_PTR APIENTRY
SetPasswordDlgProc(
    HWND   hDlg,
    UINT   uMsg,
    WPARAM wParam,
    LPARAM lParam);

INT_PTR APIENTRY
SetAccountInformationDlgProc(
    HWND   hDlg,
    UINT   uMsg,
    WPARAM wParam,
    LPARAM lParam);
#endif // !defined(_CHICAGO_)

int
SchedGetDlgItemTextLength(
    HWND hwnd,
    int id);

//
//  (Control id, help id) list for context sensitivity help.
//

ULONG s_aGeneralPageHelpIds[] =
{
    idc_icon,           Hidc_icon,
    lbl_job_name,       Hlbl_job_name,
    lbl_comments,       Hlbl_comments,
    txt_comments,       Htxt_comments,
    lbl_app_name,       Hlbl_app_name,
    txt_app_name,       Htxt_app_name,
    btn_browse,         Hbtn_browse,
    lbl_workingdir,     Hlbl_workingdir,
    txt_workingdir,     Htxt_workingdir,
    lbl_run_as,         Hlbl_execute_as,
    txt_run_as,         Htxt_execute_as,
    btn_passwd,         Hbtn_passwd,
    chk_enable_job,     Hchk_enable_job,
    btn_settings,       Hbtn_settings,
    0,0
};


const ULONG s_aSetPasswordDlgHelpIds[] =
{
    set_passwd_dlg,     Hset_passwd_dlg,
    lbl_sp_passwd,      Hlbl_sp_passwd,
    edt_sp_passwd,      Hedt_sp_passwd,
    lbl_sp_cfrmpasswd,  Hlbl_sp_cfrmpasswd,
    edt_sp_cfrmpasswd,  Hedt_sp_cfrmpasswd,
    0,0
};


extern "C" TCHAR szMstaskHelp[];

const TCHAR SAGERUN_PARAM[] = TEXT("/SAGERUN:");

TCHAR szMstaskHelp[]  = TEXT("%windir%\\help\\mstask.hlp");

//____________________________________________________________________________
//____________________________________________________________________________
//________________                      ______________________________________
//________________  class CGeneralPage  ______________________________________
//________________                      ______________________________________
//____________________________________________________________________________
//____________________________________________________________________________


class CGeneralPage : public CPropPage
{
public:

    CGeneralPage(ITask * pIJob, LPTSTR ptszTaskPath, BOOL fPersistChanges);

    ~CGeneralPage();

private:

    virtual LRESULT _OnInitDialog(LPARAM lParam);
    virtual LRESULT _OnCommand(int id, HWND hwndCtl, UINT codeNotify);
    virtual LRESULT _OnApply(void);
    virtual LRESULT _OnCancel(void);
    virtual LRESULT _OnPSMQuerySibling(WPARAM wParam, LPARAM lParam);
    virtual LRESULT _OnPSNKillActive(LPARAM lParam);
    virtual LRESULT _OnHelp(HANDLE hRequesting, UINT uiHelpCommand);

    VOID    _ExpandAppName(LPTSTR tszApp);
    VOID    _ExpandWorkingDir();
    VOID    _GetAppAndArgs(LPTSTR tszApp, LPTSTR * pptszArg);
    VOID    _UpdateAppNameAndIcon(LPWSTR wszApp, LPWSTR * ppwszArg);
    VOID    _UpdateAppIcon(LPCTSTR tszApp);
    VOID    _SetAppEdit(LPCTSTR tszApp, LPCTSTR tszArgs);
    BOOL    _JobObjectIsLocal();
#if !defined(_CHICAGO_)
    void    _UpdateRunAsControl(void);
#endif // !defined(_CHICAGO_)
    BOOL    _Browse(TCHAR szFilePath[]);

    void    _ErrorDialog(int idsErr, LONG error = 0, UINT idsHelpHint = 0)
                 { SchedUIErrorDialog(Hwnd(), idsErr, error, idsHelpHint); }

#if !defined(_CHICAGO_)
    AccountInfo     m_AccountInfo;          // Account/password change info.
#endif // !defined(_CHICAGO_)

    ITask         * m_pIJob;

    // Flag values.  Note the compiler allocates a single dword for these

    DWORD           m_CommentDirty : 1;     //
    DWORD           m_AppNameDirty : 1;     //
    DWORD           m_WorkingDirDirty : 1;  // A value of 1 => dirty
    DWORD           m_RunAsDirty : 1;       //
    DWORD           m_updateIcon : 1;       // 1 => update needed
    DWORD           m_ChangeFromSetText : 1;// 0 => en_change from user

    //
    //  icon helper
    //

    CIconHelper   * m_pIconHelper;

    //
    //  Should we save on Apply or OK.
    //

    BOOL            m_fPersistChanges;

    BOOL            m_fEnableJob;

    //
    // Saved or new sage settings parameter
    //

    TCHAR           m_tszSageRunParam[20]; // /SAGERUN:4294967295

#if !defined(_CHICAGO_)
    //
    // The RunAs edit control can be updated by the user, or automatically
    // with apply via the set account information dialog. In response to
    // user edits, we want to apply the changes. For the automatic RunAs
    // update, we've already applied the changes, therefore, this flag
    // exists to not re-dirty the page.
    //

    BOOL            m_fApplyRunAsEditChange;

    //
    //  The other pages need to query the general page if there is an
    //  application or security account change for the common security
    //  code in the page's save path (JFSaveJob). The record of this
    //  dirty information must be retained post-Apply; therefore, these
    //  slightly redundant flags are necessary.
    //

    BOOL            m_fTaskApplicationChange;
    BOOL            m_fTaskAccountChange;

    //
    //  Set this flag to TRUE if we have launched the set password dialog.
    //  Othwerwise, the user may get a redundant set account information
    //  dialog on apply.
    //

    BOOL            m_fSuppressAcctInfoRequestOnApply;
#endif // !defined(_CHICAGO_)

}; // class CGeneralPage


inline
CGeneralPage::CGeneralPage(
    ITask * pIJob,
    LPTSTR  ptszTaskPath,
    BOOL    fPersistChanges)
        :
        m_pIJob(pIJob),
        m_pIconHelper(NULL),
        m_fPersistChanges(fPersistChanges),
        m_ChangeFromSetText(FALSE),
#if !defined(_CHICAGO_)
        m_fApplyRunAsEditChange(TRUE),
        m_fTaskApplicationChange(FALSE),
        m_fTaskAccountChange(FALSE),
        m_fSuppressAcctInfoRequestOnApply(FALSE),
#endif // !defined(_CHICAGO_)
        CPropPage(MAKEINTRESOURCE(general_page), ptszTaskPath)
{
    TRACE(CGeneralPage, CGeneralPage);

    Win4Assert(m_pIJob != NULL);

    m_pIJob->AddRef();
    m_tszSageRunParam[0] = TEXT('\0');

#if !defined(_CHICAGO_)
    InitializeAccountInfo(&m_AccountInfo);
#endif // !defined(_CHICAGO_)
}


inline
CGeneralPage::~CGeneralPage()
{
    TRACE(CGeneralPage, ~CGeneralPage);

    if (m_pIconHelper != NULL)
    {
        m_pIconHelper->Release();
    }

    if (m_pIJob != NULL)
    {
        m_pIJob->Release();
    }

#if !defined(_CHICAGO_)
    ResetAccountInfo(&m_AccountInfo);
#endif // !defined(_CHICAGO_)
}


//+--------------------------------------------------------------------------
//
//  Member:     CGeneralPage::_JobObjectIsLocal
//
//  Synopsis:   Return TRUE if job object being edited lives on the local
//              machine, FALSE otherwise.
//
//  History:    2-01-1997   DavidMun   Created
//
//---------------------------------------------------------------------------

inline BOOL
CGeneralPage::_JobObjectIsLocal()
{
    return IsLocalFilename(GetTaskPath());
}


LRESULT
CGeneralPage::_OnInitDialog(
    LPARAM lParam)
{
    TRACE(CGeneralPage, _OnInitDialog);

    HRESULT     hr              = S_OK;
    LPWSTR      pwsz            = NULL;
    BOOL        fEnableSecurity = FALSE;
    TCHAR       tszApp[MAX_PATH + 1] = TEXT("");

    //
    // Note that we don't limit the txt_app_name control.  App names are
    // limited to MAX_PATH characters, but app parameters are not.
    //
    Edit_LimitText(_hCtrl(txt_comments), MAX_PATH);
    Edit_LimitText(_hCtrl(txt_workingdir), MAX_PATH);
    Edit_LimitText(_hCtrl(txt_run_as), MAX_USERNAME);

    //
    // Policy - if this reg key is valid, disable browse
    //

    if (RegReadPolicyKey(TS_KEYPOLICY_DENY_BROWSE))
    {
        DEBUG_OUT((DEB_ITRACE, "Policy ALLOW_BROWSE active, no browse or edit\n"));
        EnableWindow(_hCtrl(btn_browse), FALSE);
        ShowWindow(_hCtrl(btn_browse), SW_HIDE);
        EnableWindow(_hCtrl(txt_app_name), FALSE);
        EnableWindow(_hCtrl(txt_workingdir), FALSE);
    }

    do
    {
#if !defined(_CHICAGO_)
        //
        // NB : For the first release of the scheduling agent, all security
        //      operations are disabled under Win95, even Win95 to NT.
        //

        //
        // Determine if the security settings should be shown or not.
        // The task object must reside within a tasks folder & exist
        // on an NT machine.
        //

        fEnableSecurity = (this->GetPlatformId() == VER_PLATFORM_WIN32_NT &&
                           this->IsTaskInTasksFolder());
#else
        fEnableSecurity = FALSE;
#endif // !defined(_CHICAGO_)

        if (!fEnableSecurity)
        {
            DestroyWindow(_hCtrl(lbl_run_as));
            DestroyWindow(_hCtrl(txt_run_as));
            DestroyWindow(_hCtrl(btn_passwd));
        }

        // See if the schedule page has already created the icon
        // helper, if not create it here.

        m_pIconHelper = (CIconHelper *)PropSheet_QuerySiblings(
                                GetParent(Hwnd()), GET_ICON_HELPER, 0);

        if (m_pIconHelper == NULL)
        {
            m_pIconHelper = new CIconHelper();

            if (m_pIconHelper == NULL)
            {
                hr = E_OUTOFMEMORY;
                break;
            }
        }
        else
        {
            m_pIconHelper->AddRef();
        }

        //
        // Set job name (static text showing path to .job object)
        //

        SetDlgItemText(Hwnd(), lbl_job_name, this->GetTaskPath());

        //
        // Set comments
        //

        hr = m_pIJob->GetComment(&pwsz);

        CHECK_HRESULT(hr);
        BREAK_ON_FAIL(hr);

        I_SetDlgItemText(Hwnd(), txt_comments, pwsz);
        CoTaskMemFree(pwsz);

        //
        // Get application name preparatory to setting it.
        //

        hr = m_pIJob->GetApplicationName(&pwsz);

        CHECK_HRESULT(hr);
        BREAK_ON_FAIL(hr);

#if defined(_CHICAGO_)
        //
        // Show the settings button only on Win95 and only if the task's
        // application is Sage-aware. Using the ApplicationName property in
        // pwsz, before it is freed below.
        //
        hr = UnicodeToAnsi(tszApp, pwsz, MAX_PATH+1);

        if (SUCCEEDED(hr) && !IsSageAware(tszApp, NULL, NULL))
        {
            //
            // The app isn't Sage-aware, so hide the Settings button and
            // move the Browse button to the Settings button position.
            //
            HWND hBrowseBtn = GetDlgItem(Hwnd(), btn_browse);
            HWND hSettingsBtn = GetDlgItem(Hwnd(), btn_settings);

            WINDOWPLACEMENT wp;
            wp.length = sizeof(WINDOWPLACEMENT);

            GetWindowPlacement(hSettingsBtn, &wp);

            ShowWindow(hSettingsBtn, SW_HIDE);

            MoveWindow(hBrowseBtn,
                       wp.rcNormalPosition.left,
                       wp.rcNormalPosition.top,
                       wp.rcNormalPosition.right - wp.rcNormalPosition.left,
                       wp.rcNormalPosition.bottom - wp.rcNormalPosition.top,
                       FALSE);
        }
#else
        lstrcpyn(tszApp, pwsz, MAX_PATH + 1);
#endif // defined(_CHICAGO_)

        //
        // If the application name fetched from the job object has spaces in
        // it, add quotes around the tchar version.
        //

        if (wcschr(pwsz, L' '))
        {
            AddQuotes(tszApp, MAX_PATH + 1);
        }
        CoTaskMemFree(pwsz);

        //
        // Now get the parameters and append a space and the tchar
        // representation to the app name.
        //

        hr = m_pIJob->GetParameters(&pwsz);
        CHECK_HRESULT(hr);
        BREAK_ON_FAIL(hr);


#if !defined(UNICODE)
        TCHAR tszArg[MAX_PATH + 1];
        UnicodeToAnsi(tszArg, pwsz, MAX_PATH + 1);
#else
        TCHAR * tszArg = pwsz;
#endif // !defined(UNICODE)

        //
        // If the /SAGERUN:n parameter appears in the arguments, save it in
        // an internal buffer, but don't show it in the UI.
        //

        LPTSTR ptszSageRun = _tcsstr(tszArg, SAGERUN_PARAM);

        if (ptszSageRun)
        {
            lstrcpyn(m_tszSageRunParam,
                     ptszSageRun,
                     ARRAYLEN(m_tszSageRunParam));

            *ptszSageRun = TEXT('\0');
            StripLeadTrailSpace(tszArg); // get rid of spaces before /sagerun
        }

        _SetAppEdit(tszApp, tszArg);

        CoTaskMemFree(pwsz);

        //
        // Set the working directory
        //

        hr = m_pIJob->GetWorkingDirectory(&pwsz);
        CHECK_HRESULT(hr);
        BREAK_ON_FAIL(hr);

        if (wcschr(pwsz, L' '))
        {
            WCHAR wszWorkingDir[MAX_PATH + 1] = L"\"";

            wcsncpy(wszWorkingDir + 1, pwsz, MAX_PATH - 1);
            wcscat(wszWorkingDir, L"\"");
            I_SetDlgItemText(Hwnd(), txt_workingdir, wszWorkingDir);
        }
        else
        {
            I_SetDlgItemText(Hwnd(), txt_workingdir, pwsz);
        }
        CoTaskMemFree(pwsz);

        //
        //  Set the job enabled check box
        //

        DWORD dwFlags;

        hr = m_pIJob->GetFlags(&dwFlags);

        m_fEnableJob = (dwFlags & TASK_FLAG_DISABLED) ? FALSE : TRUE;

        CheckDlgButton(m_hPage, chk_enable_job, m_fEnableJob);

        if (!this->IsTaskInTasksFolder())
        {
            EnableWindow(_hCtrl(chk_enable_job), FALSE);
            m_fEnableJob = FALSE;
        }

#if !defined(_CHICAGO_)
        if (fEnableSecurity)
        {
            //
            // Set account name (run as)
            //

            hr = m_pIJob->GetAccountInformation(&pwsz);

            if (SUCCEEDED(hr))
            {
                I_SetDlgItemText(Hwnd(), txt_run_as, pwsz);
                m_AccountInfo.pwszAccountName = pwsz;
                EnableWindow(_hCtrl(btn_passwd), TRUE);
            }
            else if (hr == SCHED_E_ACCOUNT_INFORMATION_NOT_SET)
            {
                hr = S_OK;

                //
                // Set the run as field to the current user's name and
                // enable the set/change password button.
                // Leave it blank for AT jobs - else they look like they've
                // been set to run as this user, which is very confusing
                // (see bug 376163).  BUGBUG  Better behavior would be to
                // display a status message that this is an AT job and will
                // be upgraded if modified.
                //

                TCHAR tszUserName[MAX_USERNAME + 1] = TEXT("");

                if (! (dwFlags & JOB_I_FLAG_NET_SCHEDULE))
                {
                    GetDefaultDomainAndUserName(tszUserName, MAX_USERNAME + 1);
                }
                SetDlgItemText(Hwnd(), txt_run_as, tszUserName);
                EnableWindow(_hCtrl(btn_passwd), TRUE);
            }
            else
            {
                //
                // Disable the run as and set/change password button ctrls &
                // put up an error dialog letting the user know that security
                // services are unavailable.
                //
                // Actually, the set/change password button is already
                // disabled.
                //

                CHECK_HRESULT(hr);
                EnableWindow(_hCtrl(lbl_run_as), FALSE);
                EnableWindow(_hCtrl(txt_run_as), FALSE);
            }
        }
#endif // !defined(_CHICAGO_)

    } while (0);

    if (FAILED(hr))
    {
        if (hr == E_OUTOFMEMORY)
        {
            _ErrorDialog(IERR_OUT_OF_MEMORY);
        }
        else
        {
            _ErrorDialog(IERR_GENERAL_PAGE_INIT, hr);
        }

		//
		// Show application icon
		//
        m_updateIcon = 1;
        _UpdateAppIcon(tszApp);

        EnableWindow(Hwnd(), FALSE);

        return FALSE;
    }

    //
    // Show application icon
    //

    m_updateIcon = 1;
    _UpdateAppIcon(tszApp);


    //
    // Need to initialize these here since doing a SetDlgItemText causes
    // a WM_COMMAND msg with EN_CHANGE to be called for edit boxes.
    //

    m_CommentDirty = 0;
    m_AppNameDirty = 0;
    m_WorkingDirDirty = 0;
    m_RunAsDirty = 0;

    m_fDirty = FALSE;

    return TRUE;
}




#if !defined(_CHICAGO_)

//+--------------------------------------------------------------------------
//
//  Function:   GetDefaultDomainAndUserName
//
//  Synopsis:   Fill [ptszDomainAndUserName] with "domain\user" string
//
//  Arguments:  [ptszDomainAndUserName] - buffer to receive string
//              [cchBuf]                - should be at least MAX_USERNAME
//
//  Modifies:   *[ptszDomainAndUserName].
//
//  History:    06-03-1997   DavidMun   Created
//
//  Notes:      If an error occurs while retrieving the domain name, only
//              the user name is returned.  If even that cannot be
//              retrieved, the buffer is set to an empty string.
//
//---------------------------------------------------------------------------

VOID
GetDefaultDomainAndUserName(
    LPTSTR ptszDomainAndUserName,
    ULONG  cchBuf)
{
    TRACE_FUNCTION(GetDefaultDomainAndUserName);

    if (!GetUserNameEx(NameSamCompatible,
                       ptszDomainAndUserName,
                       &cchBuf))
    {
        DEBUG_OUT((DEB_ERROR,
                   "GetDefaultDomainAndUserName: GetUserNameEx failed %d\n",
                   GetLastError()));

        ptszDomainAndUserName[0] = L'\0';
    }
}


#endif // !defined(_CHICAGO_)




void
CGeneralPage::_UpdateAppIcon(
    LPCTSTR ptszAppName)
{
    if (m_updateIcon == 0)
    {
        return;
    }

    m_updateIcon = 0;

    if (ptszAppName)
    {
        m_pIconHelper->SetAppIcon((LPTSTR)ptszAppName);
    }

    BOOL fEnabled;

    if (this->IsTaskInTasksFolder())
    {
        fEnabled = (IsDlgButtonChecked(m_hPage, chk_enable_job)
                    == BST_CHECKED);
    }
    else
    {
        fEnabled = FALSE;
    }

    m_pIconHelper->SetJobIcon(fEnabled);

    SendDlgItemMessage(Hwnd(), idc_icon, STM_SETICON,
                    (WPARAM)m_pIconHelper->hiconJob, 0L);
}


BOOL
CGeneralPage::_Browse(
    TCHAR szFilePath[]) // of size MAX_PATH
{
    TCHAR szDefExt[5];
    TCHAR szBrowserDir[MAX_PATH];
    TCHAR szFilters[MAX_PATH];
    TCHAR szTitle[100];

    DWORD dwFlags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR;

    LoadString(g_hInstance, IDS_EXE, szDefExt, ARRAYLEN(szDefExt));
    LoadString(g_hInstance, IDS_BROWSE, szTitle, ARRAYLEN(szTitle));

    ZeroMemory(szFilters, ARRAYLEN(szFilters));
    if (!LoadString(g_hInstance,
                    IDS_PROGRAMSFILTER,
                    szFilters,
                    ARRAYLEN(szFilters)))
    {
		CHECK_HRESULT( HRESULT_FROM_WIN32(GetLastError()) );
        return FALSE;
    }

    GetCurrentDirectory(MAX_PATH, szBrowserDir);

    OPENFILENAME ofn;
    ZeroMemory(&ofn, sizeof(ofn));

    szFilePath[0] = TEXT('\0');

    // Setup info for comm dialog.
    ofn.lStructSize       = CDSIZEOF_STRUCT(OPENFILENAME, lpTemplateName);
    ofn.hwndOwner         = Hwnd();
    ofn.hInstance         = g_hInstance;
    ofn.lpstrFilter       = szFilters;
    ofn.nFilterIndex      = 1;
    ofn.lpstrFile         = szFilePath;
    ofn.nMaxFile          = MAX_PATH;
    ofn.lpstrInitialDir   = szBrowserDir;
    ofn.lpstrTitle        = szTitle;
    ofn.Flags             = dwFlags;
    ofn.lpstrDefExt       = szDefExt;

    // Call it.
    if (GetOpenFileName(&ofn) == FALSE)
    {
        DEBUG_OUT((DEB_ERROR, "GetOpenFileName failed<0x%x>\n",
                                            CommDlgExtendedError()));

        return FALSE;
    }
    return TRUE;
}


LRESULT
CGeneralPage::_OnCommand(
    int     id,
    HWND    hwndCtl,
    UINT    codeNotify)
{
    TRACE(CGeneralPage, _OnCommand);

    if ((id == btn_browse) && (codeNotify == BN_CLICKED))
    {
        // popup the file browse dialog

        TCHAR tszFilePath[MAX_PATH+1];

        if (_Browse(tszFilePath))
        {
            if (_tcschr(tszFilePath, TEXT(' ')))
            {
                AddQuotes(tszFilePath, MAX_PATH);
            }
            SetDlgItemText(Hwnd(), txt_app_name, tszFilePath);
            SendMessage(Hwnd(),
                        WM_NEXTDLGCTL,
                        (WPARAM)_hCtrl(txt_workingdir),
                        TRUE);
            _UpdateAppIcon(tszFilePath);
            _EnableApplyButton();
        }
    }
#if defined(_CHICAGO_)
    else if (id == btn_settings && codeNotify == BN_CLICKED)
    {
        TCHAR tszApp[MAX_PATH + 1];

        _GetAppAndArgs(tszApp, NULL);

        // invoke the application to do its settings.

        BOOL fHadSageRun = (*m_tszSageRunParam != '\0');

        DoSageSettings(tszApp, m_tszSageRunParam);

        //
        // If the DoSageSettings func created a /sagerun parameter,
        // mark the app name as dirty, since the sagerun parameter doesn't
        // appear in the UI, therefore we won't be doing a settext with
        // it and automatically dirtying.
        //

        if (!fHadSageRun && *m_tszSageRunParam)
        {
            m_AppNameDirty = 1;
            _EnableApplyButton();
        }
    }
#endif // defined(_CHICAGO_)
#if !defined(_CHICAGO_)
    else if (id == btn_passwd && codeNotify == BN_CLICKED)
    {
        // popup the set password dialog
        LaunchSetPasswordDlg(Hwnd(), &m_AccountInfo);

        //
        // Since we've launched the set password dialog, we want
        // to suppress the set account information dialog which
        // otherwise may come up on apply.
        //

        m_fSuppressAcctInfoRequestOnApply = TRUE;

        _EnableApplyButton();
    }
#endif // !defined(_CHICAGO_)
    else if (codeNotify == EN_CHANGE)
    {
        switch (id)
        {
        case txt_comments:
            m_CommentDirty = 1;
            break;

        case txt_app_name:
            //
            // If we just did a SetDlgItemText, then this change notification
            // should be ignored.
            //
            if (m_ChangeFromSetText)
            {
                m_ChangeFromSetText = 0;
            }
            else
            {
                m_AppNameDirty = 1;
                m_updateIcon = 1;
            }
            break;

        case txt_workingdir:
            m_WorkingDirDirty = 1;
            break;

        case txt_run_as:
#if !defined(_CHICAGO_)
                //
                // If m_fApplyRunAsEditChange is TRUE, the user has edited
                // the RunAs control, therefore, we want to apply changes.
                // If FALSE, the RunAs control has been automatically updated
                // by the UI as a result of an apply. In this case, we do
                // not want to apply the change, as it already has been.
                //

                if (m_fApplyRunAsEditChange)
                {
                    m_RunAsDirty = 1;
                    EnableWindow(_hCtrl(btn_passwd), TRUE);
                    break;
                }
                else
                {
                    Win4Assert(!m_fDirty);
                    return TRUE;
                }
#endif // !defined(_CHICAGO_)

        default:
            return FALSE;
        }

        _EnableApplyButton();
    }
    else if (codeNotify == EN_KILLFOCUS &&
             id == txt_app_name)
    {
        if (m_AppNameDirty == 1)
        {
            TCHAR tszApp[MAX_PATH + 1];

            _GetAppAndArgs(tszApp, NULL);
            if (_JobObjectIsLocal())
            {
                _ExpandAppName(tszApp);
            }
            _UpdateAppIcon(tszApp);
        }
    }
    else if (id == chk_enable_job)
    {
        _EnableApplyButton();
        m_updateIcon = 1;
        _UpdateAppIcon(NULL);
    }

    return TRUE;
}



LRESULT
CGeneralPage::_OnApply(void)
{
    TRACE(CGeneralPage, _OnApply);
    //DbxDisplay("CGeneralPage::_OnApply");

    if (m_fDirty == FALSE)
    {
        return TRUE;
    }

    HRESULT     hr = S_OK;
    WCHAR       wcBuff[MAX_PATH+1];
    LPWSTR      pwszArg = NULL;

    do
    {
        if (m_CommentDirty == 1)
        {
            wcBuff[0] = L'\0';

            I_GetDlgItemText(Hwnd(), txt_comments, wcBuff, MAX_PATH+1);

            hr = m_pIJob->SetComment(wcBuff);

            CHECK_HRESULT(hr);
            BREAK_ON_FAIL(hr);

            m_CommentDirty = 0;
        }

#if !defined(_CHICAGO_)
        m_fTaskApplicationChange = FALSE;
#endif // !defined(_CHICAGO_)

        if (m_AppNameDirty == 1)
        {
            WCHAR wszApp[MAX_PATH + 1];

            _UpdateAppNameAndIcon(wszApp, &pwszArg);
            if (pwszArg == NULL)
            {
                break;
            }

            hr = m_pIJob->SetApplicationName(wszApp);
            CHECK_HRESULT(hr);
            BREAK_ON_FAIL(hr);

            //
            // Since the application name has changed, it may no longer name
            // (or may now name) a sage-aware application.  Make sure that
            // the sagerun argument is appended only if it should be.
            //
            // BUGBUG IsSageAware and CreateSageRunKey use HKLM, so they're
            // broken for remote case.  If this is fixed, enable this code
            // on NT if the target machine is win9x.
            //
            // BUGBUG pwszArg is limited to MAX_PATH characters on Win9x.
            //

#if defined(_CHICAGO_)
            int iSageRun;

            if (IsSageAwareW(wszApp, pwszArg, &iSageRun))
            {
                wsprintf(m_tszSageRunParam,
                         TEXT("%s%u"),
                         SAGERUN_PARAM,
                         iSageRun);

                CHAR szApp[MAX_PATH + 1];

                UnicodeToAnsi(szApp, wszApp, ARRAYLEN(szApp));

                LPSTR pszFileName = PathFindFileName(szApp);

                if (pszFileName)
                {
                    CreateSageRunKey(pszFileName, iSageRun);
                }
            }
            else
            {
                *m_tszSageRunParam = TEXT('\0');
            }

            WCHAR wszArg[MAX_PATH + 1];

            ULONG cchArg = wcslen(pwszArg);

            if (*m_tszSageRunParam &&
                lstrlen(m_tszSageRunParam) + cchArg + 1 < ARRAYLEN(wszArg))
            {
                wcscpy(wszArg, pwszArg);
                if (cchArg)
                {
                    wszArg[cchArg++] = L' ';
                }
                AnsiToUnicode(&wszArg[cchArg],
                              m_tszSageRunParam,
                              ARRAYLEN(wszArg) - cchArg);
                hr = m_pIJob->SetParameters(wszArg);
            }
            else
            {
                hr = m_pIJob->SetParameters(pwszArg);
            }
#else
            hr = m_pIJob->SetParameters(pwszArg);
#endif // defined(_CHICAGO_)

            CHECK_HRESULT(hr);
            BREAK_ON_FAIL(hr);

            m_AppNameDirty           = 0;
#if !defined(_CHICAGO_)
            m_fTaskApplicationChange = TRUE;
#endif // !defined(_CHICAGO_)
        }

        if (m_WorkingDirDirty == 1)
        {
            wcBuff[0] = L'\0';

            _ExpandWorkingDir();
            I_GetDlgItemText(Hwnd(), txt_workingdir, wcBuff, MAX_PATH+1);

            hr = m_pIJob->SetWorkingDirectory(wcBuff);

            CHECK_HRESULT(hr);
            BREAK_ON_FAIL(hr);

            m_WorkingDirDirty = 0;
        }

        //
        // For the first release of the scheduling agent, all security
        // operations are disabled under Win95, even Win95 to NT.
        //

#if !defined(_CHICAGO_)
        m_fTaskAccountChange = FALSE;
        if (m_RunAsDirty == 1 || m_AccountInfo.pwszPassword != tszEmpty)
        {
            wcBuff[0] = L'\0';
            DWORD ccAccountName = I_GetDlgItemText(Hwnd(),
                                                   txt_run_as,
                                                   wcBuff,
                                                   MAX_USERNAME + 1);

            if ((m_RunAsDirty == 1 && m_AccountInfo.pwszAccountName == NULL) ||
                (m_RunAsDirty == 1 && m_AccountInfo.pwszAccountName != NULL &&
                 lstrcmpiW(m_AccountInfo.pwszAccountName, wcBuff) != 0))
            {
                //
                // The user has changed the account name. Check if they have
                // specified a password.
                //

                if (m_AccountInfo.pwszPassword == tszEmpty)
                {
                    //
                    // User hasn't specified a password. Launch the set
                    // password dialog.
                    //

                    LaunchSetPasswordDlg(Hwnd(), &m_AccountInfo);

                    //
                    // Since we've launched the set password dialog, we want
                    // to suppress the set account information dialog which
                    // otherwise may come up on apply.
                    //

                    m_fSuppressAcctInfoRequestOnApply = TRUE;
                }
            }

            if (m_AccountInfo.pwszPassword != tszEmpty)
            {
                hr = m_pIJob->SetAccountInformation(
                                            wcBuff,
                                            m_AccountInfo.pwszPassword);

                if (FAILED(hr))
                {
                    CHECK_HRESULT(hr);
                    break;
                }

                m_RunAsDirty         = 0;
                m_AccountInfo.fDirty = FALSE;
                m_fTaskAccountChange = TRUE;
            }
        }
#endif // !defined(_CHICAGO_)

        //
        //  Save the job enabled state
        //

        if (this->IsTaskInTasksFolder())
        {
            DWORD dwFlags;

            hr = m_pIJob->GetFlags(&dwFlags);

            BOOL fTemp = (IsDlgButtonChecked(m_hPage, chk_enable_job)
                                                        == BST_CHECKED);

            if (m_fEnableJob != fTemp)
            {
                m_fEnableJob = fTemp;

                if (m_fEnableJob == TRUE)
                {
                    dwFlags &= ~TASK_FLAG_DISABLED;
                }
                else
                {
                    dwFlags |= TASK_FLAG_DISABLED;
                }

                hr = m_pIJob->SetFlags(dwFlags);

                CHECK_HRESULT(hr);
                BREAK_ON_FAIL(hr);
            }
        }

        //
        // reset dirty flag
        //

        m_fDirty = FALSE;

        //
        //  If evrything went well see if the other pages are ready to
        //  save the job to storage.
        //

        if ((m_fPersistChanges == TRUE)  &&
            (PropSheet_QuerySiblings(GetParent(Hwnd()),
                                        QUERY_READY_TO_BE_SAVED, 0))
            == 0)
        {
            //
            // Save the job file to storage.
            //
            // For the first release of the scheduling agent, all security
            // operations are disabled under Win95, even Win95 to NT.
            //

            hr = JFSaveJob(Hwnd(),
                           m_pIJob,
#if !defined(_CHICAGO_)
                           this->GetPlatformId() == VER_PLATFORM_WIN32_NT &&
                            this->IsTaskInTasksFolder(),
                           m_fTaskAccountChange,
                           m_fTaskApplicationChange,
                           m_fSuppressAcctInfoRequestOnApply);
#else
                           FALSE,
                           FALSE,
                           FALSE,
                           TRUE);
#endif // !defined(_CHICAGO_)

            CHECK_HRESULT(hr);
            BREAK_ON_FAIL(hr);

#if !defined(_CHICAGO_)
            m_fTaskApplicationChange          = FALSE;
            m_fTaskAccountChange              = FALSE;
            m_fSuppressAcctInfoRequestOnApply = FALSE;

            // Will result in refresh of the run as edit control.
            //
            _UpdateRunAsControl();
#endif // !defined(_CHICAGO_)
        }

    } while (0);

    delete [] pwszArg;

    if (FAILED(hr))
    {
        if (hr == E_OUTOFMEMORY)
        {
            _ErrorDialog(IERR_OUT_OF_MEMORY);
        }
        else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
        {
            _ErrorDialog(IERR_FILE_NOT_FOUND);
        }
        else if (hr == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED))
        {
            _ErrorDialog(IERR_ACCESS_DENIED);
        }
        else
        {
            _ErrorDialog(IERR_INTERNAL_ERROR, hr);
        }
    }

    return SUCCEEDED(hr);
}




//+--------------------------------------------------------------------------
//
//  Member:     CGeneralPage::_UpdateAppNameAndIcon
//
//  Synopsis:   Update the application name edit control with an expanded
//              path for the app, then update the icon for the new path.
//
//  Arguments:  [wszApp]  - NULL or buffer to receive WCHAR app name
//              [ppwszArgs] - NULL or address of pointer to buffer to receive
//                  WCHAR arguments.  Must be deallocated with delete[].
//
//  Modifies:   *[wszApp], *[ppwszArgs]
//
//  History:    1-31-1997   DavidMun   Created
//
//  Notes:      App path is only expanded if job object is local.
//
//---------------------------------------------------------------------------

VOID
CGeneralPage::_UpdateAppNameAndIcon(
    LPWSTR wszApp,
    LPWSTR * ppwszArgs)
{
    TCHAR tszApp[MAX_PATH + 1];
    LPTSTR ptszArgs = NULL;
    TCHAR tszAppAndArg[MAX_PATH + 1];

    _GetAppAndArgs(tszApp, &ptszArgs);
    if (ptszArgs == NULL)
    {
        return;
    }

    if (_JobObjectIsLocal())
    {
        _ExpandAppName(tszApp);

        m_updateIcon = 1;
        _UpdateAppIcon(tszApp);

        //
        // If the working directory is empty and the app is on the local
        // machine, provide a default of the directory where the app lives.
        //

        TCHAR tszWorkingDir[MAX_PATH + 1];
        GetDlgItemText(Hwnd(), txt_workingdir, tszWorkingDir, MAX_PATH + 1);
        StripLeadTrailSpace(tszWorkingDir);
        DeleteQuotes(tszWorkingDir);

        if (!*tszWorkingDir && IsLocalFilename(tszApp))
        {
            lstrcpy(tszWorkingDir, tszApp);
            DeleteQuotes(tszWorkingDir);

            // Get rid of the filename at the end of the string

            PathRemoveFileSpec(tszWorkingDir);

            if (HasSpaces(tszWorkingDir))
            {
                AddQuotes(tszWorkingDir, MAX_PATH + 1);
            }
            SetDlgItemText(Hwnd(), txt_workingdir, tszWorkingDir);
        }

        m_ChangeFromSetText = 1;
        _SetAppEdit(tszApp, ptszArgs);
    }

    if (wszApp)
    {
#if !defined(UNICODE)
        AnsiToUnicode(wszApp, tszApp, MAX_PATH + 1);
#else
        lstrcpy(wszApp, tszApp);
#endif // defined(UNICODE)
    }

    if (ppwszArgs)
    {
#if !defined(UNICODE)
        LONG cch = lstrlen(ptszArgs) + 1;
        *ppwszArgs = new WCHAR[cch];
        if (*ppwszArgs != NULL)
        {
            AnsiToUnicode(*ppwszArgs, ptszArgs, cch);
        }
        delete [] ptszArgs;
#else
        *ppwszArgs = ptszArgs;
#endif // defined(UNICODE)
    }
    else
    {
        delete [] ptszArgs;
    }
}




//+--------------------------------------------------------------------------
//
//  Member:     CGeneralPage::_ExpandWorkingDir
//
//  Synopsis:   Replace the string in the working directory edit control
//              with one which has expanded environment strings.
//
//  History:    06-04-1997   DavidMun   Created
//
//  Notes:      Note that this will result in the working dir being marked
//              as dirty.
//
//---------------------------------------------------------------------------

VOID
CGeneralPage::_ExpandWorkingDir()
{
    TCHAR tszApp[MAX_PATH + 1];
    TCHAR tszAppAndArg[MAX_PATH + 1];

    //
    // If editing a remote job, don't touch the working dir, since we
    // can't expand using its environment variables.
    //

    if (!_JobObjectIsLocal())
    {
        return;
    }

    TCHAR tszWorkingDir[MAX_PATH + 1];

    GetDlgItemText(Hwnd(), txt_workingdir, tszWorkingDir, MAX_PATH + 1);
    StripLeadTrailSpace(tszWorkingDir);
    DeleteQuotes(tszWorkingDir);

    TCHAR tszExpandedWorkingDir[MAX_PATH + 1];

    ULONG cchWritten;

    cchWritten = ExpandEnvironmentStrings(tszWorkingDir,
                                          tszExpandedWorkingDir,
                                          ARRAYLEN(tszExpandedWorkingDir));

    //
    // If the call succeeded, write the update string to the working dir
    // edit control.  If there were no environment variables to replace,
    // the string will be unchanged.
    //
    // Note that writing to the edit control will cause an EN_CHANGE which
    // will result in m_WorkingDirDirty being set.
    //

    if (cchWritten && cchWritten <= ARRAYLEN(tszExpandedWorkingDir))
    {
        SetDlgItemText(Hwnd(), txt_workingdir, tszExpandedWorkingDir);
    }
}




//+--------------------------------------------------------------------------
//
//  Member:     CGeneralPage::_GetAppAndArgs
//
//  Synopsis:   Fetch the string in the application name edit control and
//              split it into the app name part and the arguments part.
//
//  Arguments:  [tszApp]  - NULL or buffer for app name
//              [pptszArgs] - NULL or address of pointer to buffer for args,
//                  must be deallocated with delete[]
//
//  Modifies:   *[tszApp], *[pptszArgs]
//
//  History:    1-31-1997   DavidMun   Created
//
//  Notes:      Application name is space delimited and must be surrounded
//              by double quotes if it contains spaces.  For example:
//
//              text in edit ctrl        'filename' 'arguments'
//              ---------------------    -------------------------
//              notepad.exe foo.txt   => 'notepad.exe' 'foo.txt'
//              "notepad.exe"foo.txt  => '"notepad.exe"foo.txt' ''
//              "notepad.exe" foo.txt => '"notepad.exe"' 'foo.txt'
//              "no  pad.exe" foo.txt => '"no  pad.exe"' 'foo.txt'
//              no  pad.exe foo.txt   => 'no' '  pad.exe foo.txt'
//
//---------------------------------------------------------------------------

VOID
CGeneralPage::_GetAppAndArgs(
    LPTSTR tszApp,
    LPTSTR * pptszArgs)
{
    //
    // Split application name into executable name & parameters.  First
    // strip lead/trail spaces and null terminate at the first whitespace
    // outside a quote.
    //

    int cch = SchedGetDlgItemTextLength(Hwnd(), txt_app_name) + 1;
    if (cch <= 1)
    {
        DEBUG_OUT((DEB_ERROR,
                   "SchedGetDlgItemTextLength failed %d\n",
                   GetLastError()));
    }

    TCHAR *ptszBoth = new TCHAR[cch];
    if (ptszBoth == NULL)
    {
        DEBUG_OUT((DEB_ERROR,
                   "_GetAppAndArgs alloc failed %d\n",
                   GetLastError()));
        return;
    }

    GetDlgItemText(Hwnd(), txt_app_name, ptszBoth, cch);
    StripLeadTrailSpace(ptszBoth);

    LPTSTR  ptsz;
    BOOL    fInQuote = FALSE;

    for (ptsz = ptszBoth; *ptsz; ptsz = NextChar(ptsz))
    {
        if (*ptsz == TEXT('"'))
        {
            fInQuote = !fInQuote;
        }
        else if (*ptsz == TEXT(' '))
        {
            if (!fInQuote)
            {
                *ptsz++ = L'\0';
                break;
            }
        }
    }

    //
    // Return app name if caller wants it.
    //

    if (tszApp)
    {
        lstrcpy(tszApp, ptszBoth);
    }

    //
    // Now ptsz points to the first char past the application
    // name.  If there are no arguments, then ptsz is pointing
    // to the end of the string.  Otherwise it's pointing to the
    // start of the argument string.  Return this if caller wants it.
    //

    if (pptszArgs)
    {
        *pptszArgs = new TCHAR[cch - (ptsz - ptszBoth)];
        if (*pptszArgs == NULL)
        {
            DEBUG_OUT((DEB_ERROR,
                       "_GetAppAndArgs second alloc failed %d\n",
                       GetLastError()));
        }
        else
        {
            lstrcpy(*pptszArgs, ptsz);
        }
    }

    delete [] ptszBoth;
}




//+--------------------------------------------------------------------------
//
//  Member:     CGeneralPage::_ExpandAppName
//
//  Synopsis:   Change filename in [tszApp] to full path.
//
//  Arguments:  [tszApp] - filename to modify
//
//  Modifies:   *[tszApp]
//
//  History:    1-31-1997   DavidMun   Created
//
//  Notes:      CAUTION: Should be called only for job objects on the
//              LOCAL MACHINE.
//
//---------------------------------------------------------------------------

VOID
CGeneralPage::_ExpandAppName(
    LPTSTR tszApp)
{
    TCHAR tszWorkingDir[MAX_PATH + 1];
    BOOL fFound;

    GetDlgItemText(Hwnd(), txt_workingdir, tszWorkingDir, MAX_PATH + 1);
    fFound = ProcessApplicationName(tszApp, tszWorkingDir);

    if (_tcschr(tszApp, TEXT(' ')))
    {
        AddQuotes(tszApp, MAX_PATH);
    }
}




//+--------------------------------------------------------------------------
//
//  Member:     CGeneralPage::_SetAppEdit
//
//  Synopsis:   Set the application edit control to contain the concatenated
//              text in [tszApp] and [tszArgs].
//
//  Arguments:  [tszApp]  - application name
//              [tszArgs] - arguments
//
//  History:    1-31-1997   DavidMun   Created
//
//  Notes:      No modification (adding quotes, etc.) is done to [tszApp].
//              Caller should set m_ChangeFromSetText = 1 if calling from
//              outside of wm_initdialog processing.
//
//---------------------------------------------------------------------------

VOID
CGeneralPage::_SetAppEdit(LPCTSTR tszApp, LPCTSTR tszArgs)
{
    HWND hwndAppName = GetDlgItem(Hwnd(), txt_app_name);

    Edit_SetText(hwndAppName, tszApp);

    if (tszArgs && *tszArgs)
    {
        ULONG cchApp = lstrlen(tszApp);

        Edit_SetSel(hwndAppName, cchApp, cchApp);
        Edit_ReplaceSel(hwndAppName, TEXT(" "));
        Edit_SetSel(hwndAppName, cchApp + 1, cchApp + 1);
        Edit_ReplaceSel(hwndAppName, tszArgs);
    }
}




LRESULT CGeneralPage::_OnCancel(void)
{
    return 0;
}



LRESULT
CGeneralPage::_OnPSMQuerySibling(
    WPARAM  wParam,
    LPARAM  lParam)
{
    TRACE(CGeneralPage, _OnPSMQuerySibling);

    INT_PTR iRet = 0;

    switch (wParam)
    {
    case QUERY_READY_TO_BE_SAVED:
        iRet = (int)m_fDirty;
        break;

    case GET_ICON_HELPER:
        iRet = (INT_PTR)m_pIconHelper;
        break;

#if !defined(_CHICAGO_)
    case QUERY_TASK_APPLICATION_DIRTY_STATUS:
        *((BOOL *)lParam) = m_fTaskApplicationChange;
        iRet = 1;
        break;

    case QUERY_TASK_ACCOUNT_INFO_DIRTY_STATUS:
        *((BOOL *)lParam) = m_fTaskAccountChange;
        iRet = 1;
        break;

    case QUERY_SUPPRESS_ACCOUNT_INFO_REQUEST_FLAG:
        *((BOOL *)lParam) = m_fSuppressAcctInfoRequestOnApply;
        iRet = 1;
        break;

    case RESET_TASK_APPLICATION_DIRTY_STATUS:
        m_fTaskApplicationChange = FALSE;
        iRet = 1;
        break;

    case RESET_TASK_ACCOUNT_INFO_DIRTY_STATUS:
        m_fTaskAccountChange = FALSE;
        iRet = 1;
        break;

    case RESET_SUPPRESS_ACCOUNT_INFO_REQUEST_FLAG:
        m_fSuppressAcctInfoRequestOnApply = FALSE;
        iRet = 1;
        break;

    case TASK_ACCOUNT_CHANGE_NOTIFY:
        Win4Assert(!m_fDirty);
        _UpdateRunAsControl();
        iRet = 1;
        break;
#endif // !defined(_CHICAGO_)

    }

    SetWindowLongPtr(Hwnd(), DWLP_MSGRESULT, iRet);
    return iRet;
}



#if !defined(_CHICAGO_)
void
CGeneralPage::_UpdateRunAsControl(void)
{
    LPWSTR pwsz;

    if (SUCCEEDED(m_pIJob->GetAccountInformation(&pwsz)))
    {
        //
        // Instruct the EN_CHANGE processing of the RunAs edit control
        // to not apply this change.
        //

        m_fApplyRunAsEditChange = FALSE;
        I_SetDlgItemText(Hwnd(), txt_run_as, pwsz);
        m_fApplyRunAsEditChange = TRUE;
        CoTaskMemFree(pwsz);
    }
}
#endif // !defined(_CHICAGO_)


LRESULT
CGeneralPage::_OnPSNKillActive(
    LPARAM lParam)
{
    TRACE(CGeneralPage, _OnPSNKillActive);

    if (m_AppNameDirty)
    {
        _UpdateAppNameAndIcon(NULL, NULL);
    }

    if (m_WorkingDirDirty)
    {
        _ExpandWorkingDir();
    }
    return CPropPage::_OnPSNKillActive(lParam);
}



LRESULT
CGeneralPage::_OnHelp(
    HANDLE hRequesting,
    UINT uiHelpCommand)
{
    WinHelp((HWND)hRequesting,
            szMstaskHelp,
            uiHelpCommand,
            (DWORD_PTR)(LPSTR)s_aGeneralPageHelpIds);
    return TRUE;
}


HRESULT
GetGeneralPage(
    ITask           * pIJob,
    LPTSTR            ptszTaskPath,
    BOOL              fPersistChanges,
    HPROPSHEETPAGE  * phpage)
{
    TRACE_FUNCTION(GetGeneralPage);

    Win4Assert(pIJob != NULL);
    Win4Assert(phpage != NULL);

    HRESULT         hr      = S_OK;
    LPOLESTR        polestr = NULL;
    IPersistFile  * ppf     = NULL;
    LPTSTR          ptszPath = NULL;

    do
    {
        //
        // Get the job name.
        //

        if (ptszTaskPath != NULL)
        {
            //
            // use passed-in path
            //

            ptszPath = ptszTaskPath;
        }
        else
        {
            //
            // Obtain the job path from the interfaces.
            //

            hr = GetJobPath(pIJob, &ptszPath);
        }

        BREAK_ON_FAIL(hr);

        CGeneralPage * pPage = new CGeneralPage(
                                        pIJob,
                                        ptszPath,
                                        fPersistChanges);

        if (pPage == NULL)
        {
            hr = E_OUTOFMEMORY;
            CHECK_HRESULT(hr);
            break;
        }

        HPROPSHEETPAGE hpage = CreatePropertySheetPage(&pPage->m_psp);

        if (hpage == NULL)
        {
            delete pPage;
            hr = E_OUTOFMEMORY;
            CHECK_HRESULT(hr);
            break;
        }

        *phpage = hpage;

    } while (0);

    //
    // If we made a copy of pIJob's path string, free it.
    //

    if (ptszPath != ptszTaskPath)
    {
        delete [] ptszPath;
    }

    if (ppf != NULL)
    {
        ppf->Release();
    }

    return hr;
}



HRESULT
AddGeneralPage(
    PROPSHEETHEADER &psh,
    ITask          * pIJob)
{
    TRACE_FUNCTION(AddGeneralPage);

    HPROPSHEETPAGE hpage = NULL;

    HRESULT hr = GetGeneralPage(pIJob, NULL, TRUE, &hpage);

    if (SUCCEEDED(hr))
    {
        psh.phpage[psh.nPages++] = hpage;
    }

    return hr;
}



HRESULT
AddGeneralPage(
    LPFNADDPROPSHEETPAGE    lpfnAddPage,
    LPARAM                  cookie,
    ITask                 * pIJob)
{
    HPROPSHEETPAGE hpage = NULL;

    HRESULT hr = GetGeneralPage(pIJob, NULL, TRUE, &hpage);

    if (SUCCEEDED(hr))
    {
        if (!lpfnAddPage(hpage, cookie))
        {
            DestroyPropertySheetPage(hpage);

            hr = E_FAIL;
            CHECK_HRESULT(hr);
        }
    }

    return hr;
}


#if !defined(_CHICAGO_)
//+---------------------------------------------------------------------------
//
//  Function:   ResetAccountInfo
//
//  Synopsis:   Deallocate and initialize the account information specified.
//
//  Arguments:  [pAccountInfo] -- Account info structure.
//
//  Returns:    None.
//
//  Notes:      None.
//
//----------------------------------------------------------------------------
void
ResetAccountInfo(
    AccountInfo * pAccountInfo)
{
    //
    // Delete the account name.
    //

    if (pAccountInfo->pwszAccountName != NULL)
    {
        //
        // NB : pwszAccountName always allocated by CoTaskMemAlloc.
        //

        CoTaskMemFree(pAccountInfo->pwszAccountName);
        pAccountInfo->pwszAccountName = NULL;
    }

    //
    // Zero, delete the password. Except when the password is set to the
    // static empty string.
    //

    if (pAccountInfo->pwszPassword != NULL &&
        pAccountInfo->pwszPassword != tszEmpty)
    {
        ZERO_PASSWORD(pAccountInfo->pwszPassword);
        delete pAccountInfo->pwszPassword;
        pAccountInfo->pwszPassword = (LPWSTR) tszEmpty;
    }
}
#endif // !defined(_CHICAGO_)

#if !defined(_CHICAGO_)
//+---------------------------------------------------------------------------
//
//  Function:   InitializeAccountInfo
//
//  Synopsis:   Initializes account info fields.
//
//  Arguments:  [pAccountInfo]      -- Account info structure.
//
//  Returns:    None.
//
//  Notes:      None.
//
//----------------------------------------------------------------------------
void
InitializeAccountInfo(AccountInfo * pAccountInfo)
{
    pAccountInfo->pwszAccountName = NULL;

    //
    // If we haven't prompted for a password, pwszPassword points to the
    // global empty string variable.  This allows us to tell whether the
    // user intended the password to be an empty string, or we simply
    // haven't prompted for it.
    //

    pAccountInfo->pwszPassword    = (LPWSTR) tszEmpty;
}
#endif // !defined(_CHICAGO_)

//
// Helpers to launch Set/Change Password & Set Account Information dialogs.
//

#if !defined(_CHICAGO_)
//+---------------------------------------------------------------------------
//
//  Function:   LaunchSetPasswordDlg
//
//  Synopsis:   Helper to launch the dialog to modify the account password.
//
//  Arguments:  [hWnd]         -- Parent window handle.
//              [pAccountInfo] -- Structure manipulated by the dialog.
//
//  Returns:    DialogBoxParam return code.
//
//  Notes:      None.
//
//----------------------------------------------------------------------------
INT_PTR
LaunchSetPasswordDlg(HWND hWnd, AccountInfo * pAccountInfo)
{
    return(DialogBoxParam(g_hInstance,
                          MAKEINTRESOURCE(set_passwd_dlg),
                          hWnd,
                          SetPasswordDlgProc,
                          (LPARAM)pAccountInfo));
}
#endif // !defined(_CHICAGO_)

#if !defined(_CHICAGO_)
//+---------------------------------------------------------------------------
//
//  Function:   LaunchSetAccountInformationDlg
//
//  Synopsis:   Helper to launch the dialog to modify the account namd and
//              password.
//
//  Arguments:  [hWnd]         -- Parent window handle.
//              [pAccountInfo] -- Structure manipulated by the dialog.
//
//  Returns:    DialogBoxParam return code.
//
//  Notes:      None.
//
//----------------------------------------------------------------------------
INT_PTR
LaunchSetAccountInformationDlg(HWND hWnd, AccountInfo * pAccountInfo)
{
    return(DialogBoxParam(g_hInstance,
                          MAKEINTRESOURCE(set_account_info_dlg),
                          hWnd,
                          SetAccountInformationDlgProc,
                          (LPARAM)pAccountInfo));
}
#endif // !defined(_CHICAGO_)

//
// Set/Change Password & Set Account Information dialogs.
//

#if !defined(_CHICAGO_)
//+---------------------------------------------------------------------------
//
//  Function:   SetPasswordDlgProc
//
//  Synopsis:   This dialog allows the user specify an account password.
//              The password is confirmed by a redundant confirmation edit
//              field.
//
//  Arguments:  [hDlg]   -- Dialog handle.
//              [uMsg]   -- Message.
//              [wParam] -- Command.
//              [lParam] -- Account information dialog ptr on WM_INITDIALOG.
//
//  Returns:    TRUE  -- Message processed by this dialog.
//              FALSE -- Message not processed (WM_INITDIALOG excepted).
//
//  Notes:      None.
//
//----------------------------------------------------------------------------
INT_PTR APIENTRY
SetPasswordDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    static AccountInfo * pai                      = NULL;
    TCHAR  tszPassword[MAX_PASSWORD + 1]          = TEXT("");
    TCHAR  tszConfirmedPassword[MAX_PASSWORD + 1] = TEXT("");
    LPWSTR pwszPassword;

    switch (uMsg)
    {
    case WM_INITDIALOG:
        Win4Assert(lParam != NULL);
        pai = (AccountInfo *)lParam;
        Edit_LimitText(GetDlgItem(hDlg, edt_sp_passwd), MAX_PASSWORD);
        Edit_LimitText(GetDlgItem(hDlg, edt_sp_cfrmpasswd), MAX_PASSWORD);
        I_SetDlgItemText(hDlg, edt_sp_passwd, pai->pwszPassword);
        I_SetDlgItemText(hDlg, edt_sp_cfrmpasswd, pai->pwszPassword);

        //
        // Center the dialog.
        //

        CenterDialog(hDlg);
        return TRUE; // TRUE == let windows set focus

    case WM_COMMAND:
        switch(LOWORD(wParam))
        {
        case IDOK:
            DWORD ccPassword;

            ccPassword = GetDlgItemText(hDlg,
                                        edt_sp_passwd,
                                        tszPassword,
                                        MAX_PASSWORD + 1);

            GetDlgItemText(hDlg,
                           edt_sp_cfrmpasswd,
                           tszConfirmedPassword,
                           MAX_PASSWORD + 1);

            if (lstrcmp(tszPassword, tszConfirmedPassword) != 0)
            {
                //
                // Passwords didn't match. Let the user know so he/she
                // can correct it.
                //

                ZeroMemory(tszPassword, sizeof tszPassword);
                ZeroMemory(tszConfirmedPassword, sizeof tszConfirmedPassword);
                SchedUIErrorDialog(hDlg, IERR_PASSWORD, (LPTSTR)NULL);
                return(TRUE);
            }

            if (ccPassword)
            {
                //
                // Non-NULL password.
                //

                ccPassword++;
                pwszPassword = new WCHAR[ccPassword];

                if (pwszPassword != NULL)
                {
#if !defined(UNICODE)
                    HRESULT hr = AnsiToUnicode(pwszPassword,
                                               tszPassword,
                                               ccPassword);

                    if (FAILED(hr))
                    {
                        SchedUIErrorDialog(hDlg,
                                           IERR_INTERNAL_ERROR,
                                           0);
                        ZERO_PASSWORD(pwszPassword);
                        delete [] pwszPassword;
                        ZeroMemory(tszPassword, sizeof tszPassword);
                        ZeroMemory(tszConfirmedPassword,
                                   sizeof tszConfirmedPassword);
                        return(TRUE);
                    }
#else
                    lstrcpy(pwszPassword, tszPassword);
#endif // !defined(UNICODE)
                }
                else
                {
                    SchedUIErrorDialog(hDlg, IERR_OUT_OF_MEMORY,
                                       (LPTSTR)NULL);
                }
            }
            else
            {
                //
                // Clear the password.
                //

                pwszPassword = new WCHAR[1];
                if (pwszPassword != NULL)
                {
                    *pwszPassword = L'\0';
                }
                else
                {
                    SchedUIErrorDialog(hDlg, IERR_OUT_OF_MEMORY,
                                       (LPTSTR)NULL);
                    EndDialog(hDlg, wParam);
                    return(TRUE);
                }
            }

            //
            // Zero, delete the previous password. But don't delete the
            // static empty string.
            //

            if (pai->pwszPassword != NULL &&
                pai->pwszPassword != tszEmpty)
            {
                ZERO_PASSWORD(pai->pwszPassword);
                delete pai->pwszPassword;
            }
            pai->pwszPassword = pwszPassword;

        case IDCANCEL:
            ZeroMemory(tszPassword, sizeof tszPassword);
            ZeroMemory(tszConfirmedPassword, sizeof tszConfirmedPassword);
            EndDialog(hDlg, wParam);
            return TRUE;

        default:
            return FALSE;
        }

    case WM_HELP:
        WinHelp((HWND) ((LPHELPINFO) lParam)->hItemHandle,
                szMstaskHelp,
                HELP_WM_HELP,
                (DWORD_PTR)(LPSTR)s_aSetPasswordDlgHelpIds);
        return TRUE;

    case WM_CONTEXTMENU:
        WinHelp((HWND) wParam,
                szMstaskHelp,
                HELP_CONTEXTMENU,
                (DWORD_PTR)(LPSTR)s_aSetPasswordDlgHelpIds);
        return TRUE;

    default:
        return FALSE;
    }
}
#endif // !defined(_CHICAGO_)

#if !defined(_CHICAGO_)
//+---------------------------------------------------------------------------
//
//  Function:   SetAccountInformationDlgProc
//
//  Synopsis:   This dialog allows the user to specify full account
//              information: the account name and password. The password
//              is confirmed by a redundant confirmation edit field.
//              Logic also ensures the user must specify an account name.
//
//  Arguments:  [hDlg]   -- Dialog handle.
//              [uMsg]   -- Message.
//              [wParam] -- Command.
//              [lParam] -- Account information dialog ptr on WM_INITDIALOG.
//
//  Returns:    TRUE  -- Message processed by this dialog.
//              FALSE -- Message not processed (WM_INITDIALOG excepted).
//
//  Notes:      None.
//
//----------------------------------------------------------------------------
INT_PTR APIENTRY
SetAccountInformationDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam,
                             LPARAM lParam)
{
    static AccountInfo * pai                      = NULL;
    TCHAR  tszAccountName[MAX_USERNAME + 1]       = TEXT("");
    TCHAR  tszPassword[MAX_PASSWORD + 1]          = TEXT("");
    TCHAR  tszConfirmedPassword[MAX_PASSWORD + 1] = TEXT("");
    DWORD  ccAccountName                          = MAX_USERNAME + 1;
    LPWSTR pwszPassword;

    switch (uMsg)
    {
    case WM_INITDIALOG:
        Win4Assert(lParam != NULL);
        pai = (AccountInfo *)lParam;
        Edit_LimitText(GetDlgItem(hDlg, edt_sa_passwd), MAX_USERNAME);
        Edit_LimitText(GetDlgItem(hDlg, edt_sa_passwd), MAX_PASSWORD);
        Edit_LimitText(GetDlgItem(hDlg, edt_sa_cfrmpasswd), MAX_PASSWORD);
        if (pai->pwszAccountName != NULL)
        {
#if !defined(UNICODE)
            //
            // Even if conversion fails, tszAccountName will be terminated,
            // so put it in the UI regardless.
            //
            ZeroMemory(tszAccountName, ARRAYLEN(tszAccountName));
            (void) UnicodeToAnsi(tszAccountName, pai->pwszAccountName,
                            ccAccountName);
#else
            lstrcpy(tszAccountName, pai->pwszAccountName);
#endif // !defined(UNICODE)
        }
        else
        {
            GetDefaultDomainAndUserName(tszAccountName, ccAccountName);
        }
        SetDlgItemText(hDlg, txt_sa_run_as, tszAccountName);
        I_SetDlgItemText(hDlg, edt_sa_passwd, pai->pwszPassword);
        I_SetDlgItemText(hDlg, edt_sa_cfrmpasswd, pai->pwszPassword);

        //
        // Center the dialog.
        //

        CenterDialog(hDlg);
        return TRUE; // TRUE == let windows set focus

    case WM_COMMAND:
        switch(LOWORD(wParam))
        {
        case IDOK:
            DWORD ccAccountName, ccPassword;
            tszAccountName[0] = _T('\0');

            ccAccountName = GetDlgItemText(hDlg, txt_sa_run_as,
                                           tszAccountName, MAX_USERNAME + 1);

            if (tszAccountName[0] != _T('\0'))
            {
                ccAccountName++;
                LPWSTR pwszAccountName = (LPWSTR)CoTaskMemAlloc(
                                     ccAccountName * sizeof(WCHAR));

                if (pwszAccountName != NULL)
                {
#if !defined(UNICODE)
                    HRESULT hr = AnsiToUnicode(pwszAccountName,
                                               tszAccountName,
                                               ccAccountName);

                    if (FAILED(hr))
                    {
                        CoTaskMemFree(pwszAccountName);
                        SchedUIErrorDialog(hDlg, IERR_INTERNAL_ERROR, 0);
                        break;
                    }
#else
                    lstrcpy(pwszAccountName, tszAccountName);
#endif // !defined(UNICODE)

                    ccPassword = GetDlgItemText(hDlg, edt_sa_passwd,
                                                tszPassword,
                                                MAX_PASSWORD + 1);

                    GetDlgItemText(hDlg, edt_sa_cfrmpasswd,
                                   tszConfirmedPassword, MAX_PASSWORD + 1);

                    if (lstrcmp(tszPassword, tszConfirmedPassword) != 0)
                    {
                        //
                        // Passwords didn't match. Let the user know so he/she
                        // can correct it.
                        //

                        CoTaskMemFree(pwszAccountName);
                        SchedUIErrorDialog(hDlg, IERR_PASSWORD,
                                           (LPTSTR)NULL);
                        return(TRUE);
                    }
                    else
                    {
                        if (ccPassword)
                        {
                            //
                            // Non-NULL password.
                            //

                            ccPassword++;
                            pwszPassword = new WCHAR[ccPassword];

                            if (pwszPassword != NULL)
                            {
#if !defined(UNICODE)
                                HRESULT hr = AnsiToUnicode(pwszPassword,
                                                           tszPassword,
                                                           ccPassword);
                                if (FAILED(hr))
                                {
                                    CoTaskMemFree(pwszAccountName);
                                    SchedUIErrorDialog(hDlg,
                                                       IERR_INTERNAL_ERROR,
                                                       0);
                                    return(TRUE);
                                }
#else
                                lstrcpy(pwszPassword, tszPassword);
#endif // !defined(UNICODE)
                            }
                            else
                            {
                                CoTaskMemFree(pwszAccountName);
                                SchedUIErrorDialog(hDlg, IERR_OUT_OF_MEMORY,
                                                   (LPTSTR)NULL);
                                EndDialog(hDlg, wParam);
                                return(TRUE);
                            }
                        }
                        else
                        {
                            //
                            // Clear the password.
                            //
                            pwszPassword = new WCHAR[1];
                            if (pwszPassword != NULL)
                            {
                                *pwszPassword = L'\0';
                            }
                            else
                            {
                                CoTaskMemFree(pwszAccountName);
                                SchedUIErrorDialog(hDlg, IERR_OUT_OF_MEMORY,
                                                   (LPTSTR)NULL);
                                EndDialog(hDlg, wParam);
                                return(TRUE);
                            }
                        }
                    }

                    if (pai->pwszAccountName != NULL)
                    {
                        CoTaskMemFree(pai->pwszAccountName);
                    }
                    if (pai->pwszPassword != NULL &&
                        pai->pwszPassword != tszEmpty)
                    {
                        ZERO_PASSWORD(pai->pwszPassword);
                        delete pai->pwszPassword;
                    }
                    pai->pwszAccountName = pwszAccountName;
                    pai->pwszPassword    = pwszPassword;
                }
                else
                {
                    SchedUIErrorDialog(hDlg, IERR_OUT_OF_MEMORY,
                                       (LPTSTR)NULL);
                }
            }
            else
            {
                //
                // User cannot specify an empty account name.
                //

                SchedUIErrorDialog(hDlg, IERR_ACCOUNTNAME, (LPTSTR)NULL);
                return(TRUE);
            }

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

    default:
        return FALSE;
    }
}
#endif // !defined(_CHICAGO_)



#if !defined(_CHICAGO_)
//+---------------------------------------------------------------------------
//
//  Function:   CenterDialog
//
//  Synopsis:   Helper to center a dialog on screen.
//
//  Arguments:  [hDlg]   -- Dialog handle.
//
//  Returns:    None.
//
//  Notes:      None.
//
//----------------------------------------------------------------------------
void
CenterDialog(HWND hDlg)
{
    RECT rc;
    GetWindowRect(hDlg, &rc);

    SetWindowPos(hDlg,
                 NULL,
                 ((GetSystemMetrics(SM_CXSCREEN) - (rc.right - rc.left)) / 2),
                 ((GetSystemMetrics(SM_CYSCREEN) - (rc.bottom - rc.top)) / 2),
                 0,
                 0,
                 SWP_NOSIZE | SWP_NOACTIVATE);
}
#endif // !defined(_CHICAGO_)


//+---------------------------------------------------------------------------
//
//  Function:   SchedGetDlgItemTextLength
//
//  Synopsis:   Implements a GetDlgItemTextLength function since Win32 lacks it
//
//  Arguments:
//
//  Returns:
//
//----------------------------------------------------------------------------
int
SchedGetDlgItemTextLength(
    HWND hwnd,
    int id)
{
    if ((hwnd = GetDlgItem(hwnd, id)) != NULL)
    {
        return GetWindowTextLength(hwnd);
    }

    return 0;
}