Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3528 lines
102 KiB

/*
* UTIL.C
*
* Microsoft Confidential
* Copyright (c) Microsoft Corporation 1993-1994
* All rights reserved
*
*/
#include "proj.h"
#include <tspnotif.h>
#include <slot.h>
#ifdef PROFILE_MASSINSTALL
extern DWORD g_dwTimeSpent;
extern DWORD g_dwTimeBegin;
DWORD g_dwTimeStartModemInstall;
#endif
WORD g_wUsedNameArray[MAX_INSTALLATIONS];
// Function prototypes for the TAPI entry-points
typedef LONG (WINAPI FAR* DIALINITEDPROC)(LPDWORD pdwInited);
typedef LONG (WINAPI FAR* OPENDIALASSTPROC)(HWND hwnd, LPCSTR pszAddressIn, BOOL bSimple, BOOL bSilentInstall);
// Unattended install INF file line fields
#define FIELD_PORT 0
#define FIELD_DESCRIPTION 1
#define FIELD_MANUFACTURER 2
#define FIELD_PROVIDER 3
// Unattended install INF file lines.
typedef struct _tagModemSpec
{
TCHAR szPort[LINE_LEN];
TCHAR szDescription[LINE_LEN];
TCHAR szManufacturer[LINE_LEN];
TCHAR szProvider[LINE_LEN];
} MODEM_SPEC, FAR *LPMODEM_SPEC;
// UNATTENDED-INSTALL-RELATED-GLOBALS
// Global failure-code used by final message box to display error code.
UINT gUnattendFailID;
#ifdef WIN95
/*----------------------------------------------------------
Purpose: Returns the instance portion of a registry pathname
Returns: see above
Cond: --
*/
LPCTSTR
PUBLIC
StrFindInstanceName(
IN LPCTSTR pszPath)
{
LPCTSTR psz;
for (psz = pszPath; *pszPath; pszPath = CharNext(pszPath))
{
if ('\\' == pszPath[0] && pszPath[1])
psz = pszPath + 1;
}
return psz;
}
#endif // WIN95
/*----------------------------------------------------------
Purpose: Position this wizard where the outside wizard is,
and hide and disable the owner until we're done
with this wizard.
The related call to this function is LeaveInsideWizard.
Returns: --
Cond: --
*/
void
PUBLIC
EnterInsideWizard(
IN HWND hDlg)
{
// (The parent of this dialog is the propsheet manager. We
// want the owner of the propsheet manager.)
HWND hwndFrame = GetParent(hDlg);
HWND hwndOutside = GetParent(hwndFrame);
RECT rc;
ASSERT(IsWindow(hwndOutside));
// Check that it is visible before hiding it. This way
// we won't move this dialog wrongfully if the window is
// already hidden.
if (IsWindow(hwndOutside) && IsWindowVisible(hwndOutside))
{
// Position dialog directly over the outside wizard
GetWindowRect(hwndOutside, &rc);
SetWindowPos(hwndFrame, NULL, rc.left, rc.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
SetWindowRedraw(hwndFrame, TRUE);
InvalidateRect(hwndFrame, NULL, TRUE);
UpdateWindow(hwndFrame); // Show the insize wizard
ShowWindow(hwndOutside, FALSE); // Hide the outside wizard
}
}
/*----------------------------------------------------------
Purpose: Position the outside wizard where the inside wizard
is (was).
Returns: --
Cond: --
*/
void
PUBLIC
LeaveInsideWizard(
IN HWND hDlg)
{
HWND hwndFrame = GetParent(hDlg);
HWND hwndOutside = GetParent(hwndFrame);
RECT rc;
ASSERT(IsWindow(hwndOutside));
if (IsWindow(hwndOutside))
{
// In case the user moved the dialog, reposition the owner dialog
// to the new position so everything is seamless
GetWindowRect(hwndFrame, &rc);
InvalidateRect(hwndOutside, NULL, TRUE);
SetWindowPos(hwndOutside, NULL, rc.left, rc.top, 0, 0,
SWP_NOSIZE | SWP_NOZORDER);
ShowWindow(hwndOutside, TRUE);
ShowWindow(hwndFrame, FALSE); // hide inside wizard
UpdateWindow(hwndOutside); // show outside wizard
}
}
/*----------------------------------------------------------
Purpose: Returns a string of the form:
"Base string #n"
where "Base string" is pszBase and n is the nCount.
Returns: --
Cond: --
*/
void
PUBLIC
MakeUniqueName(
OUT LPTSTR pszBuf,
IN LPCTSTR pszBase,
IN UINT nCount)
{
TCHAR szTemplate[MAX_BUF_MED];
LoadString(g_hinst, IDS_DUP_TEMPLATE, szTemplate, SIZECHARS(szTemplate));
wsprintf(pszBuf, szTemplate, pszBase, (UINT)nCount);
}
/*----------------------------------------------------------
Purpose: If a RunOnce command exists in the driver key, run it,
then delete the command.
Returns: --
Cond: --
*/
void
PUBLIC
DoRunOnce(
IN HKEY hkeyDrv)
{
DWORD cbData;
TCHAR szCmd[MAX_PATH];
cbData = sizeof(szCmd);
if (NO_ERROR == RegQueryValueEx(hkeyDrv, c_szRunOnce, NULL, NULL, (LPBYTE)szCmd, &cbData))
{
BOOL bRet;
PROCESS_INFORMATION procinfo;
STARTUPINFO startupinfo;
ZeroInit(&startupinfo);
startupinfo.cb = sizeof(startupinfo);
bRet = CreateProcess(NULL, szCmd, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS,
NULL, NULL, &startupinfo, &procinfo);
RegDeleteValue(hkeyDrv, c_szRunOnce);
#ifdef DEBUG
if (bRet)
{
TRACE_MSG(TF_GENERAL, "Running \"%s\" succeeded", (LPTSTR)szCmd);
}
else
{
TRACE_MSG(TF_GENERAL, "Running \"%s\" returned %#08lx", (LPTSTR)szCmd, GetLastError());
}
#endif
}
else
{
#ifndef PROFILE_MASSINSTALL
TRACE_MSG(TF_GENERAL, "No RunOnce command found");
#endif
}
}
/*----------------------------------------------------------
Purpose: Shows the stand-alone verion of the dial info dialog
if it is necessary.
Returns: --
Cond: --
*/
void
PUBLIC
DoDialingProperties(
IN HWND hwndOwner,
IN BOOL bMiniDlg,
IN BOOL bSilentInstall)
{
HINSTANCE hinstTapi;
DIALINITEDPROC pfnDialInited;
OPENDIALASSTPROC pfnOpenDialAsst;
// Load the TAPI DLL for the dial info dialog
hinstTapi = LoadLibrary(c_szTapiDLL);
if (ISVALIDHINSTANCE(hinstTapi))
{
pfnOpenDialAsst = (OPENDIALASSTPROC)GetProcAddress(hinstTapi, "LOpenDialAsst");
if (pfnOpenDialAsst)
{
BOOL bShowDlg = FALSE;
pfnDialInited = (DIALINITEDPROC)GetProcAddress(hinstTapi, "LAddrParamsInited");
if (pfnDialInited)
{
// Did the function succeed?
DWORD dwInited;
if (0 == pfnDialInited(&dwInited))
{
// Yes; is tapi initialized or should we always
// show the dialog?
bShowDlg = (0 == dwInited || !bMiniDlg);
// Is tapi uninitialized?
if (0 == dwInited)
{
// Yes; always show the mini-dialog to initialize
bMiniDlg = TRUE;
}
}
}
if (bShowDlg)
{
// Invoke the dialog
pfnOpenDialAsst(hwndOwner, NULL, bMiniDlg, bSilentInstall);
}
}
FreeLibrary(hinstTapi);
}
}
//-----------------------------------------------------------------------------------
// DeviceInstaller wrappers and support functions
//-----------------------------------------------------------------------------------
/*----------------------------------------------------------
Purpose: Returns TRUE if the given device data is one of the
detected modems in a set.
This function, paired with CplDiMarkModem, uses
the devParams.ClassInstallReserved field to determine
this. This is not a hack -- this is what the field
is for.
Returns: --
Cond: --
*/
BOOL
PUBLIC
CplDiIsModemMarked(
IN HDEVINFO hdi,
IN PSP_DEVINFO_DATA pdevData,
IN DWORD dwMarkFlags) // MARKF_*
{
SP_DEVINSTALL_PARAMS devParams;
devParams.cbSize = sizeof(devParams);
if (CplDiGetDeviceInstallParams(hdi, pdevData, &devParams))
{
if (IsFlagSet(devParams.ClassInstallReserved, dwMarkFlags))
{
return TRUE;
}
}
return FALSE;
}
/*----------------------------------------------------------
Purpose: Remembers this device instance as a detected modem
during this detection session.
Returns: --
Cond: --
*/
void
PUBLIC
CplDiMarkModem(
IN HDEVINFO hdi,
IN PSP_DEVINFO_DATA pdevData,
IN DWORD dwMarkFlags) // MARKF_*
{
SP_DEVINSTALL_PARAMS devParams;
devParams.cbSize = sizeof(devParams);
if (CplDiGetDeviceInstallParams(hdi, pdevData, &devParams))
{
// Use the ClassInstallReserved field as a boolean indicator
// of whether this device in the device set is detected.
SetFlag(devParams.ClassInstallReserved, dwMarkFlags);
CplDiSetDeviceInstallParams(hdi, pdevData, &devParams);
}
}
/*----------------------------------------------------------
Purpose: Enumerates all the devices in the devinfo set and
unmarks any devices that were previously marked as
detected.
Returns: --
Cond: --
*/
void
PRIVATE
CplDiUnmarkModem(
IN HDEVINFO hdi,
IN PSP_DEVINFO_DATA pdevData,
IN DWORD dwMarkFlags) // MARKF_*
{
SP_DEVINSTALL_PARAMS devParams;
devParams.cbSize = sizeof(devParams);
if (CplDiGetDeviceInstallParams(hdi, pdevData, &devParams))
{
// Clear the ClassInstallReserved field
ClearFlag(devParams.ClassInstallReserved, dwMarkFlags);
CplDiSetDeviceInstallParams(hdi, pdevData, &devParams);
}
}
/*----------------------------------------------------------
Purpose: Enumerates all the devices in the devinfo set and
unmarks any devices that were previously marked as
detected.
Returns: --
Cond: --
*/
void
PRIVATE
CplDiUnmarkAllModems(
IN HDEVINFO hdi,
IN DWORD dwMarkFlags) // MARKF_*
{
SP_DEVINFO_DATA devData;
SP_DEVINSTALL_PARAMS devParams;
DWORD iDevice = 0;
DBG_ENTER(CplDiUnmarkAllModems);
devData.cbSize = sizeof(devData);
devParams.cbSize = sizeof(devParams);
while (CplDiEnumDeviceInfo(hdi, iDevice++, &devData))
{
if (IsEqualGUID(&devData.ClassGuid, g_pguidModem) &&
CplDiGetDeviceInstallParams(hdi, &devData, &devParams))
{
// Clear the ClassInstallReserved field
ClearFlag(devParams.ClassInstallReserved, dwMarkFlags);
CplDiSetDeviceInstallParams(hdi, &devData, &devParams);
}
}
DBG_EXIT(CplDiUnmarkAllModems);
}
/*----------------------------------------------------------
Purpose: Returns TRUE if the device key is a local connection.
A "local connection" is a logical modem device that
represents a cable connection, used by programs such
as the Direct Cable Connection applet.
This function determines the modem device type by
comparing HardwareIDs.
If the modem device is a local connection, *pnPortSubclass
is set to PORT_SUBCLASS_SERIAL or PORT_SUBCLASS_PARALLEL.
Returns: see above
Cond: --
*/
BOOL
PUBLIC
CplDiIsLocalConnection(
IN HDEVINFO hdi,
IN PSP_DEVINFO_DATA pdevData,
OUT LPBYTE pnPortSubclass) OPTIONAL
{
BOOL bRet;
#ifdef WINNT
// There is no DCC on NT SUR.
bRet = FALSE;
#else
TCHAR szHardwareID[MAX_BUF_MED];
// Get the hardware ID for the modem device
bRet = CplDiGetHardwareID(hdi, pdevData, NULL, szHardwareID, sizeof(szHardwareID), NULL);
if (bRet)
{
bRet = (IsSzEqual(szHardwareID, c_szHardwareIDSerial) ||
IsSzEqual(szHardwareID, c_szHardwareIDParallel));
if (bRet && pnPortSubclass)
{
if (IsSzEqual(szHardwareID, c_szHardwareIDSerial))
{
*pnPortSubclass = PORT_SUBCLASS_SERIAL;
}
else
{
ASSERT(IsSzEqual(szHardwareID, c_szHardwareIDParallel));
*pnPortSubclass = PORT_SUBCLASS_PARALLEL;
}
}
}
#endif // WINNT
return bRet;
}
/*----------------------------------------------------------
Purpose: Installs a modem that is compatible with the specified
DeviceInfoData.
Returns: TRUE on success
Cond: --
*/
BOOL
PRIVATE
InstallCompatModem(
IN HDEVINFO hdi,
IN PSP_DEVINFO_DATA pdevData,
IN BOOL bInstallLocalOnly)
{
BOOL bRet = TRUE; // Default success
SP_DRVINFO_DATA drvData;
ASSERT(pdevData);
DBG_ENTER(InstallCompatModem);
MyYield();
// Only install it if it has a selected driver. (Other modems
// that were already installed in a different session may be
// in this device info set. We don't want to reinstall them!)
drvData.cbSize = sizeof(drvData);
if (CplDiIsModemMarked(hdi, pdevData, MARKF_INSTALL) &&
CplDiGetSelectedDriver(hdi, pdevData, &drvData))
{
// Install the driver
if (FALSE == bInstallLocalOnly || CplDiIsLocalConnection(hdi, pdevData, NULL))
{
bRet = CplDiCallClassInstaller(DIF_INSTALLDEVICE, hdi, pdevData);
CplDiUnmarkModem(hdi, pdevData, MARKF_INSTALL);
}
}
DBG_EXIT_BOOL_ERR(InstallCompatModem, bRet);
return bRet;
}
/*----------------------------------------------------------
Purpose: Calls the class installer to install the modem.
Returns: TRUE if at least one modem was installed or if
there were no new modems at all
Cond: Caller should protect this function with CM_Lock
and CM_Unlock (Win95 only).
*/
BOOL
PUBLIC
CplDiInstallModem(
IN HDEVINFO hdi,
IN PSP_DEVINFO_DATA pdevData, OPTIONAL
IN BOOL bLocalOnly)
{
BOOL bRet;
int cFailed = 0;
int cNewModems;
HCURSOR hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));
DBG_ENTER(CplDiInstallModem);
if (pdevData)
{
// Install the given DeviceInfoData
cNewModems = 1;
if ( !InstallCompatModem(hdi, pdevData, bLocalOnly) )
{
cFailed = 1;
}
}
else
{
DWORD iDevice;
SP_DEVINFO_DATA devData;
cNewModems = 0;
// Enumerate all the DeviceInfoData elements in this device set
devData.cbSize = sizeof(devData);
iDevice = 0;
while (CplDiEnumDeviceInfo(hdi, iDevice++, &devData))
{
// Was the install successful?
if ( !InstallCompatModem(hdi, &devData, bLocalOnly) )
{
// No
cFailed++;
}
cNewModems++;
}
}
SetCursor(hcur);
bRet = (cFailed < cNewModems || 0 == cNewModems);
DBG_EXIT_BOOL_ERR(CplDiInstallModem, bRet);
return bRet;
}
/*----------------------------------------------------------
Purpose: This function gets the device info set for the modem
class. The set may be empty, which means there are
no modems currently installed.
The parameter pbInstalled is set to TRUE if there
is a modem installed on the system.
Returns: TRUE a set is created
FALSE
Cond: --
*/
BOOL
PUBLIC
CplDiGetModemDevs(
OUT HDEVINFO FAR * phdi,
IN HWND hwnd, OPTIONAL
IN DWORD dwFlags, // DIGCF_ bit field
OUT BOOL FAR * pbInstalled) OPTIONAL
{
BOOL bRet;
HDEVINFO hdi;
DBG_ENTER(CplDiGetModemDevs);
ASSERT(phdi);
hdi = CplDiGetClassDevs(g_pguidModem, NULL, hwnd, dwFlags);
if (INVALID_HANDLE_VALUE != hdi)
{
SP_DEVINFO_DATA devData;
// Is there a modem present on the system?
devData.cbSize = sizeof(devData);
bRet = CplDiEnumDeviceInfo(hdi, 0, &devData);
if (pbInstalled)
{
*pbInstalled = bRet;
}
SetLastError(NO_ERROR);
}
*phdi = hdi;
bRet = (INVALID_HANDLE_VALUE != *phdi);
DBG_EXIT_BOOL_ERR(CplDiGetModemDevs, bRet);
return bRet;
}
/*----------------------------------------------------------
Purpose: This function gets the logical config for the given
ConfigMgr device instance. If the logical config
does not exist and bCreate is TRUE, this function
will create an empty logical config.
Returns: CR_SUCCESS
some ConfigMgr error
Cond: --
*/
CONFIGRET
PRIVATE
CMGetLogicalConfig(
OUT PLOG_CONF plogconf,
IN DEVINST dnDevInst,
IN BOOL bCreate)
{
CONFIGRET cr;
// Get the logical config for this device instance
cr = CM_Get_First_Log_Conf(plogconf, dnDevInst, BOOT_LOG_CONF);
if (CR_SUCCESS != cr && bCreate)
{
// It seems we couldn't get a logical config. So create
// an empty one.
cr = CM_Add_Empty_Log_Conf(plogconf, dnDevInst, LCPRI_NORMAL, BOOT_LOG_CONF);
}
return cr;
}
/*----------------------------------------------------------
Purpose: This function returns the modem detection signature
of the given DeviceInfoData.
Returns: TRUE on success
Cond: --
*/
BOOL
PUBLIC
CplDiGetDetectSignature(
IN HDEVINFO hdi,
IN PSP_DEVINFO_DATA pdevData,
OUT PMODEM_DETECT_SIG pmds)
{
BOOL bRet;
#ifndef PROFILE_MASSINSTALL
DBG_ENTER(CplDiGetDetectSignature);
#endif
ASSERT(hdi && INVALID_HANDLE_VALUE != hdi);
ASSERT(pdevData);
ASSERT(pmds);
if (sizeof(*pmds) != pmds->cbSize)
{
bRet = FALSE;
SetLastError(ERROR_INVALID_PARAMETER);
}
else
{
CONFIGRET cr;
LOG_CONF logconf;
// Get the logical config for this device instance
cr = CMGetLogicalConfig(&logconf, pdevData->DevInst, FALSE);
if (CR_SUCCESS == cr)
{
RESOURCEID restype;
RES_DES resdes;
PCS_DES pcsdes;
DWORD cbSizeT;
// Get the resource descriptor handle for this logical config
//
// NOTE: unlike the name of this function, this function will
// return the same resdes on repeated calls. So do not
// call this function with the expectation that it will
// eventually return "no more resdes values".
//
cr = CM_Get_Next_Res_Des(&resdes, logconf, ResType_ClassSpecific,
&restype, 0);
if (CR_SUCCESS == cr)
{
// Get the size of the detection signature
cr = CM_Get_Res_Des_Data_Size(&cbSizeT, resdes, 0);
if (CR_SUCCESS == cr)
{
if (0 == cbSizeT)
{
// This should never happen
ASSERT(0);
cr = CR_FAILURE;
SetLastError(ERROR_INVALID_DATA);
}
else
{
// Allocate a temporary buffer
pcsdes = (PCS_DES)LocalAlloc(LPTR, cbSizeT);
if ( !pcsdes )
{
// Out of memory
cr = CR_OUT_OF_MEMORY;
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
}
else
{
// Get the data
cr = CM_Get_Res_Des_Data(resdes, pcsdes, cbSizeT, 0);
if (CR_SUCCESS == cr)
{
BltByte(pmds, pcsdes->CSD_Signature, pmds->cbSize);
}
LocalFree(LOCALOF(pcsdes));
}
}
}
}
}
bRet = (CR_SUCCESS == cr);
}
#ifndef PROFILE_MASSINSTALL
DBG_EXIT_BOOL_ERR(CplDiGetDetectSignature, bRet);
#endif
return bRet;
}
/*----------------------------------------------------------
Purpose: This function sets the modem detection signature
of the given DeviceInfoData.
Returns: TRUE on success
Cond: --
*/
BOOL
PUBLIC
CplDiSetDetectSignature(
IN HDEVINFO hdi,
IN PSP_DEVINFO_DATA pdevData,
IN PMODEM_DETECT_SIG pmds)
{
BOOL bRet;
#ifndef PROFILE_MASSINSTALL
DBG_ENTER(CplDiSetDetectSignature);
#endif
ASSERT(hdi && INVALID_HANDLE_VALUE != hdi);
ASSERT(pdevData);
ASSERT(pmds);
if ( !pmds || sizeof(*pmds) != pmds->cbSize || !pdevData )
{
bRet = FALSE;
SetLastError(ERROR_INVALID_PARAMETER);
}
else
{
CONFIGRET cr;
LOG_CONF logconf;
cr = CMGetLogicalConfig(&logconf, pdevData->DevInst, TRUE);
if (CR_SUCCESS != cr)
{
bRet = FALSE;
}
else
{
// (The size of the detection signature is our detection signature
// plus the size of the CS_DES header)
PCS_DES pcsdes;
DWORD cbAlloc = sizeof(*pcsdes) + pmds->cbSize;
pcsdes = (PCS_DES)LocalAlloc(LPTR, cbAlloc);
if ( !pcsdes )
{
bRet = FALSE;
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
}
else
{
RES_DES resdes;
RESOURCEID restype;
pcsdes->CSD_SignatureLength = pmds->cbSize;
BltByte(&pcsdes->CSD_ClassGuid, g_pguidModem, sizeof(GUID));
BltByte(pcsdes->CSD_Signature, pmds, pmds->cbSize);
// Set the resource description. Adding a resource
// description when one already exists will fail. So
// first try to add one. If that fails, get an existing
// resource description so we can modify it.
//
// BUGBUG (scotth):
// You'd think we'd do the opposite -- ie, if "get" fails,
// try "adding". It turns out CM_Get_Next_Res_Des
// always returns success even when nothing is there.
// This is by design for SUR. We'll want to change
// this later, because we won't want to continually
// add resource descriptions -- we'll want to replace
// it.
// Did we fail to add a new resource description?
cr = CM_Add_Res_Des(&resdes,
logconf,
ResType_ClassSpecific,
pcsdes,
cbAlloc - sizeof(pcsdes->CSD_Signature),
0);
if (CR_SUCCESS != cr)
{
// Yes; get the existing one
cr = CM_Get_Next_Res_Des(&resdes, logconf, ResType_ClassSpecific,
&restype, 0);
if (CR_SUCCESS == cr)
{
// Modify it
cr = CM_Modify_Res_Des(&resdes,
resdes,
ResType_ClassSpecific,
pcsdes,
cbAlloc - sizeof(pcsdes->CSD_Signature),
0);
}
}
bRet = (CR_SUCCESS == cr);
if (bRet)
{
SetLastError(NO_ERROR);
}
LocalFree(LOCALOF(pcsdes));
}
}
}
#ifndef PROFILE_MASSINSTALL
DBG_EXIT_BOOL_ERR(CplDiSetDetectSignature, bRet);
#endif
return bRet;
}
/*----------------------------------------------------------
Purpose: Take a hardware ID and copy it to the supplied buffer.
This function changes all backslashes to ampersands.
Returns: --
Cond: --
*/
BOOL
PUBLIC
CplDiCopyScrubbedHardwareID(
OUT LPTSTR pszBuf,
IN LPCTSTR pszIDList, // Multi string
IN DWORD cbSize)
{
BOOL bRet;
LPTSTR psz;
LPCTSTR pszID;
BOOL bCopied;
ASSERT(pszBuf);
ASSERT(pszIDList);
bCopied = FALSE;
bRet = TRUE;
// Choose the first, best compatible ID. If we cannot find
// one, choose the first ID, and scrub it so it doesn't have
// any backslahes.
for (pszID = pszIDList; 0 != *pszID; pszID += lstrlen(pszID) + 1)
{
// Is the buffer big enough?
if (CbFromCch(lstrlen(pszID)) >= cbSize)
{
// No
bRet = FALSE;
SetLastError(ERROR_INSUFFICIENT_BUFFER);
break;
}
else
{
// Yes; are there any backslashes?
for (psz = (LPTSTR)pszID; 0 != *psz; psz = CharNext(psz))
{
if ('\\' == *psz)
{
break;
}
}
if (0 == *psz)
{
// No; use this ID
lstrcpy(pszBuf, pszID);
bCopied = TRUE;
break;
}
}
}
// Was an ID found in the list that does not have a backslash?
if (bRet && !bCopied)
{
// No; use the first one and scrub it.
lstrcpy(pszBuf, pszIDList);
// Clean up the hardware ID. Some hardware IDs may
// have an additional level to them (eg, PCMCIA\xxxxxxx).
// We must change this sort of ID to PCMCIA&xxxxxxx.
for (psz = pszBuf; 0 != *psz; psz = CharNext(psz))
{
if ('\\' == *psz)
{
*psz = '&';
}
}
}
return bRet;
}
/*----------------------------------------------------------
Purpose: This function returns the rank-0 (the first) hardware
ID of the given DriverInfoData.
If no DriverInfoData is provided, this function will
use the selected driver. If there is no selected
driver, this function fails.
Returns: TRUE on success
FALSE if the buffer is too small or another error
Cond: --
*/
BOOL
PUBLIC
CplDiGetHardwareID(
IN HDEVINFO hdi,
IN PSP_DEVINFO_DATA pdevData, OPTIONAL
IN PSP_DRVINFO_DATA pdrvData, OPTIONAL
OUT LPTSTR pszHardwareIDBuf,
IN DWORD cbSize,
OUT LPDWORD pcbSizeOut) OPTIONAL
{
BOOL bRet;
PSP_DRVINFO_DETAIL_DATA pdrvDetail;
SP_DRVINFO_DATA drvData;
DWORD cbSizeT;
#ifndef PROFILE_MASSINSTALL
DBG_ENTER(CplDiGetHardwareID);
#endif
ASSERT(hdi && INVALID_HANDLE_VALUE != hdi);
ASSERT(pszHardwareIDBuf);
if ( !pdrvData )
{
pdrvData = &drvData;
drvData.cbSize = sizeof(drvData);
bRet = CplDiGetSelectedDriver(hdi, pdevData, &drvData);
}
else
{
bRet = TRUE;
}
if (bRet)
{
// Get the driver detail so we can get the HardwareID of
// the selected driver
CplDiGetDriverInfoDetail(hdi, pdevData, pdrvData, NULL, 0, &cbSizeT);
ASSERT(0 < cbSizeT);
pdrvDetail = (PSP_DRVINFO_DETAIL_DATA)LocalAlloc(LPTR, cbSizeT);
if ( !pdrvDetail )
{
// Out of memory
bRet = FALSE;
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
}
else
{
pdrvDetail->cbSize = sizeof(*pdrvDetail);
bRet = CplDiGetDriverInfoDetail(hdi, pdevData, pdrvData, pdrvDetail,
cbSizeT, NULL);
if (bRet)
{
// Is the buffer big enough?
bRet = CplDiCopyScrubbedHardwareID(pszHardwareIDBuf, pdrvDetail->HardwareID, cbSize);
if (pcbSizeOut)
{
// Return the required size
*pcbSizeOut = CbFromCch(lstrlen(pdrvDetail->HardwareID));
}
}
LocalFree(LOCALOF(pdrvDetail));
}
}
#ifndef PROFILE_MASSINSTALL
DBG_EXIT_BOOL_ERR(CplDiGetHardwareID, bRet);
#endif
return bRet;
}
/*----------------------------------------------------------
Purpose: Creates a DeviceInfoData for a modem. This function is
used when the caller has a DeviceInfoSet and a selected
driver from the global class driver list, but no real
DeviceInfoData in the device-set.
Returns: TRUE on success
Cond: --
*/
BOOL
PRIVATE
CplDiCreateInheritDeviceInfo(
IN HDEVINFO hdi,
IN PSP_DEVINFO_DATA pdevData, OPTIONAL
IN HWND hwndOwner, OPTIONAL
OUT PSP_DEVINFO_DATA pdevDataOut)
{
BOOL bRet;
SP_DRVINFO_DATA drvData;
TCHAR szHardwareID[MAX_BUF_MED];
DBG_ENTER(CplDiCreateInheritDeviceInfo);
ASSERT(hdi && INVALID_HANDLE_VALUE != hdi);
ASSERT(pdevDataOut);
// Get the selected driver
drvData.cbSize = sizeof(drvData);
bRet = CplDiGetSelectedDriver(hdi, pdevData, &drvData);
if (bRet)
{
// Was a window owner supplied?
if (NULL == hwndOwner)
{
// No; use the window owner of the DeviceInfoData to be cloned.
SP_DEVINSTALL_PARAMS devParams;
devParams.cbSize = sizeof(devParams);
CplDiGetDeviceInstallParams(hdi, pdevData, &devParams);
hwndOwner = devParams.hwndParent;
}
// Get the hardware ID
bRet = CplDiGetHardwareID(hdi, pdevData, &drvData, szHardwareID, sizeof(szHardwareID), NULL);
// (Our buffer should be big enough)
ASSERT(bRet);
if (bRet)
{
// Create a DeviceInfoData. The Device Instance ID will be
// something like: Root\UNIMODEMxxxxxx\0000. We just supply
// the UNIMODEMxxxxx, which is the hardware ID. The device
// instance will inherit the driver settings of the global
// class driver list.
bRet = CplDiCreateDeviceInfo(hdi, szHardwareID, g_pguidModem,
drvData.Description, hwndOwner,
DICD_GENERATE_ID | DICD_INHERIT_CLASSDRVS,
pdevDataOut);
}
}
DBG_EXIT_BOOL_ERR(CplDiCreateInheritDeviceInfo, bRet);
return bRet;
}
/*----------------------------------------------------------
Purpose: Creates a device instance that is compatible with the
given hardware ID.
This function can also obtain a device description of
the device instance.
If there is no compatible device, this function
returns FALSE.
Returns: see above
Cond: --
*/
BOOL
PUBLIC
CplDiCreateCompatibleDeviceInfo(
IN HDEVINFO hdi,
IN LPCTSTR pszHardwareID,
IN LPCTSTR pszDeviceDesc, OPTIONAL
OUT LPTSTR pszDeviceDescBuf, OPTIONAL
IN DWORD cchBuf, OPTIONAL
OUT PSP_DEVINFO_DATA pdevDataOut)
{
BOOL bRet;
DBG_ENTER(CplDiCreateCompatibleDeviceInfo);
ASSERT(hdi && INVALID_HANDLE_VALUE != hdi);
ASSERT(pszHardwareID);
ASSERT(pdevDataOut);
// Create a phantom device instance
bRet = CplDiCreateDeviceInfo(hdi, pszHardwareID, g_pguidModem,
pszDeviceDesc, NULL, DICD_GENERATE_ID,
pdevDataOut);
if (bRet)
{
SP_DEVINSTALL_PARAMS devParams;
TCHAR szHardwareID[MAX_BUF_MED];
int cch = lstrlen(pszHardwareID);
// Set the flag to focus on only classes that pertain to
// modems. This will keep CplDiBuildDriverInfoList from
// slowing down any further once more INF files are added.
//
devParams.cbSize = sizeof(devParams);
if (CplDiGetDeviceInstallParams(hdi, pdevDataOut, &devParams))
{
// Specify using our GUID to make things a little faster.
SetFlag(devParams.FlagsEx, DI_FLAGSEX_USECLASSFORCOMPAT);
// Set the Select Device parameters
CplDiSetDeviceInstallParams(hdi, pdevDataOut, &devParams);
}
// Set the HardwareID so some sort of compatible driver list
// can be built. Don't forget the szHardwareID is a
// multi-string, so it needs the double-null termination.
lstrcpyn(szHardwareID, pszHardwareID, SIZECHARS(szHardwareID));
szHardwareID[cch+1] = 0; // second null terminator
bRet = CplDiSetDeviceRegistryProperty(hdi, pdevDataOut,
SPDRP_HARDWAREID,
(CONST BYTE *)szHardwareID,
CbFromCch(cch+2));
if (bRet)
{
// Build the compatible driver list
bRet = CplDiBuildDriverInfoList(hdi, pdevDataOut, SPDIT_COMPATDRIVER);
if (bRet)
{
SP_DRVINFO_DATA drvDataEnum;
// Use the first driver as the compatible driver.
drvDataEnum.cbSize = sizeof(drvDataEnum);
bRet = CplDiEnumDriverInfo(hdi, pdevDataOut,
SPDIT_COMPATDRIVER, 0,
&drvDataEnum);
if (bRet)
{
// Set the first driver as the selected driver
bRet = CplDiSetSelectedDriver(hdi, pdevDataOut, &drvDataEnum);
if (bRet)
{
if ( !pszDeviceDesc )
{
// Set the device description now that we
// have one
CplDiSetDeviceRegistryProperty(hdi, pdevDataOut,
SPDRP_DEVICEDESC, (LPBYTE)drvDataEnum.Description,
CbFromCch(lstrlen(drvDataEnum.Description)+1));
}
if (pszDeviceDescBuf)
{
// Copy the device description for a return value
lstrcpyn(pszDeviceDescBuf, drvDataEnum.Description, cchBuf);
}
}
}
}
}
// Did something fail above?
if ( !bRet )
{
// Yes; delete the device info we just created
CplDiDeleteDeviceInfo(hdi, pdevDataOut);
}
}
DBG_EXIT_BOOL_ERR(CplDiCreateCompatibleDeviceInfo, bRet);
return bRet;
}
/*----------------------------------------------------------
Purpose: This function sets the integer in the given array
that is indexed by the numeric value of the friendly
name instance to TRUE.
Returns: TRUE on success
FALSE otherwise
Cond: --
*/
BOOL
PUBLIC
CplDiRecordNameInstance(
IN LPCTSTR pszFriendlyName,
IN OUT WORD FAR * lpwNameArray)
{
BOOL bRet = FALSE;
LPTSTR szInstance, psz;
int iInstance, ii;
ASSERT(pszFriendlyName);
ASSERT(*pszFriendlyName);
if (szInstance = AnsiRChr(pszFriendlyName, '#'))
{
szInstance = CharNext(szInstance);
if (*szInstance == 0)
return FALSE;
// Make sure that everything following '#' is numeric.
for (psz = szInstance; *psz; psz = CharNext(psz))
{
ii = (int)*psz;
if (ii < '0' || ii > '9')
{
goto exit;
}
}
// Have an instance number on the friendly name. Record it.
bRet = AnsiToInt(szInstance, &iInstance);
if (!bRet)
{
TRACE_MSG(TF_ERROR, "AnsiToInt() failed");
return FALSE;
}
if (iInstance >= MAX_INSTALLATIONS - 1)
{
TRACE_MSG(TF_ERROR, "Too many drivers installed.");
return FALSE;
}
lpwNameArray[iInstance] = TRUE;
return TRUE;
}
exit:
lpwNameArray[1] = TRUE;
return TRUE;
}
/*----------------------------------------------------------
Purpose: This function
Returns: FALSE on error - couldn't mark for mass install.
TRUE if successful.
Cond: --
*/
BOOL
PUBLIC
CplDiMarkForMassInstall(
IN HDEVINFO hdi,
IN PSP_DEVINFO_DATA pdevData,
IN PSP_DRVINFO_DATA pdrvData)
{
BOOL bRet = FALSE; // assume failure
SP_DRVINSTALL_PARAMS drvParams;
drvParams.cbSize = sizeof(drvParams);
bRet = CplDiGetDriverInstallParams(hdi, pdevData, pdrvData, &drvParams);
if (!bRet)
{
TRACE_MSG(TF_ERROR, "CplDiGetDriverInstallParams() failed: %#08lx", GetLastError());
goto exit;
}
drvParams.PrivateData = (DWORD)&g_wUsedNameArray[0];
CplDiSetDriverInstallParams(hdi, pdevData, pdrvData, &drvParams);
if (!bRet)
{
TRACE_MSG(TF_ERROR, "CplDiSetDriverInstallParams() failed: %#08lx", GetLastError());
goto exit;
}
CplDiMarkModem(hdi, pdevData, MARKF_MASS_INSTALL);
bRet = TRUE;
exit:
return bRet;
}
/*----------------------------------------------------------
Purpose: This function processes the set of modems that are
already installed looking for a duplicate of the
selected driver. Ports on which the device has been
installed previously are removed from the given
ports list. A list is created of the friendly name
instance numbers that are already in use. Selected
driver is marked for mass install barring fatal
error.
NOTE: This function will return FALSE and avoid the mass
install at the slighest hint of an error condition.
Mass install is just an optimization - if it can't
be done successfully it shouldn't be attempted.
Returns: TRUE if successful. Selected Driver marked for mass
install (whether or not there were dups).
FALSE on fatal error - not able to process for dups.
Cond: --
*/
BOOL
PUBLIC
CplDiPreProcessDups(
IN HDEVINFO hdi,
IN HWND hwndOwner, OPTIONAL
IN OUT LPTSTR FAR * ppszPortList, // Multi-string
OUT PSP_DEVINFO_DATA pdevData,
OUT DWORD FAR * lpcDups,
OUT DWORD FAR * lpdwFlags)
{
BOOL bRet, bHaveDev;
SP_DEVINFO_DATA devDataEnum;
SP_DRVINFO_DATA drvData;
HDEVINFO hdiClass = NULL;
HKEY hkey = NULL;
TCHAR szDescrNew[LINE_LEN];
TCHAR szDescrEnum[LINE_LEN];
TCHAR szMfactNew[LINE_LEN];
TCHAR szMfactEnum[LINE_LEN];
TCHAR szProvNew[LINE_LEN];
TCHAR szProvEnum[LINE_LEN];
TCHAR szOnPort[LINE_LEN];
TCHAR szFriendlyName[LINE_LEN];
DWORD iIndex, cbData, iArrIdx, cbPortList;
LONG lErr;
WORD cPorts, wPortIndexArray[MAX_INSTALLATIONS];
LPTSTR pszPort, pszNewPort;
LPTSTR pszNewPortList = NULL;
DBG_ENTER(CplDiPreProcessDups);
ASSERT(lpcDups);
ASSERT(lpdwFlags);
*lpcDups = 0;
// Get the DEVINFO_DATA for the selected driver and retrieve it's
// identifying info (description, manufacturer, provider).
// We have a DeviceInfoSet and a selected driver. But we have no
// real DeviceInfoData. Given the DeviceInfoSet, the selected driver,
// and the global class driver list, ....
pdevData->cbSize = sizeof(*pdevData);
bRet = CplDiCreateInheritDeviceInfo(hdi, NULL, hwndOwner, pdevData);
if (!bRet)
{
TRACE_MSG(TF_ERROR, "CplDiCreateInheritDeviceInfo() failed: %#08lx", GetLastError());
ASSERT(0);
goto exit;
}
// Get the DRVINFO_DATA for the selected driver.
drvData.cbSize = sizeof(drvData);
bRet = CplDiGetSelectedDriver(hdi, pdevData, &drvData);
if (!bRet)
{
TRACE_MSG(TF_ERROR, "CplDiGetSelectedDriver() failed: %#08lx", GetLastError());
ASSERT(0);
goto exit;
}
// Assume failure at some point below.
bRet = FALSE;
hdiClass = CplDiGetClassDevs(g_pguidModem, NULL, NULL, 0);
if (hdiClass == INVALID_HANDLE_VALUE)
{
TRACE_MSG(TF_ERROR, "CplDiGetClassDevs() failed: %#08lx", GetLastError());
hdiClass = NULL;
goto exit;
}
lstrcpyn(szDescrNew, drvData.Description, SIZECHARS(szDescrNew));
lstrcpyn(szMfactNew, drvData.MfgName, SIZECHARS(szMfactNew));
lstrcpyn(szProvNew, drvData.ProviderName, SIZECHARS(szProvNew));
if (!szDescrNew[0])
{
TRACE_MSG(TF_ERROR, "FAILED to get description for selected driver.");
goto exit;
}
ZeroMemory(wPortIndexArray, sizeof(wPortIndexArray));
ZeroMemory(g_wUsedNameArray, sizeof(g_wUsedNameArray));
// Look through all installed modem devices for instances
// of the selected driver.
devDataEnum.cbSize = sizeof(devDataEnum);
for (iIndex = 0, iArrIdx = 0;
CplDiEnumDeviceInfo(hdiClass, iIndex, &devDataEnum);
iIndex++)
{
hkey = CplDiOpenDevRegKey(hdiClass, &devDataEnum, DICS_FLAG_GLOBAL, 0,
DIREG_DRV, KEY_READ);
if (hkey == INVALID_HANDLE_VALUE)
{
TRACE_MSG(TF_ERROR, "CplDiOpenDevRegKey() failed: %#08lx", GetLastError());
ASSERT(0);
hkey = NULL;
goto exit;
}
// The driver description should always exist in the driver key.
cbData = sizeof(szDescrEnum);
lErr = RegQueryValueEx(hkey, REGSTR_VAL_DRVDESC, NULL, NULL,
(LPBYTE)szDescrEnum, &cbData);
if (lErr != ERROR_SUCCESS)
{
TRACE_MSG(TF_ERROR,
"Failed to read driver description from REG driver node. (%#08lx)",
lErr);
ASSERT(0);
goto exit;
}
if (!IsSzEqual(szDescrNew, szDescrEnum))
continue;
// The manufacturer may be a NULL string, but it should always exist
// in the driver key.
cbData = sizeof(szMfactEnum);
lErr = RegQueryValueEx(hkey, c_szManufacturer, NULL, NULL,
(LPBYTE)szMfactEnum, &cbData);
if (lErr != ERROR_SUCCESS)
{
TRACE_MSG(TF_ERROR,
"Failed to read manufacturer from REG driver node. (%#08lx)",
lErr);
ASSERT(0);
goto exit;
}
if (!IsSzEqual(szMfactNew, szMfactEnum))
continue;
// The provider may be a NULL string or it may not be in the registry.
cbData = sizeof(szProvEnum);
lErr = RegQueryValueEx(hkey, REGSTR_VAL_PROVIDER_NAME, NULL, NULL,
(LPBYTE)szProvEnum, &cbData);
if (lErr == ERROR_SUCCESS)
{
if (!IsSzEqual(szProvNew, szProvEnum))
continue;
}
else
{
if (!IsSzEqual(szProvNew, TEXT("\0")))
continue;
}
// The description, manufacturer, and provider strings matched those
// of the new modem. We've found a previously installed instance.
(*lpcDups)++;
// See what port it's on so it can be removed from the install ports
// list.
cbData = sizeof(szOnPort);
lErr = RegQueryValueEx(hkey, c_szAttachedTo, NULL, NULL,
(LPBYTE)szOnPort, &cbData);
if (lErr != ERROR_SUCCESS)
{
TRACE_MSG(TF_ERROR,
"Failed to read port from REG driver node. (%#08lx)",
lErr);
ASSERT(0);
goto exit;
}
// Try to find this port in the install ports list.
for (pszPort = *ppszPortList, cPorts = 1;
*pszPort != 0;
pszPort += lstrlen(pszPort) + 1, cPorts++)
{
// If it's already on a port that we're trying to (re)install
// it on, remember the portlist index so it can be removed
// later. Remember the index as *1-based* so that the array of
// saved indices can be processed by stopping at 0.
if (IsSzEqual(szOnPort, pszPort))
{
wPortIndexArray[iArrIdx] = cPorts;
if (iArrIdx == MAX_INSTALLATIONS - 2)
{
TRACE_MSG(TF_ERROR, "Too many drivers installed.");
goto exit;
}
iArrIdx++;
break;
}
}
// Read the friendly name and add it to the list of used names.
cbData = sizeof(szFriendlyName);
lErr = RegQueryValueEx(hkey, c_szFriendlyName, NULL, NULL,
(LPBYTE)szFriendlyName, &cbData);
if (lErr != ERROR_SUCCESS)
{
TRACE_MSG(TF_ERROR,
"Failed to read friendly name from REG driver node. (%#08lx)",
lErr);
ASSERT(0);
goto exit;
}
if (!CplDiRecordNameInstance(szFriendlyName, g_wUsedNameArray))
{
TRACE_MSG(TF_ERROR, "CplDiRecordNameInstance() failed.");
goto exit;
}
}
// Check for failed CplDiEnumDeviceInfo().
if ((lErr = GetLastError()) != ERROR_NO_MORE_ITEMS)
{
TRACE_MSG(TF_ERROR, "CplDiEnumDeviceInfo() failed: %#08lx", lErr);
ASSERT(0);
goto exit;
}
// If the device is already installed on ports that the user has
// selected to install on, create and return a new ports list that
// doesn't contain those duplicate ports.
if (wPortIndexArray[0])
{
// Figure out the size of the passed in ports list and allocate
// a new list of the same size.
for (pszPort = *ppszPortList, cbPortList = 0;
*pszPort != 0;
pszPort += lstrlen(pszPort) + 1)
{
cbPortList += CbFromCch(lstrlen(pszPort)+1);
}
cbPortList += CbFromCch(1); // double null terminator
if (!(pszNewPortList = (LPTSTR)LocalAlloc(LPTR, cbPortList)))
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
goto exit;
}
// Copy the old ports list to the new one, removing any ports that
// the device is already installed on.
for (pszPort = *ppszPortList, pszNewPort = pszNewPortList, iIndex = 1;
*pszPort != 0;
pszPort += lstrlen(pszPort) + 1, iIndex++)
{
// If this index isn't in the "forget it" list, write the port
// to the new ports list. NOTE: indices stored in the array
// are *not* in sequential order, so we search the whole list.
bHaveDev = FALSE;
for (iArrIdx = 0; wPortIndexArray[iArrIdx]; iArrIdx++)
{
if (wPortIndexArray[iArrIdx] == iIndex)
{
bHaveDev = TRUE;
break;
}
}
if (!bHaveDev)
{
lstrcpy(pszNewPort, pszPort);
pszNewPort += lstrlen(pszNewPort) + 1;
}
}
// NOTE: double NULL terminator on pszNewPortList is taken care of
// by virtue of the fact that we *shortened* the list.
// Free the old ports list and return the new one.
CatMultiString(ppszPortList, NULL);
}
// Pre-processing for duplicates has succeeded so this installation
// will be treated like a mass install (even if the number of ports
// remaining is < MIN_MULTIPORT).
bRet = CplDiMarkForMassInstall(hdi, pdevData, &drvData);
if (bRet)
{
SetFlag(*lpdwFlags, IMF_MASS_INSTALL);
if (pszNewPortList)
*ppszPortList = pszNewPortList;
}
exit:
if (hdiClass)
{
CplDiDestroyDeviceInfoList(hdiClass);
}
if (hkey)
{
RegCloseKey(hkey);
}
if (!bRet)
{
CplDiDeleteDeviceInfo(hdi, pdevData);
}
DBG_EXIT_BOOL_ERR(CplDiPreProcessDups, bRet);
return bRet;
}
/*----------------------------------------------------------
Purpose: Creates a device instance for a modem that includes
the entire class driver list. This function then
creates additional device instances that are cloned
quickly from the original
Returns:
Cond: --
*/
BOOL
PUBLIC
CplDiBuildModemDriverList(
IN HDEVINFO hdi,
IN PSP_DEVINFO_DATA pdevData)
{
#pragma data_seg(DATASEG_READONLY)
static TCHAR const FAR c_szProvider[] = REGSTR_VAL_PROVIDER_NAME; // TEXT("ProviderName");
#pragma data_seg()
BOOL bRet;
SP_DRVINFO_DATA drvDataEnum;
SP_DEVINSTALL_PARAMS devParams;
DBG_ENTER(CplDiBuildModemDriverList);
ASSERT(hdi && INVALID_HANDLE_VALUE != hdi);
ASSERT(pdevData);
// Build a global class driver list
// Set the flag to focus on only classes that pertain to
// modems. This will keep CplDiBuildDriverInfoList from
// slowing down any further once more INF files are added.
//
devParams.cbSize = sizeof(devParams);
if (CplDiGetDeviceInstallParams(hdi, NULL, &devParams))
{
// Specify using our GUID to make things a little faster.
SetFlag(devParams.FlagsEx, DI_FLAGSEX_USECLASSFORCOMPAT);
// Set the Select Device parameters
CplDiSetDeviceInstallParams(hdi, NULL, &devParams);
}
bRet = CplDiBuildDriverInfoList(hdi, NULL, SPDIT_CLASSDRIVER);
if (bRet)
{
SP_DRVINFO_DATA drvData;
TCHAR szDescription[LINE_LEN];
TCHAR szMfgName[LINE_LEN];
TCHAR szProviderName[LINE_LEN];
// Get the information needed to search for a matching driver
// in the class driver list. We need three strings:
//
// Description
// MfgName
// ProviderName (optional)
//
// The Description and MfgName are properties of the device
// (SPDRP_DEVICEDESC and SPDRP_MFG). The ProviderName is
// stored in the driver key.
// Try getting this info from the selected driver first.
// Is there a selected driver?
drvData.cbSize = sizeof(drvData);
bRet = CplDiGetSelectedDriver(hdi, pdevData, &drvData);
if (bRet)
{
// Yes
lstrcpyn(szMfgName, drvData.MfgName, SIZECHARS(szMfgName));
lstrcpyn(szDescription, drvData.Description, SIZECHARS(szDescription));
lstrcpyn(szProviderName, drvData.ProviderName, SIZECHARS(szProviderName));
}
else
{
// No; grovel in the driver key
DWORD dwType;
HKEY hkey;
hkey = CplDiOpenDevRegKey(hdi, pdevData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_READ);
if (INVALID_HANDLE_VALUE == hkey)
{
bRet = FALSE;
}
else
{
DWORD cbData = sizeof(szProviderName);
// Get the provider name
*szProviderName = 0;
RegQueryValueEx(hkey, c_szProvider, NULL, NULL,
(LPBYTE)szProviderName, &cbData);
RegCloseKey(hkey);
// Get the device description and manufacturer
bRet = CplDiGetDeviceRegistryProperty(hdi, pdevData,
SPDRP_DEVICEDESC, &dwType, (LPBYTE)szDescription,
sizeof(szDescription), NULL);
if (bRet)
{
bRet = CplDiGetDeviceRegistryProperty(hdi, pdevData,
SPDRP_MFG, &dwType, (LPBYTE)szMfgName,
sizeof(szMfgName), NULL);
}
}
}
// Could we get the search criteria?
if (bRet)
{
// Yes
DWORD iIndex = 0;
bRet = FALSE; // Assume there is no match
// Find the equivalent selected driver in this new
// compatible driver list, and set it as the selected
// driver for this new DeviceInfoData.
drvDataEnum.cbSize = sizeof(drvDataEnum);
while (CplDiEnumDriverInfo(hdi, NULL, SPDIT_CLASSDRIVER,
iIndex++, &drvDataEnum))
{
// Is this driver a match?
if (IsSzEqual(szDescription, drvDataEnum.Description) &&
IsSzEqual(szMfgName, drvDataEnum.MfgName) &&
(0 == *szProviderName ||
IsSzEqual(szProviderName, drvDataEnum.ProviderName)))
{
// Yes; set this as the selected driver
bRet = CplDiSetSelectedDriver(hdi, NULL, &drvDataEnum);
break;
}
}
}
}
DBG_EXIT_BOOL_ERR(CplDiBuildModemDriverList, bRet);
return bRet;
}
/*----------------------------------------------------------
Purpose: Sets the modem detection signature (if there is one)
and registers the device instance.
Returns: TRUE on success
Cond: --
*/
BOOL
PUBLIC
CplDiRegisterModem(
IN HDEVINFO hdi,
IN PSP_DEVINFO_DATA pdevData,
IN PMODEM_DETECT_SIG pmds, OPTIONAL
IN BOOL bFindDups,
OUT PDETECTSIG_PARAMS pparams) OPTIONAL
{
BOOL bRet;
DBG_ENTER(CplDiRegisterModem);
ASSERT(hdi && INVALID_HANDLE_VALUE != hdi);
ASSERT(pdevData);
if (pmds)
{
// Set the detection signature
bRet = CplDiSetDetectSignature(hdi, pdevData, pmds);
}
else
{
bRet = TRUE;
}
if (bRet)
{
DWORD dwFlags = bFindDups ? SPRDI_FIND_DUPS : 0;
// Register the device so it is not a phantom anymore
#ifdef PROFILE_MASSINSTALL
TRACE_MSG(TF_GENERAL, "calling CplDiRegisterDeviceInfo() with SPRDI_FIND_DUPS = %#08lx", dwFlags);
#endif
bRet = CplDiRegisterDeviceInfo(hdi, pdevData, dwFlags,
DetectSig_Compare, pparams, NULL);
if ( !bRet )
{
TRACE_MSG(TF_ERROR, "Failed to register the Device Instance. Error=%#08lx.", GetLastError());
}
else
{
#ifdef PROFILE_MASSINSTALL
TRACE_MSG(TF_GENERAL, "Back from CplDiRegisterDeviceInfo().");
#endif
// Mark it so it will be installed
CplDiMarkModem(hdi, pdevData, MARKF_INSTALL);
}
}
DBG_EXIT_BOOL_ERR(CplDiRegisterModem, bRet);
return bRet;
}
/*----------------------------------------------------------
Purpose: Takes a device instance and properly installs it.
This function assures that the device has a selected
driver and a detection signature. It also registers
the device instance.
Returns: TRUE on success
Cond: --
*/
BOOL
PUBLIC
CplDiRegisterAndInstallModem(
IN HDEVINFO hdi,
IN HWND hwndOwner, OPTIONAL
IN PSP_DEVINFO_DATA pdevData, OPTIONAL
IN LPCTSTR pszPort,
IN DWORD dwFlags)
{
BOOL bRet;
SP_DRVINFO_DATA drvData;
SP_DEVINFO_DATA devData;
int id;
DBG_ENTER(CplDiRegisterAndInstallModem);
ASSERT(hdi && INVALID_HANDLE_VALUE != hdi);
ASSERT(pszPort);
// Create the devinfo data if it wasn't given.
if (!pdevData)
{
// We have a DeviceInfoSet and a selected driver. But we have no
// real DeviceInfoData. Given the DeviceInfoSet, the selected driver,
// the the global class driver list, create a DeviceInfoData that
// we can really install.
devData.cbSize = sizeof(devData);
bRet = CplDiCreateInheritDeviceInfo(hdi, NULL, hwndOwner, &devData);
if (bRet && IsFlagSet(dwFlags, IMF_MASS_INSTALL))
{
drvData.cbSize = sizeof(drvData);
CplDiGetSelectedDriver(hdi, NULL, &drvData);
CplDiMarkForMassInstall(hdi, &devData, &drvData);
}
}
else
{
devData = *pdevData; // (to avoid changing all references herein)
bRet = TRUE;
}
if ( !bRet )
{
// Some error happened. Tell the user.
id = MsgBox(g_hinst,
hwndOwner,
MAKEINTRESOURCE(IDS_ERR_CANT_ADD_MODEM2),
MAKEINTRESOURCE(IDS_CAP_MODEMSETUP),
NULL,
MB_OKCANCEL | MB_ICONINFORMATION);
if (IDCANCEL == id)
{
SetLastError(ERROR_CANCELLED);
}
}
else
{
TCHAR szHardwareID[MAX_BUF_MED];
MODEM_DETECT_SIG mds;
DWORD nErr = NO_ERROR;
// Get the hardwareID for the detection signature
bRet = CplDiGetHardwareID(hdi, &devData, NULL, szHardwareID,
sizeof(szHardwareID), NULL);
if (bRet)
{
// Register the device as a modem device
DETECTSIG_PARAMS dsparams;
BOOL bFindDups;
DetectSig_Init(&mds, 0, szHardwareID, pszPort);
// If this is the mass install case, then don't find duplicates.
// It takes too long. (The flag determines whether SPRDI_FIND_DUPS
// is passed to CplDiRegisterDeviceInfo()....)
bFindDups = IsFlagClear(dwFlags, IMF_MASS_INSTALL);
bRet = CplDiRegisterModem(hdi, &devData, &mds, bFindDups, &dsparams);
if ( !bRet )
{
SP_DRVINFO_DATA drvData;
nErr = GetLastError(); // Save the error
drvData.cbSize = sizeof(drvData);
CplDiGetSelectedDriver(hdi, &devData, &drvData);
// Is this a duplicate?
if (ERROR_DUPLICATE_FOUND == nErr)
{
// Yes
ASSERT(IsFlagSet(dsparams.dwMatchingMask, MDSM_HARDWAREID));
ASSERT(IsFlagSet(dsparams.dwMatchingMask, MDSM_PORT));
// A modem exactly like this is already installed on this
// port. Ask the user if she still wants to install.
if (IsFlagSet(dwFlags, IMF_CONFIRM))
{
if (IDYES == MsgBox(g_hinst,
hwndOwner,
MAKEINTRESOURCE(IDS_WRN_DUPLICATE_MODEM),
MAKEINTRESOURCE(IDS_CAP_MODEMSETUP),
NULL,
MB_YESNO | MB_ICONWARNING,
drvData.Description, mds.szPort))
{
// User wants to do it. Register without checking
// for duplicates
bRet = CplDiRegisterModem(hdi, &devData,
&mds, FALSE, NULL);
if ( !bRet )
{
goto WhineToUser;
}
}
}
}
else
{
// No; something else failed
TRACE_MSG(TF_ERROR, "CplDiRegisterModem() failed: %#08lx.", nErr);
WhineToUser:
id = MsgBox(g_hinst,
hwndOwner,
MAKEINTRESOURCE(IDS_ERR_REGISTER_FAILED),
MAKEINTRESOURCE(IDS_CAP_MODEMSETUP),
NULL,
MB_OKCANCEL | MB_ICONINFORMATION,
drvData.Description, mds.szPort);
if (IDCANCEL == id)
{
nErr = ERROR_CANCELLED;
}
}
}
if (bRet)
{
SP_DEVINSTALL_PARAMS devParams;
devParams.cbSize = sizeof(devParams);
// Any flags to set?
if (dwFlags && CplDiGetDeviceInstallParams(
hdi,
&devData,
&devParams
))
{
DWORD dwExtraMarkFlags = 0;
if (IsFlagSet(dwFlags, IMF_QUIET_INSTALL))
{
SetFlag(devParams.Flags, DI_QUIETINSTALL);
}
if (IsFlagSet(dwFlags, IMF_REGSAVECOPY))
{
dwExtraMarkFlags = MARKF_REGSAVECOPY;
}
else if (IsFlagSet(dwFlags, IMF_REGUSECOPY))
{
dwExtraMarkFlags = MARKF_REGUSECOPY;
}
if (dwExtraMarkFlags)
{
SetFlag(
devParams.ClassInstallReserved,
dwExtraMarkFlags
);
}
// If this is the mass install case, then speed up the call
// into CplDiInstallDevice() by avoiding re-enumeration.
if (IsFlagSet(dwFlags, IMF_MASS_INSTALL))
{
SetFlag(devParams.Flags, DI_DONOTCALLCONFIGMG);
}
CplDiSetDeviceInstallParams(hdi, &devData, &devParams);
}
// Install the modem
bRet = CplDiInstallModem(hdi, &devData, FALSE);
nErr = GetLastError();
}
}
// Did anything above fail?
if ( !bRet )
{
// Yes; clean up
CplDiDeleteDeviceInfo(hdi, &devData);
}
if (NO_ERROR != nErr)
{
// Set the last error to be what it really was
SetLastError(nErr);
}
}
DBG_EXIT_BOOL_ERR(CplDiRegisterAndInstallModem, bRet);
return bRet;
}
/*----------------------------------------------------------
Purpose: Warn the user about whether she needs to reboot
if any of the installed modems was marked as such.
Returns: --
Cond: --
*/
void
PRIVATE
WarnUserAboutReboot(
IN HDEVINFO hdi)
{
DWORD iDevice;
SP_DEVINFO_DATA devData;
SP_DEVINSTALL_PARAMS devParams;
// Enumerate all the DeviceInfoData elements in this device set
devData.cbSize = sizeof(devData);
devParams.cbSize = sizeof(devParams);
iDevice = 0;
//#ifdef INSTANT_DEVICE_ACTIVATION
// gDeviceFlags|= fDF_DEVICE_NEEDS_REBOOT;
//#endif // INSTANT_DEVICE_ACTIVATION
while (CplDiEnumDeviceInfo(hdi, iDevice++, &devData))
{
if (CplDiGetDeviceInstallParams(hdi, &devData, &devParams))
{
if (ReallyNeedsReboot(&devData, &devParams))
{
//#ifdef INSTANT_DEVICE_ACTIVATION
// gDeviceFlags|= fDF_DEVICE_NEEDS_REBOOT;
//#else //!INSTANT_DEVICE_ACTIVATION
// Yes; tell the user (once)
MsgBox(g_hinst,
devParams.hwndParent,
MAKEINTRESOURCE(IDS_WRN_REBOOT2),
MAKEINTRESOURCE(IDS_CAP_MODEMSETUP),
NULL,
MB_OK | MB_ICONINFORMATION);
//#endif // !INSTANT_DEVICE_ACTIVATION
break;
}
}
}
}
/*----------------------------------------------------------
Purpose: Takes a device instance and properly installs it.
This function assures that the device has a selected
driver and a detection signature. It also registers
the device instance.
The pszPort parameter is a multi-string (ie, double-
null termination). This specifies the port the
modem should be attached to. If there are multiple
ports specified, then this function creates device
instances for each port. However in the mass modem
install case, it will preprocess the ports list and
remove ports on which the selected modem is already
installed. This is done here because it's too
expensive (for many ports i.e. > 100) to turn on the
SPRDI_FIND_DUPS flag and let the setup api's do it.
The caller's ports list is *modified* in this case.
Returns: TRUE on success
Cond: --
*/
BOOL
APIENTRY
CplDiInstallModemFromDriver(
IN HDEVINFO hdi,
IN HWND hwndOwner, OPTIONAL
IN OUT LPTSTR FAR * ppszPortList, // Multi-string
IN DWORD dwFlags) // IMF_ bit field
{
BOOL bRet;
DBG_ENTER(CplDiInstallModemFromDriver);
ASSERT(hdi && INVALID_HANDLE_VALUE != hdi);
ASSERT(*ppszPortList);
try
{
LPCTSTR pszPort;
BOOL bSingleInstall = (0 == (*ppszPortList)[lstrlen(*ppszPortList) + 1]);
DWORD cPorts;
DWORD cFailedPorts = 0;
DWORD cSkippedPorts = 0;
TCHAR rgtchStatusTemplate[256];
DWORD cchStatusTemplate=0;
BOOL bFirstGood = TRUE;
SP_DEVINFO_DATA devData;
PSP_DEVINFO_DATA pdevData = NULL;
BOOL bAllDups = FALSE;
// Count the number of ports to check for the mass install case.
// Set a flag for CplDiRegisterAndInstallModem().
for (pszPort = *ppszPortList, cPorts = 0;
*pszPort != 0;
pszPort += lstrlen(pszPort) + 1)
{
cPorts++;
if (cPorts > MIN_MULTIPORT)
{
// This call sets up the mass install case if it succeeds.
if (CplDiPreProcessDups(hdi, hwndOwner, ppszPortList,
&devData, &cSkippedPorts, &dwFlags))
{
pdevData = &devData;
if ((*ppszPortList)[0] == 0)
bAllDups = TRUE;
}
break;
}
}
if ( !bSingleInstall && !bAllDups )
{
BOOL bRet = LoadString(
g_hinst,
IDS_INSTALL_STATUS,
rgtchStatusTemplate,
SIZECHARS(rgtchStatusTemplate)
);
if (bRet)
{
cchStatusTemplate = lstrlen(rgtchStatusTemplate);
}
SetFlag(dwFlags, IMF_QUIET_INSTALL);
ClearFlag(dwFlags, IMF_CONFIRM);
SetFlag(dwFlags, IMF_REGSAVECOPY);
{
DWORD PRIVATE RegDeleteKeyNT(HKEY, LPCTSTR);
LPCTSTR szREGCACHE =
REGSTR_PATH_SETUP TEXT("\\Unimodem\\RegCache");
RegDeleteKeyNT(HKEY_LOCAL_MACHINE, szREGCACHE);
}
}
// Install a device for each port in the port list
cPorts = 0;
for (pszPort = *ppszPortList;
0 != *pszPort;
pszPort += lstrlen(pszPort) + 1)
{
TCHAR rgtchStatus[256];
cPorts++;
#ifdef PROFILE_MASSINSTALL
g_dwTimeStartModemInstall = GetTickCount();
#endif
// "cchStatusTemplate+lstrlen(pszPort)" slightly overestimates
// the size of the formatted result, that's OK.
if ( cchStatusTemplate
&& (cchStatusTemplate+lstrlen(pszPort))<SIZECHARS(rgtchStatus))
{
wsprintf(rgtchStatus, rgtchStatusTemplate, pszPort);
Install_SetStatus(hwndOwner, rgtchStatus);
}
// Install the modem
// WARNING: if this call failed, pdevData has been deleted!
if (cPorts != 1)
pdevData = NULL;
bRet = CplDiRegisterAndInstallModem(hdi, hwndOwner, pdevData,
pszPort, dwFlags);
if ( !bRet )
{
DWORD dwErr = GetLastError();
cFailedPorts++;
if (ERROR_CANCELLED == dwErr)
{
// Stop because the user said so
break;
}
else if (ERROR_DUPLICATE_FOUND == dwErr)
{
cSkippedPorts++;
}
}
else
{
if (bFirstGood && !bSingleInstall)
{
// This is the 1st good install. From now on, specify the
// IMF_REGUSECOPY flag.
ClearFlag(dwFlags, IMF_REGSAVECOPY);
SetFlag(dwFlags, IMF_REGUSECOPY);
bFirstGood = FALSE;
}
}
#ifdef PROFILE_MASSINSTALL
TRACE_MSG(TF_GENERAL, "***--------- %lu ms to install ONE modem ---------***",
GetTickCount() - g_dwTimeStartModemInstall);
TRACE_MSG(TF_GENERAL, "***--------- %lu ms TOTAL time spent installing modems ---------***",
GetTickCount() - g_dwTimeBegin);
#endif
}
// ???: bRet could be either TRUE or FALSE here!!!
if (cPorts > cFailedPorts)
{
#ifdef PROFILE_MASSINSTALL
TRACE_MSG(TF_GENERAL, "*** Friendly Name generation took %lu ms out of %lu ms total install time",
g_dwTimeSpent, GetTickCount() - g_dwTimeBegin);
#endif
// At least some modems were installed
bRet = TRUE;
}
if (0 < cSkippedPorts && IsFlagClear(dwFlags, IMF_CONFIRM))
{
// Tell the user we skipped some ports
MsgBox(g_hinst,
hwndOwner,
MAKEINTRESOURCE(IDS_WRN_SKIPPED_PORTS),
MAKEINTRESOURCE(IDS_CAP_MODEMSETUP),
NULL,
MB_OK | MB_ICONINFORMATION);
}
// If this is the mass install case, then we have to assume that
// a reboot is necessary since we didn't allow cfgmgr32 to
// re-enumerate the installed drivers (because it takes too long).
if (IsFlagSet(dwFlags, IMF_MASS_INSTALL))
{
if (bRet) // something *was* installed
{
MsgBox(g_hinst,
hwndOwner,
MAKEINTRESOURCE(IDS_WRN_REBOOT2),
MAKEINTRESOURCE(IDS_CAP_MODEMSETUP),
NULL,
MB_OK | MB_ICONINFORMATION);
}
if (bAllDups)
{
bRet = TRUE;
}
}
else if (!bSingleInstall && (cSkippedPorts < cPorts))
{
{
// We just installed a bunch of modems at the same time.
// Do any of the installed modems require a reboot?
//
// (Note we set the quiet flag for this case when calling
// the class installer, so the user wouldn't a zillion
// "need to reboot" messages.)
WarnUserAboutReboot(hdi);
#ifndef INSTANT_DEVICE_ACTIVATION
#ifndef NT_BETA_1
MsgBox(g_hinst,
hwndOwner,
MAKEINTRESOURCE( IDS_NT_BETA_1 ),
MAKEINTRESOURCE(IDS_CAP_MODEMSETUP),
NULL,
MB_OK | MB_ICONINFORMATION);
#endif // NT_BETA_1
#endif /!INSTANT_DEVICE_ACTIVATION
}
}
}
except (EXCEPTION_EXECUTE_HANDLER)
{
bRet = FALSE;
SetLastError(ERROR_INVALID_PARAMETER);
}
DBG_EXIT_BOOL_ERR(CplDiInstallModemFromDriver, bRet);
return bRet;
}
/*----------------------------------------------------------
Purpose: Does all the dirty work to detect a modem.
Returns: TRUE on success
Cond: --
*/
BOOL
APIENTRY
CplDiDetectModem(
IN HDEVINFO hdi,
IN PDETECT_DATA pdetectdata, OPTIONAL
IN HWND hwndOwner, OPTIONAL
IN OUT LPDWORD pdwFlags) // DMF_ bit field
{
BOOL bRet;
DBG_ENTER(CplDiDetectModem);
ASSERT(hdi && INVALID_HANDLE_VALUE != hdi);
ASSERT(pdwFlags);
try
{
DWORD dwFlags = *pdwFlags;
ClearFlag(dwFlags, DMF_CANCELLED);
ClearFlag(dwFlags, DMF_DETECTED_MODEM);
ClearFlag(dwFlags, DMF_GOTO_NEXT_PAGE);
// Use the given device info set as the set of detected modem
// devices. This device set will be empty at first. When
// detection is finished, we'll see if anything was added to
// the set.
if (pdetectdata)
{
CplDiSetClassInstallParams(hdi, NULL, PCIPOfPtr(pdetectdata),
sizeof(*pdetectdata));
}
// Set the quiet flag?
if (IsFlagSet(dwFlags, DMF_QUIET))
{
// Yes
SP_DEVINSTALL_PARAMS devParams;
devParams.cbSize = sizeof(devParams);
if (CplDiGetDeviceInstallParams(hdi, NULL, &devParams))
{
SetFlag(devParams.Flags, DI_QUIETINSTALL);
CplDiSetDeviceInstallParams(hdi, NULL, &devParams);
}
}
// Start detection
bRet = CplDiCallClassInstaller(DIF_DETECT, hdi, NULL);
if (bRet)
{
SP_DEVINFO_DATA devData;
BOOL bDetectedOne = FALSE;
DWORD iDevice = 0;
// Find the first detected modem (if there is one) in
// the set.
devData.cbSize = sizeof(devData);
while (CplDiEnumDeviceInfo(hdi, iDevice++, &devData))
{
if (CplDiIsModemMarked(hdi, &devData, MARKF_DETECTED))
{
bDetectedOne = TRUE;
break;
}
}
// Was at least one modem detected?
if (bDetectedOne)
{
// Yes
SetFlag(dwFlags, DMF_DETECTED_MODEM);
// Is this the mass-modem case, in which we might be installing
// the detected modem on more than one port?
if (IsFlagSet(pdetectdata->dwFlags, DDF_QUERY_SINGLE) &&
IsFlagClear(dwFlags, DMF_ONE_PORT_INSTALL))
{
// Yes; create a global class driver list so the
// detect modem can be cloned for quicker installation.
HCURSOR hcurSav = SetCursor(LoadCursor(NULL, IDC_WAIT));
bRet = CplDiBuildModemDriverList(hdi, &devData);
SetCursor(hcurSav);
if ( !bRet )
{
if (IsFlagClear(dwFlags, DMF_QUIET))
{
// Some error occurred, show an error message
MsgBox(g_hinst,
hwndOwner,
MAKEINTRESOURCE(IDS_ERR_CANT_ADD_MODEM2),
MAKEINTRESOURCE(IDS_CAP_MODEMSETUP),
NULL,
MB_OK | MB_ICONERROR);
}
}
// Now that a global class driver list has been created,
// and the compatible driver selected from that list,
// we will delete this registered device instance.
// The real device instance will be created later; if
// we don't delete this registered device instance now,
// it will be left around like a turd.
CplDiRemoveDevice(hdi, &devData);
}
}
if (bRet)
{
SetFlag(dwFlags, DMF_GOTO_NEXT_PAGE);
}
CplDiUnmarkAllModems(hdi, MARKF_DETECTED);
}
// Did the user cancel detection?
else if (ERROR_CANCELLED == GetLastError())
{
// Yes
SetFlag(dwFlags, DMF_CANCELLED);
}
*pdwFlags = dwFlags;
}
except (EXCEPTION_EXECUTE_HANDLER)
{
bRet = FALSE;
SetLastError(ERROR_INVALID_PARAMETER);
}
DBG_EXIT_BOOL_ERR(CplDiDetectModem, bRet);
return bRet;
}
/*----------------------------------------------------------
Purpose: Perform an unattended manual installation of the
modems specified in the given INF file section.
Returns: --
Cond: --
*/
BOOL
PRIVATE
GetInfModemData(
HINF hInf,
LPTSTR szSection,
LPTSTR szPreferredFriendlyPort, // OPTIONAL
LPMODEM_SPEC lpModemSpec,
HPORTMAP hportmap,
LPBOOL lpbFatal
)
{
BOOL bRet = FALSE; // assume failure
INFCONTEXT Context;
TCHAR szInfLine[LINE_LEN];
LPTSTR lpszValue;
DWORD dwReqSize;
static LONG lLineCount = -1; // flag that count hasn't been obtained yet
TCHAR rgtchFriendlyPort[LINE_LEN];
ZeroMemory(lpModemSpec, sizeof(MODEM_SPEC));
*lpbFatal=FALSE;
if (szPreferredFriendlyPort && *szPreferredFriendlyPort)
{
// Preferred port specified -- look for exactly that port. Not fatal
// if you don't find it...
bRet = SetupFindFirstLine(
hInf,
szSection,
szPreferredFriendlyPort,
&Context
);
if (!bRet) goto exit;
}
else
{
if (lLineCount == -1)
{
if ((lLineCount = SetupGetLineCount(hInf, szSection)) < 1)
{
TRACE_MSG(TF_ERROR, "SetupGetLineCount() failed or found no lines");
goto exit;
}
}
// make a 0-based index out of it / decrement for next line
if (lLineCount-- == 0L)
{
// no more lines
goto exit;
}
// get the line
if (!SetupGetLineByIndex(hInf, szSection, lLineCount, &Context))
{
TRACE_MSG(TF_ERROR, "SetupGetLineByIndex(): line %#08lX doesn't exist", lLineCount);
goto exit;
}
}
*lpbFatal=TRUE;
bRet = FALSE; // assume failure once again
// read the key (port #)
if (!SetupGetStringField(&Context, FIELD_PORT, rgtchFriendlyPort,
ARRAYSIZE(rgtchFriendlyPort), &dwReqSize))
{
TRACE_MSG(TF_ERROR, "SetupGetStringField() failed: %#08lx", GetLastError());
gUnattendFailID = IDS_ERR_UNATTEND_INF_NOPORT;
goto exit;
}
ASSERT(
!szPreferredFriendlyPort
|| !*szPreferredFriendlyPort
|| !lstrcmpi(szPreferredFriendlyPort, rgtchFriendlyPort)
);
if (!PortMap_GetPortName(
hportmap,
rgtchFriendlyPort,
lpModemSpec->szPort,
ARRAYSIZE(lpModemSpec->szPort)
))
{
TRACE_MSG(
TF_ERROR,
"Can't find port %s in portmap.",
rgtchFriendlyPort
);
gUnattendFailID = IDS_ERR_UNATTEND_INF_NOSUCHPORT;
goto exit;
}
// read the modem description
if (!SetupGetStringField(&Context, FIELD_DESCRIPTION,
lpModemSpec->szDescription, sizeof(lpModemSpec->szDescription),
&dwReqSize))
{
TRACE_MSG(TF_ERROR, "SetupGetStringField() failed: %#08lx", GetLastError());
gUnattendFailID = IDS_ERR_UNATTEND_INF_NODESCRIPTION;
goto exit;
}
// read the manufacturer name, if it exists
if (!SetupGetStringField(&Context, FIELD_MANUFACTURER,
lpModemSpec->szManufacturer, sizeof(lpModemSpec->szManufacturer),
&dwReqSize))
{
TRACE_MSG(TF_WARNING, "no manufacturer specified (%#08lx)", GetLastError());
// optional field: don't return error
}
// read the provider name, if it exists
if (!SetupGetStringField(&Context, FIELD_PROVIDER, lpModemSpec->szProvider,
sizeof(lpModemSpec->szProvider), &dwReqSize))
{
TRACE_MSG(TF_WARNING, "no provider specified (%#08lx)", GetLastError());
// optional field: don't return error
}
*lpbFatal=FALSE;
bRet = TRUE;
exit:
return(bRet);
}
/*----------------------------------------------------------
Purpose: Perform an unattended manual installation of the
modems specified in the given INF file section.
Returns: --
Cond: --
*/
BOOL
PRIVATE
UnattendedManualInstall(
HWND hwnd,
LPINSTALLPARAMS lpip,
HDEVINFO hdi,
BOOL *pbDetect,
HPORTMAP hportmap
)
{
BOOL bRet = FALSE; // assume failure
BOOL bIsModem = FALSE; // assume INF gives no modems
BOOL bEnum, bFound;
HINF hInf = NULL;
MODEM_SPEC mSpec;
SP_DRVINFO_DATA drvData;
DWORD dwIndex, dwErr;
BOOL bFatal=FALSE;
ASSERT(pbDetect);
*pbDetect = FALSE;
hInf = SetupOpenInfFile(lpip->szInfName, NULL, INF_STYLE_OLDNT, NULL);
if (hInf == INVALID_HANDLE_VALUE)
{
TRACE_MSG(TF_ERROR, "SetupOpenInfFile() failed: %#08lx", GetLastError());
MsgBox(g_hinst, hwnd,
MAKEINTRESOURCE(IDS_ERR_CANT_OPEN_INF_FILE),
MAKEINTRESOURCE(IDS_CAP_MODEMSETUP),
NULL,
MB_OK | MB_ICONEXCLAMATION,
lpip->szInfName);
hInf = NULL;
goto exit;
}
if (!CplDiBuildDriverInfoList(hdi, NULL, SPDIT_CLASSDRIVER))
{
TRACE_MSG(TF_ERROR, "CplDiBuildDriverInfoList() failed: %#08lx", GetLastError());
gUnattendFailID = IDS_ERR_UNATTEND_DRIVERLIST;
goto exit;
}
drvData.cbSize = sizeof(drvData);
// process each line in our INF file section
while (GetInfModemData(hInf, lpip->szInfSect, lpip->szPort, &mSpec, hportmap, &bFatal))
{
// a modem was specified in the INF
bIsModem = TRUE;
// search for a match against all drivers
bFound = FALSE;
dwIndex = 0;
while (bEnum = CplDiEnumDriverInfo(hdi, NULL, SPDIT_CLASSDRIVER,
dwIndex++, &drvData))
{
// keep looking if driver's not a match
if (!IsSzEqual(mSpec.szDescription, drvData.Description))
continue;
// description matches, now check manufacturer if there is one
if (!IsSzEqual(mSpec.szManufacturer, TEXT("\0")) &&
!IsSzEqual(mSpec.szManufacturer, drvData.MfgName))
continue;
// manufacturer matches, now check provider if there is one
if (!IsSzEqual(mSpec.szProvider, TEXT("\0")) &&
!IsSzEqual(mSpec.szProvider, drvData.ProviderName))
continue;
bFound = TRUE;
// found a match; set this as the selected driver & install it
if (!CplDiSetSelectedDriver(hdi, NULL, &drvData))
{
TRACE_MSG(TF_ERROR, "CplDiSetSelectedDriver() failed: %#08lx",
GetLastError());
// can't install; get out of here quick.
goto exit;
}
if (!CplDiRegisterAndInstallModem(hdi, NULL, NULL, mSpec.szPort,
IMF_QUIET_INSTALL))
{
DWORD dwErr = GetLastError();
if (ERROR_DUPLICATE_FOUND != dwErr)
{
TRACE_MSG(
TF_ERROR,
"CplDiRegisterAndInstallModem() failed: %#08lx",
dwErr
);
gUnattendFailID = IDS_ERR_UNATTEND_CANT_INSTALL;
goto exit;
}
// Treate a duplicate-found error as no error.
}
break;
}
// Did CplDiEnumDriverInfo() fail on error other than "end of list"?
if ((!bEnum) && ((dwErr = GetLastError()) != ERROR_NO_MORE_ITEMS))
{
TRACE_MSG(TF_ERROR, "CplDiEnumDriverInfo() failed: %#08lx", dwErr);
goto exit;
}
if (!bFound)
{
MsgBox(g_hinst, hwnd,
MAKEINTRESOURCE(IDS_ERR_CANT_FIND_MODEM),
MAKEINTRESOURCE(IDS_CAP_MODEMSETUP),
NULL,
MB_OK | MB_ICONEXCLAMATION,
mSpec.szPort, mSpec.szDescription);
goto exit;
}
// If port spefied, only try on specified port.
if (*(lpip->szPort)) break;
}
if (bFatal) goto exit;
// Request detection if everything succeeded but the INF didn't specify
// any modems.
*pbDetect = !bIsModem;
bRet = TRUE;
exit:
if (hInf)
SetupCloseInfFile(hInf);
return(bRet);
}
/*----------------------------------------------------------
Purpose: Perform an unattended (UI-less) install. UI can only be
displayed in the case of a critical error.
Returns: --
Cond: --
*/
BOOL
PUBLIC
UnattendedInstall(HWND hwnd, LPINSTALLPARAMS lpip)
{
BOOL bRet = FALSE; // assume failure
HDEVINFO hdi = NULL;
DWORD dwFlags = 0;
DETECT_DATA dd;
HPORTMAP hportmap=NULL;
DWORD dwPorts;
DBG_ENTER(UnattendedInstall);
gUnattendFailID = IDS_ERR_UNATTEND_GENERAL_FAILURE;
if (!CplDiGetModemDevs(&hdi, NULL, DIGCF_PRESENT, NULL))
{
goto exit;
}
if (!PortMap_Create(&hportmap))
{
gUnattendFailID = IDS_ERR_UNATTEND_NOPORTS;
hportmap=NULL;
goto exit;
}
dwPorts = PortMap_GetCount(hportmap);
if (!dwPorts)
{
gUnattendFailID = IDS_ERR_UNATTEND_NOPORTS;
goto exit;
}
// Do a "manual" install if we were given an INF file and section.
if (lstrlen(lpip->szInfName) && lstrlen(lpip->szInfSect))
{
BOOL bDetect = FALSE;
bRet = UnattendedManualInstall(hwnd, lpip, hdi, &bDetect, hportmap);
if (!bRet || !bDetect)
goto exit;
// proceed with detection: manual install function didn't fail but
// INF didn't specify any modems.
bRet = FALSE; // assume failure;
}
// No INF file & section: do a detection install.
// Set the detection parameters
ZeroInit(&dd);
CplInitClassInstallHeader(&dd, DIF_DETECT);
if (*lpip->szPort)
{
// Tell modem detection that we'll only be installing on one port,
// so that it leaves us with a registered device instance instead
// of creating a global class driver list.
SetFlag(dwFlags, DMF_ONE_PORT_INSTALL);
dd.dwFlags |= DDF_QUERY_SINGLE;
if (!PortMap_GetPortName(
hportmap,
lpip->szPort,
dd.szPortQuery,
ARRAYSIZE(dd.szPortQuery)
))
{
TRACE_MSG(
TF_ERROR,
"Can't find port %s in portmap.",
lpip->szPort
);
gUnattendFailID = IDS_ERR_UNATTEND_INF_NOSUCHPORT;
goto exit;
}
}
else
{
if (dwPorts > MIN_MULTIPORT)
{
// The machine has > MIN_MULTIPORT ports and a port *wasn't* given.
// Warn the user.
TRACE_MSG(TF_ERROR, "Too many ports. Must restrict detection.");
MsgBox(g_hinst,
hwnd,
MAKEINTRESOURCE(IDS_ERR_TOO_MANY_PORTS),
MAKEINTRESOURCE(IDS_CAP_MODEMSETUP),
NULL,
MB_OK | MB_ICONEXCLAMATION,
dwPorts);
goto exit;
}
}
// Run UI-less modem detection
SetFlag(dwFlags, DMF_QUIET);
bRet = CplDiDetectModem(hdi, &dd, NULL, &dwFlags);
// Did the detection fail?
if (!bRet || IsFlagClear(dwFlags, DMF_GOTO_NEXT_PAGE))
{
TRACE_MSG(TF_ERROR, "modem detection failed");
MsgBox(g_hinst,
hwnd,
MAKEINTRESOURCE(IDS_ERR_DETECTION_FAILED),
MAKEINTRESOURCE(IDS_CAP_MODEMWIZARD),
NULL,
MB_OK | MB_ICONEXCLAMATION);
}
// Did detection find something?
if (IsFlagSet(dwFlags, DMF_DETECTED_MODEM))
{
// Install the modem(s) that were detected. (We can assume here
// that there's something in the device class to be installed.)
bRet = CplDiInstallModem(hdi, NULL, FALSE);
if (!bRet) gUnattendFailID = IDS_ERR_UNATTEND_CANT_INSTALL;
}
exit:
if (hportmap) {PortMap_Free(hportmap); hportmap=NULL;}
if (!bRet)
{
MsgBox(g_hinst,
hwnd,
MAKEINTRESOURCE(gUnattendFailID),
MAKEINTRESOURCE(IDS_CAP_MODEMSETUP),
NULL,
MB_OK | MB_ICONEXCLAMATION);
}
DBG_EXIT_BOOL_ERR(UnattendedInstall, bRet);
return(bRet);
}
//-----------------------------------------------------------------------------------
// SetupInfo structure functions
//-----------------------------------------------------------------------------------
/*----------------------------------------------------------
Purpose: This function creates a SETUPINFO structure.
Use SetupInfo_Destroy to free the pointer to this structure.
Returns: NO_ERROR
ERROR_OUTOFMEMORY
Cond: --
*/
DWORD
PUBLIC
SetupInfo_Create(
OUT LPSETUPINFO FAR * ppsi,
IN HDEVINFO hdi,
IN PSP_DEVINFO_DATA pdevData, OPTIONAL
IN PSP_INSTALLWIZARD_DATA piwd, OPTIONAL
IN PMODEM_INSTALL_WIZARD pmiw) OPTIONAL
{
DWORD dwRet;
LPSETUPINFO psi;
DBG_ENTER(SetupInfo_Create);
ASSERT(ppsi);
ASSERT(hdi && INVALID_HANDLE_VALUE != hdi);
psi = (LPSETUPINFO)LocalAlloc(LPTR, sizeof(*psi));
if (NULL == psi)
{
dwRet = ERROR_OUTOFMEMORY;
}
else
{
psi->cbSize = sizeof(*psi);
psi->pdevData = pdevData;
// Allocate a buffer to save the INSTALLWIZARD_DATA
dwRet = ERROR_OUTOFMEMORY; // assume error
psi->piwd = (PSP_INSTALLWIZARD_DATA)LocalAlloc(LPTR, sizeof(*piwd));
if (psi->piwd)
{
if (PortMap_Create(&psi->hportmap))
{
PSP_SELECTDEVICE_PARAMS psdp = &psi->selParams;
// Initialize the SETUPINFO struct
psi->hdi = hdi;
// Is there a modem install structure that we need to save?
if (pmiw)
{
// Yes
BltByte(&psi->miw, pmiw, sizeof(psi->miw));
}
psi->miw.ExitButton = PSBTN_CANCEL; // default return
// Copy the INSTALLWIZARD_DATA
if (piwd)
{
psi->dwFlags = piwd->PrivateFlags;
BltByte(psi->piwd, piwd, sizeof(*piwd));
}
// Are there enough ports on the system to indicate
// we should treat this like a multi-modem install?
if (MIN_MULTIPORT < PortMap_GetCount(psi->hportmap))
{
// Yes
SetFlag(psi->dwFlags, SIF_PORTS_GALORE);
}
// Initialize the SELECTDEVICE_PARAMS
CplInitClassInstallHeader(psdp, DIF_SELECTDEVICE);
LoadString(g_hinst, IDS_CAP_MODEMWIZARD, psdp->Title, SIZECHARS(psdp->Title));
LoadString(g_hinst, IDS_ST_SELECT_INSTRUCT, psdp->Instructions, SIZECHARS(psdp->Instructions));
LoadString(g_hinst, IDS_ST_MODELS, psdp->ListLabel, SIZECHARS(psdp->ListLabel));
// Load the TAPI DLL for the dialing properties page.
// If this fails, we still want to continue.
psi->hinstTapi = LoadLibrary(c_szTapiDLL);
if (ISVALIDHINSTANCE(psi->hinstTapi))
{
psi->pfnDialInited = (DIALINITEDPROC)GetProcAddress(psi->hinstTapi, "LAddrParamsInited");
}
dwRet = NO_ERROR;
}
}
// Did something fail?
if (NO_ERROR != dwRet)
{
// Yes; clean up
SetupInfo_Destroy(psi);
psi = NULL;
}
}
*ppsi = psi;
DBG_EXIT(SetupInfo_Create);
return dwRet;
}
/*----------------------------------------------------------
Purpose: This function destroys a SETUPINFO structure.
Returns: NO_ERROR
Cond: --
*/
DWORD
PUBLIC
SetupInfo_Destroy(
IN LPSETUPINFO psi)
{
if (psi)
{
if (psi->piwd)
{
LocalFree(LOCALOF(psi->piwd));
}
if (psi->hportmap)
{
PortMap_Free(psi->hportmap);
}
if (ISVALIDHINSTANCE(psi->hinstTapi))
{
FreeLibrary(psi->hinstTapi);
psi->hinstTapi = NULL;
}
CatMultiString(&psi->pszPortList, NULL);
LocalFree(LOCALOF(psi));
}
return NO_ERROR;
}
//-----------------------------------------------------------------------------------
// Debug functions
//-----------------------------------------------------------------------------------
#ifdef DEBUG
#pragma data_seg(DATASEG_READONLY)
struct _DIFMAP
{
DI_FUNCTION dif;
LPCTSTR psz;
} const c_rgdifmap[] = {
DEBUG_STRING_MAP(DIF_SELECTDEVICE),
DEBUG_STRING_MAP(DIF_INSTALLDEVICE),
DEBUG_STRING_MAP(DIF_ASSIGNRESOURCES),
DEBUG_STRING_MAP(DIF_PROPERTIES),
DEBUG_STRING_MAP(DIF_REMOVE),
DEBUG_STRING_MAP(DIF_FIRSTTIMESETUP),
DEBUG_STRING_MAP(DIF_FOUNDDEVICE),
DEBUG_STRING_MAP(DIF_SELECTCLASSDRIVERS),
DEBUG_STRING_MAP(DIF_VALIDATECLASSDRIVERS),
DEBUG_STRING_MAP(DIF_INSTALLCLASSDRIVERS),
DEBUG_STRING_MAP(DIF_CALCDISKSPACE),
DEBUG_STRING_MAP(DIF_DESTROYPRIVATEDATA),
DEBUG_STRING_MAP(DIF_VALIDATEDRIVER),
DEBUG_STRING_MAP(DIF_MOVEDEVICE),
DEBUG_STRING_MAP(DIF_DETECT),
DEBUG_STRING_MAP(DIF_INSTALLWIZARD),
DEBUG_STRING_MAP(DIF_DESTROYWIZARDDATA),
DEBUG_STRING_MAP(DIF_PROPERTYCHANGE),
DEBUG_STRING_MAP(DIF_ENABLECLASS),
DEBUG_STRING_MAP(DIF_DETECTVERIFY),
DEBUG_STRING_MAP(DIF_INSTALLDEVICEFILES),
};
#pragma data_seg()
/*----------------------------------------------------------
Purpose: Returns the string form of a known InstallFunction.
Returns: String ptr
Cond: --
*/
LPCTSTR PUBLIC Dbg_GetDifName(
DI_FUNCTION dif)
{
int i;
for (i = 0; i < ARRAYSIZE(c_rgdifmap); i++)
{
if (dif == c_rgdifmap[i].dif)
return c_rgdifmap[i].psz;
}
return TEXT("Unknown InstallFunction");
}
#endif // DEBUG
#ifdef INSTANT_DEVICE_ACTIVATION
//****************************************************************************
// Functions: Notify the TSP -- general version.
// BUG BUG -- move this and the notif apis into common code in rovdi later.
//
// Return: TRUE if successful
// FALSE if failure (including if the tsp is not active)
// GetLastError() returns the win32 failure code.
// History:
// 3/24/96 JosephJ Created (copied from ..\new\slot\client.c)
//****************************************************************************
BOOL WINAPI UnimodemNotifyTSP(PNOTIFICATION_FRAME pnf)
{
BOOL fRet=FALSE;
HNOTIFICATION hN=0;
if (pnf->dwSig!=dwNFRAME_SIG || pnf->dwSize<sizeof(*pnf) ||
pnf->dwSize>MAX_NOTIFICATION_FRAME_SIZE)
{
SetLastError(ERROR_INVALID_PARAMETER);
goto end;
}
hN = notifCreate(FALSE, SLOTNAME_UNIMODEM_NOTIFY_TSP, 0, 0);
if (hN)
{
fRet = notifWriteMsg(hN, (LPBYTE) pnf, pnf->dwSize);
notifFree(hN); hN=0;
}
end:
return fRet;
}
//****************************************************************************
// Functions: Notify the TSP -- ask it to re-enumerate devices
//
// Return: TRUE if successful
// FALSE if failure (including if the tsp is not active)
// GetLastError() returns the win32 failure code.
// History:
// 3/24/96 JosephJ Created (copied from ..\new\slot\client.c)
//****************************************************************************
void NotifyTSP_ReEnum(void)
{
struct {
DWORD dw0;
DWORD dwSize;
DWORD dwType;
DWORD dwFlags;
} EmptyFr;
PNOTIFICATION_FRAME pnf = (PNOTIFICATION_FRAME) &EmptyFr;
ASSERT(sizeof(EmptyFr)==sizeof(*pnf));
pnf->dwSig = dwNFRAME_SIG;
pnf->dwSize = sizeof(EmptyFr);
pnf->dwType = TSPNOTIF_TYPE_CPL;
pnf->dwFlags = fTSPNOTIF_FLAG_CPL_REENUM;
// Notify TSP of a device change.
UnimodemNotifyTSP(pnf);
}
//****************************************************************************
// Functions: Notify the TSP -- ask it to update the default comm config for
// the specfied device.
//
// Return: TRUE if successful
// FALSE if failure (including if the tsp is not active)
// GetLastError() returns the win32 failure code.
// History:
// 5/31/96 JosephJ Created
//****************************************************************************
void NotifyTSP_NewCommConfig(LPCTSTR lpctszFriendlyName)
{
struct {
DWORD dw0;
DWORD dwSize;
DWORD dwType;
DWORD dwFlags;
TCHAR rgchFriendlyName[MAX_BUF_REG];
} EmptyFr;
PNOTIFICATION_FRAME pnf = (PNOTIFICATION_FRAME) &EmptyFr;
UINT u = lstrlen(lpctszFriendlyName);
ASSERT(sizeof(((PMODEM_PRIV_PROP) 0)->szFriendlyName)
==sizeof(EmptyFr.rgchFriendlyName));
ASSERT(pnf->rgb == (LPBYTE) EmptyFr.rgchFriendlyName);
ASSERT(MAX_NOTIFICATION_FRAME_SIZE > sizeof(EmptyFr));
if (u*sizeof(TCHAR) < sizeof (EmptyFr.rgchFriendlyName) )
{
pnf->dwSig = dwNFRAME_SIG;
pnf->dwSize = sizeof(EmptyFr);
pnf->dwType = TSPNOTIF_TYPE_CPL;
pnf->dwFlags = fTSPNOTIF_FLAG_CPL_DEFAULT_COMMCONFIG_CHANGE;
#ifdef UNICODE
pnf->dwFlags |= fTSPNOTIF_FLAG_UNICODE;
#endif // UNICODE
lstrcpy(EmptyFr.rgchFriendlyName, lpctszFriendlyName);
// Notify TSP of a device change.
UnimodemNotifyTSP(pnf);
}
else
{
ASSERT(FALSE);
}
}
#endif
BOOL ReallyNeedsReboot
(
IN PSP_DEVINFO_DATA pdevData,
IN PSP_DEVINSTALL_PARAMS pdevParams
)
{
BOOL fRet = FALSE;
SC_HANDLE schModemSys=NULL;
SC_HANDLE schSCManager=NULL;
if (pdevParams->Flags & (DI_NEEDREBOOT | DI_NEEDRESTART))
{
SERVICE_STATUS ServiceStatus;
BOOL bResult;
// We ask to reboot on failure
fRet = TRUE;
schSCManager=OpenSCManager(
NULL,
NULL,
GENERIC_READ
);
if (schSCManager == NULL)
{
TRACE_MSG(
TF_GENERAL,
"OpenSCManager returns error %08lx!",
GetLastError()
);
// Assume we have to reboot.
goto end;
}
schModemSys=OpenService(
schSCManager,
TEXT("modem"),
SERVICE_QUERY_STATUS
);
if (schModemSys == NULL)
{
TRACE_MSG(TF_GENERAL, "OpenService() for modem.sys failed!");
// Assume we have to reboot
goto end;
}
bResult=QueryServiceStatus(
schModemSys,
&ServiceStatus
);
if (!bResult)
{
TRACE_MSG(
TF_GENERAL,
"QueryServiceStatus() for modem.sys failed (%08l)!",
GetLastError()
);
goto end;
}
if (ServiceStatus.dwCurrentState != SERVICE_RUNNING)
{
TRACE_MSG(
TF_GENERAL,
"modem.sys is not started. No need to reboot"
);
fRet=FALSE;
}
}
end:
if (schModemSys) {CloseServiceHandle(schModemSys);}
if (schSCManager) {CloseServiceHandle(schSCManager);}
return fRet;
}
const LPCTSTR lpctszSP6 = TEXT(" ");
#ifdef UNDER_CONSTRUCTION
// 6/11/96: JosephJ -- this is no good because the listbox sorting does not
// come out right.
// Right-justifies the '#6' in "USR modem #6".
// "USR modem #6" becomes
// "USR modem #6"
// and
// "USR modem #999" stays
// "USR modem #999"
void FormatFriendlyNameForDisplay
(
IN TCHAR szFriendly[],
OUT TCHAR rgchDisplayName[],
IN UINT cch
)
{
UINT u = lstrlen(szFriendly);
UINT uOff = u;
TCHAR *lpszFrom = szFriendly;
TCHAR *lpszTo = rgchDisplayName;
const LPCTSTR lpctszHash = TEXT("#");
const UINT cbJUST = 4; // 4 == lstrlen("#999")
if (cch<(u+cbJUST))
{
goto end;
}
// Find 1st '#' from the right-hand-side.
{
TCHAR *lpsz = szFriendly+u;
while (lpsz>szFriendly && *lpsz!=*lpctszHash)
{
lpsz--;
}
// Check if we really found it
if (lpsz>szFriendly && *lpsz==*lpctszHash && lpsz[-1]==*lpctszSP6
&& lpsz[1]>((TCHAR)'0') && lpsz[1]<=((TCHAR)'9'))
{
uOff = lpsz-szFriendly;
}
}
ASSERT(u>=uOff);
// Copy first part of friendly name
CopyMemory(lpszTo, lpszFrom, uOff*sizeof(TCHAR));
lpszTo += uOff;
lpszFrom += uOff;
cch -= uOff;
u -= uOff;
// Right-justify remainder of the string, if it's less than cbJUST
// chars long.
if (u && u<cbJUST && cch>=cbJUST)
{
ASSERT(lstrlen(lpctszSP6)>=(int)cbJUST);
u = cbJUST-u;
CopyMemory(lpszTo, lpctszSP6, u*sizeof(TCHAR));
lpszTo+=u;
cch -=u;
}
end:
ASSERT(cch);
lstrcpyn(lpszTo, lpszFrom, cch-1);
ASSERT(lpszTo[lstrlen(lpszFrom)]==0);
}
#endif // UNDER_CONSTRUCTION
// Right-justifies the 'COMxxx'
// "COM1" becomes
// " COM1"
// and
// "COM999" stays
// "COM999"
void FormatPortForDisplay
(
IN TCHAR szPort[],
OUT TCHAR rgchPortDisplayName[],
IN UINT cch
)
{
UINT u = lstrlen(szPort);
TCHAR *ptch = rgchPortDisplayName;
const UINT cbJUST = 6; // 6 == lstrlen("COM999")
ASSERT(cch>u);
// Right-justify the string, if it's less than cbJUST chars long.
if (u<cbJUST && cch>=cbJUST)
{
ASSERT(lstrlen(lpctszSP6)>=(int)cbJUST);
u = cbJUST-u;
CopyMemory(ptch, lpctszSP6, u*sizeof(TCHAR));
ptch+=u;
cch -=u;
}
lstrcpyn(ptch, szPort, cch);
}
void UnformatAfterDisplay
(
IN OUT TCHAR *psz
)
{
TCHAR *psz1 = psz;
// find first non-blank.
while(*psz1 == *lpctszSP6)
{
psz1++;
}
// move up
do
{
*psz++ = *psz1;
} while(*psz1++);
}