/** FILE: pp_lpt.c ********** Module Header *******************************
 *
 *  Control panel applet for configuring LPT ports.  This file contains
 *  the dialog and utility functions for managing the UI for setting LPT
 *  port parameters
 *
 @@BEGIN_DDKSPLIT
 * History:
 *      jsenior - 7/10/98 - created
 @@END_DDKSPLIT
 *
 *  Copyright (C) 1990-1995 Microsoft Corporation
 *
 *************************************************************************/

//==========================================================================
//                                Include files
//==========================================================================
#include "ports.h"
#include "pp_lpt.h"
#include <windowsx.h>

TCHAR m_szFilterResourceMethod[]    = TEXT("FilterResourceMethod");
TCHAR m_szParEnableLegacyZip[]      = TEXT("ParEnableLegacyZip");
TCHAR m_szParEnableLegacyZipRegPath[] = TEXT("SYSTEM\\CurrentControlSet\\Services\\Parport\\Parameters");

const DWORD LptHelpIDs[]=
{
    IDC_STATIC, IDH_NOHELP, //Filter resource method text - help not needed
    IDC_FILTERMETHOD_TRYNOT,    idh_devmgr_portset_trynot,  //first radio button
    IDC_FILTERMETHOD_NEVER, idh_devmgr_portset_never,   //second radio button
    IDC_FILTERMETHOD_ACCEPTANY, idh_devmgr_portset_acceptany,   //third radio button
    IDC_LPTNUMTEXT, idh_devmgr_portset_portnum, //Port number text
    PP_LPT_PORT_NUMBER, idh_devmgr_portset_LPTchoice,   //the list box for port number
    IDC_LPT_ENABLE_LEGACY, idh_devmgr_enable_legacy,    //Enable legacy detection checkbox
    0, 0
};

#define NUM_FILTER_RESOURCE_METHODS 3
DWORD FilterResourceMethods[NUM_FILTER_RESOURCE_METHODS] = {
            0,  // Try not to use an interrupt
            1,  // Never use an interrupt
            2   // Accept any interrupt given to the port
            };

// C Runtime
// @@BEGIN_DDKSPLIT
//
// Prototype for IsUserAdmin (in pp.c)
//
BOOL
IsUserAdmin(VOID);
// @@END_DDKSPLIT

void
InformDriverOfChanges(BOOL NeedReboot,
                      IN PLPT_PROP_PARAMS LptPropParams);

void
LptPortOnHelp(HWND       ParentHwnd,
              LPHELPINFO HelpInfo);

BOOL
LptPortOnContextMenu(HWND HwndControl,
                     WORD Xpos,
                     WORD Ypos);

BOOL
LptPortOnInitDialog(
    HWND    ParentHwnd,
    HWND    FocusHwnd,
    LPARAM  Lparam);

ULONG
LptFillPortSettings(
    IN HWND                 ParentHwnd,
    IN PLPT_PROP_PARAMS     LptPropPageData);

ULONG
LptFillPortNameCb(
    HWND                ParentHwnd,
    PLPT_PROP_PARAMS    LptPropPageData);

void LptInitializeControls(
    IN HWND           ParentHwnd,
    IN PLPT_PROP_PARAMS LptPropPageData);

ULONG
LptSetFilterResourceMethod(
    HWND                ParentHwnd,
    PLPT_PROP_PARAMS    LptPropPageData);

ULONG
LptSavePortSettings(
    IN HWND             ParentHwnd,
    IN PLPT_PROP_PARAMS LptPropParams);

BOOL
LptPortOnNotify(
    HWND    ParentHwnd,
    LPNMHDR NmHdr);

void
LptPortOnCommand(
    HWND ParentHwnd,
    int  ControlId,
    HWND ControlHwnd,
    UINT NotifyCode);

UINT
LptEnactPortNameChanges(
    IN HWND             ParentHwnd,
    IN PLPT_PROP_PARAMS LptPropParams,
    IN HKEY             hDeviceKey,
    IN UINT             NewLptNum);

void
LptEnumerateUsedPorts(
    IN HWND             ParentHwnd,
    IN PBYTE            Buffer,
    IN DWORD            BufferSize);

void
LptInitPropParams(IN OUT PLPT_PROP_PARAMS  Params,
                  IN HDEVINFO              DeviceInfoSet,
                  IN PSP_DEVINFO_DATA      DeviceInfoData);

HPROPSHEETPAGE
LptInitSettingsPage(PROPSHEETPAGE *      psp,
                    OUT PLPT_PROP_PARAMS Params);

INT_PTR
APIENTRY
LptPortSettingsDlgProc(IN HWND   hDlg,
                       IN UINT   uMessage,
                       IN WPARAM wParam,
                       IN LPARAM lParam);

UINT
CALLBACK
LptPortSettingsDlgCallback(HWND hwnd,
                           UINT uMsg,
                           LPPROPSHEETPAGE ppsp);

/*++

Routine Description: ParallelPortPropPageProvider

    Entry-point for adding additional device manager property
    sheet pages.  Registry specifies this routine under
    Control\Class\PortNode::EnumPropPage32="msports.dll,thisproc"
    entry.  This entry-point gets called only when the Device
    Manager asks for additional property pages.

Arguments:

    Info  - points to PROPSHEETPAGE_REQUEST, see setupapi.h
    AddFunc - function ptr to call to add sheet.
    Lparam - add sheet functions private data handle.

Return Value:

    BOOL: FALSE if pages could not be added, TRUE on success

--*/
BOOL
APIENTRY
ParallelPortPropPageProvider(LPVOID                 Info,
                             LPFNADDPROPSHEETPAGE   AddFunc,
                             LPARAM                 Lparam)
{
   PSP_PROPSHEETPAGE_REQUEST pprPropPageRequest;
   PROPSHEETPAGE             psp;
   HPROPSHEETPAGE            hpsp;
   PLPT_PROP_PARAMS          params = NULL;

   pprPropPageRequest = (PSP_PROPSHEETPAGE_REQUEST) Info;

   // @@BEGIN_DDKSPLIT
   //
   // Only administrators are allowed to see this page
   //
   if (!IsUserAdmin()) {
       return FALSE;
   }
   // @@END_DDKSPLIT

   //
   // Allocate and zero out memory for the struct that will contain
   // page specific data
   //
   params = (PLPT_PROP_PARAMS) LocalAlloc(LPTR, sizeof(LPT_PROP_PARAMS));

   if (!params) {
       ErrMemDlg(GetFocus());
       return FALSE;
   }

   if (pprPropPageRequest->PageRequested == SPPSR_ENUM_ADV_DEVICE_PROPERTIES) {
        LptInitPropParams(params,
                          pprPropPageRequest->DeviceInfoSet,
                          pprPropPageRequest->DeviceInfoData);

        hpsp = LptInitSettingsPage(&psp, params);

        if (!hpsp) {
            return FALSE;
        }

        if (!(*AddFunc)(hpsp, Lparam)) {
            DestroyPropertySheetPage(hpsp);
            return FALSE;
        }
   }

   return TRUE;
} // ParallelPortPropPageProvider

/*++

Routine Description: LptPortSettingsDlgProc

    The windows control function for the Port Settings properties window

Arguments:

    hDlg, uMessage, wParam, lParam: standard windows DlgProc parameters

Return Value:

    BOOL: FALSE if function fails, TRUE if function passes

--*/
INT_PTR
APIENTRY
LptPortSettingsDlgProc(IN HWND   hDlg,
                       IN UINT   uMessage,
                       IN WPARAM wParam,
                       IN LPARAM lParam)
{
    switch(uMessage) {
    case WM_COMMAND:
        LptPortOnCommand(hDlg, (int) LOWORD(wParam), (HWND)lParam, (UINT)HIWORD(wParam));
        break;

    case WM_CONTEXTMENU:
        return LptPortOnContextMenu((HWND)wParam, LOWORD(lParam), HIWORD(lParam));

    case WM_HELP:
        LptPortOnHelp(hDlg, (LPHELPINFO) lParam);
        break;

    case WM_INITDIALOG:
        return LptPortOnInitDialog(hDlg, (HWND)wParam, lParam);

    case WM_NOTIFY:
        return LptPortOnNotify(hDlg, (NMHDR *)lParam);

    }

    return FALSE;
} // PortSettingsDialogProc

void
LptInitPropParams(IN OUT PLPT_PROP_PARAMS  Params,
                  IN HDEVINFO              DeviceInfoSet,
                  IN PSP_DEVINFO_DATA      DeviceInfoData)
{
    BOOL                        bResult;
    DWORD                       requiredSize = 0;
    SP_DEVINFO_LIST_DETAIL_DATA detailData;

    ZeroMemory(Params, sizeof(LPT_PROP_PARAMS));

    Params->DeviceInfoSet = DeviceInfoSet;
    Params->DeviceInfoData = DeviceInfoData;
    Params->ChangesEnabled = TRUE;

    //
    // Get the device ID first: if the device path is larger then
    // MAX_PATH, we will try again with a bigger buffer
    //
    bResult = SetupDiGetDeviceInstanceId(DeviceInfoSet,
                                         DeviceInfoData,
                                         NULL,
                                         MAX_PATH,
                                         &requiredSize);

    //
    // See if we are being invoked locally or over the network.  If over the net,
    // then disable all possible changes.
    //
    detailData.cbSize = sizeof(SP_DEVINFO_LIST_DETAIL_DATA);
    if (SetupDiGetDeviceInfoListDetail(DeviceInfoSet, &detailData) &&
        detailData.RemoteMachineHandle != NULL) {
        Params->ChangesEnabled = FALSE;
    }

} // LptInitPropParams

HPROPSHEETPAGE LptInitSettingsPage(PROPSHEETPAGE *      psp,
                                   OUT PLPT_PROP_PARAMS Params)
{
    //
    // Add the Port Settings property page
    //
    psp->dwSize      = sizeof(PROPSHEETPAGE);
    psp->dwFlags     = PSP_USECALLBACK; // | PSP_HASHELP;
    psp->hInstance   = g_hInst;
    psp->pszTemplate = MAKEINTRESOURCE(DLG_PP_LPT_PORTSETTINGS);

    //
    // following points to the dlg window proc
    //
    psp->pfnDlgProc = LptPortSettingsDlgProc;
    psp->lParam     = (LPARAM) Params;

    //
    // following points to some control callback of the dlg window proc
    //
    psp->pfnCallback = LptPortSettingsDlgCallback;

    //
    // allocate our "Ports Setting" sheet
    //
    return CreatePropertySheetPage(psp);
}


UINT
CALLBACK
LptPortSettingsDlgCallback(HWND hwnd,
                           UINT uMsg,
                           LPPROPSHEETPAGE ppsp)
{
    PLPT_PROP_PARAMS params;

    switch (uMsg) {
    case PSPCB_CREATE:
        return TRUE;    // return TRUE to continue with creation of page

    case PSPCB_RELEASE:
        params = (PLPT_PROP_PARAMS) ppsp->lParam;
        LocalFree(params);

        return 0;       // return value ignored

    default:
        break;
    }

    return TRUE;
}

BOOL
LptPortOnInitDialog(
    HWND    ParentHwnd,
    HWND    FocusHwnd,
    LPARAM  Lparam
    )
{
    PLPT_PROP_PARAMS lptPropParams;
    lptPropParams = (PLPT_PROP_PARAMS) ((LPPROPSHEETPAGE)Lparam)->lParam;
    //
    // Save value away
    SetWindowLongPtr(ParentHwnd, DWLP_USER, (ULONG_PTR) lptPropParams);

    //
    // Initialize the dialog box parameters
    //
    LptFillPortSettings(ParentHwnd, lptPropParams);

    //
    // Set up the dialog box with these initialized parameters
    //
    LptInitializeControls(ParentHwnd, lptPropParams);

    return TRUE;
}

/*++

Routine Description: LptFillPortSettings

    Gets the settings out of the registry ready for initializing the dialog box
    with.

Arguments:

    LptPropPageData:        the data to fill in
    ParentHwnd:             address of the window

Return Value:

    ULONG: returns error messages

--*/
ULONG
LptFillPortSettings(
    IN HWND                 ParentHwnd,
    IN PLPT_PROP_PARAMS     LptPropPageData
    )
{
    HKEY  hKey;
    DWORD dwPortNameSize, dwError;
    TCHAR szCharBuffer[81];
    DWORD dwSize, dwData, dwMethod;

    //
    // Open the device key for the source device instance, and retrieve its
    // "PortName" value.
    //
    hKey = SetupDiOpenDevRegKey(LptPropPageData->DeviceInfoSet,
                                      LptPropPageData->DeviceInfoData,
                                      DICS_FLAG_GLOBAL,
                                      0,
                                      DIREG_DEV,
                                      KEY_ALL_ACCESS);

    if (INVALID_HANDLE_VALUE == hKey) {
        return GetLastError();
    }

    dwPortNameSize = sizeof(LptPropPageData->szLptName);
    dwError = RegQueryValueEx(hKey,
                              m_szPortName,  // "PortName"
                              NULL,
                              NULL,
                              (PBYTE)LptPropPageData->szLptName,
                              &dwPortNameSize);

    if(ERROR_SUCCESS != dwError) {
        RegCloseKey(hKey);
        return dwError;
    }

    //
    // create "lpt#:"
    //
    lstrcpy(szCharBuffer, LptPropPageData->szLptName);
    lstrcat(szCharBuffer, m_szColon);

    dwSize = sizeof(LptPropPageData->FilterResourceMethod);
    dwError = RegQueryValueEx(hKey,
                             m_szFilterResourceMethod,
                             NULL,
                             NULL,
                             (LPBYTE)(&LptPropPageData->FilterResourceMethod),
                             &dwSize);

    if (dwError != ERROR_SUCCESS) {
        //
        // value does not exist. Create our own:
        // Get Filter Resource Method information
        //
        LptPropPageData->FilterResourceMethod =
            FilterResourceMethods[RESOURCE_METHOD_DEFAULT_IDX];

        dwError = RegSetValueEx(hKey,
                              m_szFilterResourceMethod,
                              0,
                              REG_DWORD,
                              (LPBYTE)(&LptPropPageData->FilterResourceMethod),
                              sizeof(LptPropPageData->FilterResourceMethod));
    }
    RegCloseKey(hKey);

    dwError = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
                           m_szParEnableLegacyZipRegPath,
                           0,
                           KEY_ALL_ACCESS,
                           &hKey);
    if (dwError != ERROR_SUCCESS) {
        //
        // Don't have access maybe?
        //
        LptPropPageData->ParEnableLegacyZip = ENABLELEGACYZIPDEFAULT;
        return dwError;
    }
    dwSize = sizeof(LptPropPageData->ParEnableLegacyZip);
    dwError = RegQueryValueEx(hKey,
                              TEXT("ParEnableLegacyZip"),
                              NULL,
                              NULL,
                              (LPBYTE)(&LptPropPageData->ParEnableLegacyZip),
                              &dwSize);
    if (dwError != ERROR_SUCCESS) {
        //
        // value does not exist. Create our own
        //
        LptPropPageData->ParEnableLegacyZip = ENABLELEGACYZIPDEFAULT;
        dwError = RegSetValueEx(hKey,
                                m_szParEnableLegacyZip,
                                0,
                                REG_DWORD,
                                (LPBYTE)(&LptPropPageData->ParEnableLegacyZip),
                                sizeof(LptPropPageData->ParEnableLegacyZip));
    }
    RegCloseKey(hKey);

    return dwError;
} // LptFillPortSettings


/*++

Routine Description: LptInitializeControls

    Initializes all of the controls that represent Fifo

Arguments:

    ParentHwnd - handle to the dialog
    LptPropPageData - Contains all of the initial values

Return Value:

    None

--*/
void LptInitializeControls(
    IN HWND           ParentHwnd,
    IN PLPT_PROP_PARAMS LptPropPageData
    )
{
    TCHAR    szCurrentValue[40];
    HWND     hwnd;
    int      i, periodIdx;

    LptFillPortNameCb(ParentHwnd, LptPropPageData);

    LptSetFilterResourceMethod(ParentHwnd, LptPropPageData);

    //
    // Set the state of the Enable Legacy Detection checkbox
    //
    if (LptPropPageData->ParEnableLegacyZip) {
        CheckDlgButton(ParentHwnd,
                       IDC_LPT_ENABLE_LEGACY,
                       BST_CHECKED);
    } else {
        CheckDlgButton(ParentHwnd,
                       IDC_LPT_ENABLE_LEGACY,
                       BST_UNCHECKED);
    }

    if (!LptPropPageData->ChangesEnabled) {
//        EnableWindow(GetDlgItem(ParentHwnd, IDC_FIFO), FALSE);

    }
} // LptInitializeControls

/*++

Routine Description: LptSetFilterResourceMethod

    Checks the appropriate resource method to use

Arguments:

    LptPropPageData:    where to get the data from
    ParentHwnd:         address of the window

Return Value:

    ULONG: returns error messages

--*/
ULONG
LptSetFilterResourceMethod(
    HWND                ParentHwnd,
    PLPT_PROP_PARAMS    LptPropPageData
    )
{
    switch (LptPropPageData->FilterResourceMethod) {
    case FILTERMETHOD_TRYNOT:
        CheckRadioButton( ParentHwnd, // handle to dialog box
                          IDC_FILTERMETHOD_TRYNOT, // first button in group
                          IDC_FILTERMETHOD_ACCEPTANY, // last button in group
                          IDC_FILTERMETHOD_TRYNOT // selected
                          );
        break;
    case FILTERMETHOD_ACCEPTANY:
        CheckRadioButton( ParentHwnd, // handle to dialog box
                          IDC_FILTERMETHOD_TRYNOT, // first button in group
                          IDC_FILTERMETHOD_ACCEPTANY, // last button in group
                          IDC_FILTERMETHOD_ACCEPTANY // selected
                          );
        break;
    case FILTERMETHOD_NEVER:
        CheckRadioButton( ParentHwnd, // handle to dialog box
                          IDC_FILTERMETHOD_TRYNOT, // first button in group
                          IDC_FILTERMETHOD_ACCEPTANY, // last button in group
                          IDC_FILTERMETHOD_NEVER // selected
                          );
        break;
    default:
        CheckRadioButton( ParentHwnd, // handle to dialog box
                          IDC_FILTERMETHOD_TRYNOT, // first button in group
                          IDC_FILTERMETHOD_ACCEPTANY, // last button in group
                          IDC_FILTERMETHOD_NEVER // selected
                          );
        break;
    }
    return 0;
}

/*++

Routine Description: LptFillPortNameCb

    fill in the Port Name combo box selection with a list
    of possible un-used portnames

Arguments:

    LptPropPageData:    where to get the data from
    hDlg:               address of the window

Return Value:

    ULONG: returns error messages

--*/
ULONG
LptFillPortNameCb(
    HWND                ParentHwnd,
    PLPT_PROP_PARAMS    LptPropPageData
    )
{
    BYTE  portUsage[MAX_LPT_PORT];
    DWORD tmp, portsReported;
    int   i, nEntries;
    int   nCurPortNum;
    TCHAR szLpt[40];
    TCHAR szInUse[40];
    HWND  portHwnd;

    portHwnd = GetDlgItem(ParentHwnd, PP_LPT_PORT_NUMBER);

    //
    // Check if our LptName is blank. If it is, disable the lpt port selector
    //
    if (_tcscmp(LptPropPageData->szLptName, TEXT("")) == 0) {
        EnableWindow(portHwnd, FALSE);
        EnableWindow(GetDlgItem(ParentHwnd, IDC_LPTNUMTEXT), FALSE);
        return 0;
    }

    //
    // assumes szLptPort filled in...
    //
    nCurPortNum = myatoi(&LptPropPageData->szLptName[3]);

    if (!LoadString(g_hInst, IDS_IN_USE, szInUse, CharSizeOf(szInUse))) {
        wcscpy(szInUse, _T(" (in use)"));
    }

    //
    // first tally up which ports NOT to offer in list box
    //
    ZeroMemory(portUsage, MAX_LPT_PORT);

    // Find out which ports not to offer
    LptEnumerateUsedPorts(ParentHwnd,
                          portUsage,
                          MAX_LPT_PORT);

    for(i = MIN_LPT-1; i < MAX_LPT_PORT; i++) {

       wsprintf(szLpt, TEXT("LPT%d"), i+1);
       if (portUsage[i] &&
           _tcscmp(szLpt, LptPropPageData->szLptName)) {
           wcscat(szLpt, szInUse);
       }

       ComboBox_AddString(portHwnd, szLpt);
   }

   ComboBox_SetCurSel(portHwnd, nCurPortNum-1);
   return 0;
} // FillPortNamesCb

void
LptPortOnCommand(
    HWND ParentHwnd,
    int  ControlId,
    HWND ControlHwnd,
    UINT NotifyCode
    )
{
    PLPT_PROP_PARAMS params =
        (PLPT_PROP_PARAMS) GetWindowLongPtr(ParentHwnd, DWLP_USER);

    if (NotifyCode == CBN_SELCHANGE) {
        PropSheet_Changed(GetParent(ParentHwnd), ParentHwnd);
    }
    else {
        switch (ControlId) {
        //
        // Because this is a prop sheet, we should never get this.
        // All notifications for ctrols outside of the sheet come through
        // WM_NOTIFY
        //
        case IDOK:
        case IDCANCEL:
            EndDialog(ParentHwnd, 0);
            return;
        }
    }
} // LptPortOnCommand

BOOL
LptPortOnNotify(
    HWND    ParentHwnd,
    LPNMHDR NmHdr
    )
{
    PLPT_PROP_PARAMS params =
        (PLPT_PROP_PARAMS) GetWindowLongPtr(ParentHwnd, DWLP_USER);

    switch (NmHdr->code) {
    //
    // Sent when the user clicks on Apply OR OK !!
    //
    case PSN_APPLY:
        //
        // Write out the lpt port options to the registry
        //
        LptSavePortSettings(ParentHwnd, params);
        SetWindowLongPtr(ParentHwnd, DWLP_MSGRESULT, PSNRET_NOERROR);
        return TRUE;

    default:
        return FALSE;
    }
} // LptPortOnNotify

/*++

Routine Description: LptSavePortSettings

    saves the advanced box settings back to the registry, if any were
    changed

Arguments:

    AdvancedData: holds the current settings and the location of of
                   the device in the registry
    ParentHwnd:          address of the window

Return Value:

    ULONG: returns error messages

--*/
ULONG
LptSavePortSettings(
    IN HWND ParentHwnd,
    IN PLPT_PROP_PARAMS LptPropParams
    )
{
    HKEY   hKey;
    DWORD  dwSize, dwData;

    UINT  i = CB_ERR, curLptNum, newLptNum = CB_ERR;
    UINT  uiTryNotChecked, uiNeverChecked, uiAcceptAnyChecked;
    DWORD curFilterResourceMethod;
    DWORD newFilterResourceMethod = 0;
    DWORD curParEnableLegacyZip, newParEnableLegacyZip;
    ULONG error = ERROR_SUCCESS;

    SP_DEVINSTALL_PARAMS spDevInstall;

    //
    // Grab all of the new settings
    //

    // Filter resource method
    curFilterResourceMethod = newFilterResourceMethod = LptPropParams->FilterResourceMethod;
    if (BST_CHECKED ==
        IsDlgButtonChecked(ParentHwnd, IDC_FILTERMETHOD_TRYNOT))
        newFilterResourceMethod = 0;
    else if (BST_CHECKED ==
        IsDlgButtonChecked(ParentHwnd, IDC_FILTERMETHOD_NEVER))
        newFilterResourceMethod = 1;
    else if (BST_CHECKED ==
        IsDlgButtonChecked(ParentHwnd, IDC_FILTERMETHOD_ACCEPTANY))
        newFilterResourceMethod = 2;


    // LPT port number
    curLptNum = myatoi(LptPropParams->szLptName + wcslen(m_szLPT));
    newLptNum = ComboBox_GetCurSel(GetDlgItem(ParentHwnd, PP_LPT_PORT_NUMBER));
    if (newLptNum == CB_ERR) {
        newLptNum = curLptNum;
    } else {
        newLptNum++;
    }

    // Legacy device detection
    curParEnableLegacyZip = LptPropParams->ParEnableLegacyZip;
    if (BST_CHECKED == IsDlgButtonChecked(ParentHwnd, IDC_LPT_ENABLE_LEGACY)) {
        newParEnableLegacyZip = 0x1;
    } else {
        newParEnableLegacyZip = 0x0;
    }

    //
    // See if they changed anything
    //
    if ((curLptNum == newLptNum) &&
        (curFilterResourceMethod == newFilterResourceMethod) &&
        (curParEnableLegacyZip == newParEnableLegacyZip)) {
        //
        // They didn't change anything. Just exit.
        //
        return ERROR_SUCCESS;
    }

    //
    // Open the device key for the source device instance
    //
    hKey = SetupDiOpenDevRegKey(LptPropParams->DeviceInfoSet,
                                     LptPropParams->DeviceInfoData,
                                     DICS_FLAG_GLOBAL,
                                     0,
                                     DIREG_DEV,
                                     KEY_ALL_ACCESS);

    if (INVALID_HANDLE_VALUE == hKey) {
        //
        // Not much we can do, just exit gracefully
        //
        return ERROR_SUCCESS;
    }

    // Check the LPT port name for changes
    if (newLptNum != curLptNum) {
        LptEnactPortNameChanges(ParentHwnd,
                                LptPropParams,
                                hKey,
                                newLptNum);
    }

    // Check the Filter resource method for changes
    if (curFilterResourceMethod != newFilterResourceMethod) {
        //
        // They changed the Filter Resource Method
        //
        dwData = newFilterResourceMethod;
        dwSize = sizeof(dwData);
        RegSetValueEx(hKey,
                m_szFilterResourceMethod,
                0,
                REG_DWORD,
                (CONST BYTE *)(&dwData),
                dwSize);
    }
    RegCloseKey(hKey);

    if (curParEnableLegacyZip != newParEnableLegacyZip) {
        //
        // Open the services path and set the new value for Legacy Parallel device
        // detection.
        //
        DWORD disposition = 0;
        error = RegCreateKeyEx(HKEY_LOCAL_MACHINE,
                               m_szParEnableLegacyZipRegPath,
                               0,
                               (TCHAR *) NULL,
                               REG_OPTION_NON_VOLATILE,
                               KEY_ALL_ACCESS,
                               (LPSECURITY_ATTRIBUTES) NULL,
                               &hKey,
                               &disposition);

        if (error == ERROR_SUCCESS) {
            error = RegSetValueEx(hKey,
                                    m_szParEnableLegacyZip,
                                    0,
                                    REG_DWORD,
                                    (LPBYTE)(&newParEnableLegacyZip),
                                    sizeof(newParEnableLegacyZip));
            RegCloseKey(hKey);
            if (error != ERROR_SUCCESS) {
                goto ParEnableLegacyZipSetParamFailed;
            }
            if (newParEnableLegacyZip == 0) {
                //
                // We want a reboot when disabling this thing, because the parallel
                // enumerator won't get rid of legacy devices.
                //
                InformDriverOfChanges(TRUE, LptPropParams);
            } else {
                InformDriverOfChanges(FALSE, LptPropParams);
            }
        } else {
ParEnableLegacyZipSetParamFailed:
            MyMessageBox(ParentHwnd,
                         IDS_LPT_LEGACY_FAILED,
                         IDS_LPT_PROPERTIES,
                         MB_OK);
            //
            // Don't want to overload the user by telling them they have to
            // reboot. Since we were unable to set things correctly, just
            // rebuild the stack.
            //
            InformDriverOfChanges(FALSE, LptPropParams);
        }
    } else {
        InformDriverOfChanges(FALSE, LptPropParams);
    }

    return error;
} // LptSaveAdvancedSettings

UINT
LptEnactPortNameChanges(
    IN HWND             ParentHwnd,
    IN PLPT_PROP_PARAMS LptPropParams,
    IN HKEY             hDeviceKey,
    IN UINT             NewLptNum)
{
    HANDLE  hLpt;
    DWORD   dwError, dwNewLptNameLen;
    UINT    curLptNum;
    BYTE    portUsage[MAX_LPT_PORT];
    TCHAR   charBuffer[LINE_LEN],
            friendlyNameFormat[LINE_LEN],
            deviceDesc[LINE_LEN],
            buffer[BUFFER_SIZE],
            szNewLptName[20];

    //
    // Check if we're trying to rename the port to the same name.
    //
    wsprintf(szNewLptName,_T("\\DosDevices\\LPT%d"),NewLptNum);
    if (wcscmp(szNewLptName, LptPropParams->szLptName) == 0) {
        return ERROR_SUCCESS;
    }

    //
    // Check if a valid port number has been passed in
    //
    if (MAX_LPT_PORT < NewLptNum) {
        //
        // Get out of here - exceeding array bounds
        // This should never happen in the property page since it is a hardcoded
        // selection box.  The user can't simply type a number.
        //
        MyMessageBox(ParentHwnd, IDS_LPT_NUM_ERROR, IDS_LPT_PROPERTIES,
            MB_OK | MB_ICONINFORMATION);
        return ERROR_SUCCESS;
    }

    //
    // Get an array of used ports
    //
    LptEnumerateUsedPorts(ParentHwnd,
                          portUsage,
                          MAX_LPT_PORT);
    if (portUsage[NewLptNum-1]) {
        //
        // Port name is taken by another port.  Check if user wants system to
        // get into inconsistent state.
        //
        if (IDNO == MyMessageBox(ParentHwnd, IDS_LPT_PORT_INUSE,
                                 IDS_LPT_PROPERTIES, MB_YESNO |
                                 MB_ICONINFORMATION)) {
            return ERROR_SUCCESS;
        }
    }

    curLptNum = myatoi(LptPropParams->szLptName + wcslen(m_szLPT));

    //
    // Make sure that the port has not been opened by another application
    //
    wsprintf(buffer, L"\\\\.\\%ws", LptPropParams->szLptName);
    hLpt = CreateFile(buffer, GENERIC_READ, 0, NULL, OPEN_EXISTING,
                      FILE_ATTRIBUTE_NORMAL, NULL);

    //
    // If the file handle is invalid, then the Lpt port is open, warn the user
    //
    if (hLpt == INVALID_HANDLE_VALUE &&
        MyMessageBox(ParentHwnd, IDS_PORT_OPEN, IDS_LPT_PROPERTIES,
                     MB_YESNO | MB_ICONINFORMATION) == IDNO) {
        return GetLastError();
    }
    CloseHandle(hLpt);

    wsprintf(szNewLptName, _T("LPT%d"), NewLptNum);

    //
    // Open the device key for the source device instance, and write its
    // new "PortName" value.
    //
    hDeviceKey = SetupDiOpenDevRegKey(LptPropParams->DeviceInfoSet,
                                      LptPropParams->DeviceInfoData,
                                      DICS_FLAG_GLOBAL,
                                      0,
                                      DIREG_DEV,
                                      KEY_ALL_ACCESS);

    if (INVALID_HANDLE_VALUE == hDeviceKey) {
        return GetLastError();
    }

    dwNewLptNameLen = ByteCountOf(wcslen(szNewLptName) + 1);

    dwError = RegSetValueEx(hDeviceKey,
                            m_szPortName,
                            0,
                            REG_SZ,
                            (PBYTE) szNewLptName,
                            dwNewLptNameLen);
    if (ERROR_SUCCESS == dwError) {
        wcscpy(LptPropParams->szLptName, szNewLptName);
    } else {
        return dwError;
    }

    // Now generate a string, to be used for the device's friendly name, that
    // incorporates both the INF-specified device description, and the port
    // name.  For example,
    //
    //     ECP Printer Port (LPT1)
    //

    if (LoadString(g_hInst,
                   IDS_FRIENDLY_FORMAT,
                   friendlyNameFormat,
                   CharSizeOf(friendlyNameFormat)) &&
       SetupDiGetDeviceRegistryProperty(LptPropParams->DeviceInfoSet,
                                        LptPropParams->DeviceInfoData,
                                        SPDRP_DEVICEDESC,
                                        NULL,
                                        (PBYTE)deviceDesc,
                                        sizeof(deviceDesc),
                                        NULL)) {
        wsprintf(charBuffer, friendlyNameFormat, deviceDesc, szNewLptName);
    }
    else {
        //
        // Simply use LPT port name.
        //
        lstrcpy(charBuffer, szNewLptName);
    }

    SetupDiSetDeviceRegistryProperty(LptPropParams->DeviceInfoSet,
                                     LptPropParams->DeviceInfoData,
                                     SPDRP_FRIENDLYNAME,
                                     (PBYTE)charBuffer,
                                     ByteCountOf(lstrlen(charBuffer) + 1)
                                    );

    return ERROR_SUCCESS;

} // LptEnactPortNameChanges

void
InformDriverOfChanges(BOOL NeedReboot,
                      IN PLPT_PROP_PARAMS LptPropParams)
{
    SP_DEVINSTALL_PARAMS spDevInstall;

    //
    // Now broadcast this change to the device manager
    //
    ZeroMemory(&spDevInstall, sizeof(SP_DEVINSTALL_PARAMS));
    spDevInstall.cbSize = sizeof(SP_DEVINSTALL_PARAMS);

    if (SetupDiGetDeviceInstallParams(LptPropParams->DeviceInfoSet,
                                      LptPropParams->DeviceInfoData,
                                      &spDevInstall)) {
        if (NeedReboot) {
            spDevInstall.Flags |= DI_PROPERTIES_CHANGE | DI_NEEDREBOOT;
        } else {
            spDevInstall.FlagsEx |= DI_FLAGSEX_PROPCHANGE_PENDING;
        }
        SetupDiSetDeviceInstallParams(LptPropParams->DeviceInfoSet,
                                      LptPropParams->DeviceInfoData,
                                      &spDevInstall);
    }
}

//
// Takes a Buffer of bytes and marks out which port names are taken
//
void
LptEnumerateUsedPorts(
    IN HWND             ParentHwnd,
    IN PBYTE            Buffer,
    IN DWORD            BufferSize)
{
    HKEY    hParallelMap;
    TCHAR   szParallel[BUFFER_SIZE];
    DWORD   dwParallelSize, dwLptSize, dwEnum, dwType, dwResult, dwPortNum;
    TCHAR   szLpt[BUFFER_SIZE];
    PTCHAR  szParPortNum;

    ZeroMemory(Buffer, BufferSize);

    //
    //
    //
    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
                     m_szRegParallelMap,
                     0,
                     KEY_ALL_ACCESS,
                     &hParallelMap) == ERROR_SUCCESS) {

        dwEnum = 0;
        do {
            dwParallelSize = CharSizeOf(szParallel);
            dwLptSize = sizeof(szLpt);
            dwResult = RegEnumValue(hParallelMap,
                                  dwEnum++,
                                  szParallel,
                                  &dwParallelSize,
                                  NULL,
                                  &dwType,
                                  (LPBYTE)szLpt,
                                  &dwLptSize);

            if (dwResult != ERROR_SUCCESS ||
                dwType != REG_SZ) {
                continue;
            }

            szParPortNum = _tcsspnp(szLpt,_T("\\DosDevices\\LPT"));
            if (szParPortNum) {
                //
                // Find out if this is an actual port and not simply a
                // device attached to the port
                //
                if (_tcscspn(szParPortNum,_T(".")) ==
                    _tcslen(szParPortNum)) {
                    dwPortNum = myatoi(szParPortNum);
                    if (dwPortNum-1 < BufferSize) {
                        Buffer[dwPortNum-1] = TRUE;
                    }
                }
            }
        } while (dwResult == ERROR_SUCCESS);
        RegCloseKey(hParallelMap);
    }
} // LptEnumerateUsedPorts

BOOL
LptPortOnContextMenu(HWND HwndControl,
                     WORD Xpos,
                     WORD Ypos)
{
    WinHelp(HwndControl,
            _T("devmgr.hlp"),
            HELP_CONTEXTMENU,
            (ULONG_PTR) LptHelpIDs);

    return FALSE;
}

void
LptPortOnHelp(HWND       ParentHwnd,
              LPHELPINFO HelpInfo)
{
    if (HelpInfo->iContextType == HELPINFO_WINDOW) {
        WinHelp((HWND) HelpInfo->hItemHandle,
                _T("devmgr.hlp"),
                HELP_WM_HELP,
                (ULONG_PTR) LptHelpIDs);
    }
}