/*++ Copyright (c) 1995 Microsoft Corporation All rights reserved. Module Name: Utildi.c Abstract: Driver Setup DeviceInstaller Utility functions Author: Muhunthan Sivapragasam (MuhuntS) 06-Sep-1995 Revision History: --*/ #include "precomp.h" #include TCHAR cszOEMInfGen[] = TEXT("%s\\inf\\OEM%d.INF"); BOOL SetSelectDevParams( IN HDEVINFO hDevInfo, IN BOOL bWin95, IN LPCTSTR pszModel OPTIONAL ) /*++ Routine Description: Sets the select device parameters by calling setup apis Arguments: hDevInfo : Handle to the printer class device information list bWin95 : TRUE if selecting Win95 driver, else WinNT driver pszModel : Printer model we are looking for -- only for Win95 case Return Value: TRUE on success FALSE else --*/ { SP_SELECTDEVICE_PARAMS SelectDevParams; LPTSTR pszWin95Instn; SelectDevParams.ClassInstallHeader.cbSize = sizeof(SelectDevParams.ClassInstallHeader); SelectDevParams.ClassInstallHeader.InstallFunction = DIF_SELECTDEVICE; // // Get current SelectDevice parameters, and then set the fields // we want to be different from default // if ( !SetupDiGetClassInstallParams( hDevInfo, NULL, &SelectDevParams.ClassInstallHeader, sizeof(SelectDevParams), NULL) ) { if ( GetLastError() != ERROR_NO_CLASSINSTALL_PARAMS ) return FALSE; ZeroMemory(&SelectDevParams, sizeof(SelectDevParams)); SelectDevParams.ClassInstallHeader.cbSize = sizeof(SelectDevParams.ClassInstallHeader); SelectDevParams.ClassInstallHeader.InstallFunction = DIF_SELECTDEVICE; } // // Set the strings to use on the select driver page .. // LoadString(ghInst, IDS_PRINTERWIZARD, SelectDevParams.Title, sizeof(SelectDevParams.Title)/sizeof(SelectDevParams.Title[0])); // // For Win95 drivers instructions are different than NT drivers // if ( bWin95 ) { pszWin95Instn = GetStringFromRcFile(IDS_WIN95DEV_INSTRUCT); if ( !pszWin95Instn ) return FALSE; if ( lstrlen(pszWin95Instn) + lstrlen(pszModel) + 1 > sizeof(SelectDevParams.Instructions) ) { FreeStr(pszWin95Instn); return FALSE; } wsprintf(SelectDevParams.Instructions, pszWin95Instn, pszModel); FreeStr(pszWin95Instn); } else { LoadString(ghInst, IDS_WINNTDEV_INSTRUCT, SelectDevParams.Instructions, sizeof(SelectDevParams.Instructions)/sizeof(SelectDevParams.Instructions[0])); } LoadString(ghInst, IDS_SELECTDEV_LABEL, SelectDevParams.ListLabel, sizeof(SelectDevParams.ListLabel)/sizeof(SelectDevParams.ListLabel[0])); return SetupDiSetClassInstallParams( hDevInfo, NULL, &SelectDevParams.ClassInstallHeader, sizeof(SelectDevParams)); } BOOL SetDevInstallParams( IN HDEVINFO hDevInfo, IN HWND hwnd, IN LPCTSTR pszDriverPath OPTIONAL ) /*++ Routine Description: Sets the device installation parameters by calling setup apis Arguments: hDevInfo : Handle to the printer class device information list hwnd : Window handle that owns the UI pszDriverPath : Path where INF file should be searched Return Value: TRUE on success FALSE else --*/ { SP_DEVINSTALL_PARAMS DevInstallParams; // // Get current SelectDevice parameters, and then set the fields // we wanted changed from default // DevInstallParams.cbSize = sizeof(DevInstallParams); if ( !SetupDiGetDeviceInstallParams(hDevInfo, NULL, &DevInstallParams) ) { return FALSE; } // // Drivers are class drivers, // ntprint.inf is sorted do not waste time sorting, // show Have Disk button, // use our strings on the select driver page // DevInstallParams.Flags |= DI_SHOWCLASS | DI_INF_IS_SORTED | DI_SHOWOEM | DI_USECI_SELECTSTRINGS; if ( pszDriverPath && *pszDriverPath ) { lstrcpy(DevInstallParams.DriverPath, pszDriverPath); } DevInstallParams.hwndParent = hwnd; return SetupDiSetDeviceInstallParams(hDevInfo, NULL, &DevInstallParams); } BOOL PSetupBuildDriversFromPath( IN HANDLE h, IN LPCTSTR pszDriverPath, IN BOOL bEnumSingleInf ) /*++ Routine Description: Sets the device installation path (INF and driver dir) by calling setup apis Arguments: h : Handle from PSetupCreateDrvSetupParams pszDriverPath : Path where INF file should be searched bEnumSingleInf : Tells if pszDriverPath is a filename instead of path Return Value: TRUE on success FALSE else --*/ { HDEVINFO hDevInfo = (HDEVINFO)h; SP_DEVINSTALL_PARAMS DevInstallParams; // // Get current SelectDevice parameters, and then set the fields // we wanted changed from default // DevInstallParams.cbSize = sizeof(DevInstallParams); if ( !SetupDiGetDeviceInstallParams(hDevInfo, NULL, &DevInstallParams) ) { return FALSE; } DevInstallParams.Flags |= DI_INF_IS_SORTED; if ( bEnumSingleInf ) DevInstallParams.Flags |= DI_ENUMSINGLEINF; lstrcpy(DevInstallParams.DriverPath, pszDriverPath); SetupDiDestroyDriverInfoList(hDevInfo, NULL, SPDIT_CLASSDRIVER); return SetupDiSetDeviceInstallParams(hDevInfo, NULL, &DevInstallParams) && SetupDiBuildDriverInfoList(hDevInfo, NULL, SPDIT_CLASSDRIVER); } VOID PSetupDestroyDrvSetupParams( IN HANDLE h ) /*++ Routine Description: This routine should be called at the end to destroy the driver setup information Arguments: h : Handle which was created by calling PSetupCreateDrvSetupParams Return Value: Nothing --*/ { if ( h ) { SetupDiDestroyDeviceInfoList((HDEVINFO) h); } } HDEVINFO CreatePrinterDevInfo( VOID ) /*++ Routine Description: Creates a printer HDEVINFO by calling setup apis/ Arguments: None Return Value: Valid HDEVINFO on success, NULL on error --*/ { HDEVINFO hDevInfo; hDevInfo = SetupDiCreateDeviceInfoList((LPGUID)&GUID_DEVCLASS_PRINTER, NULL); return hDevInfo != INVALID_HANDLE_VALUE ? hDevInfo : NULL; } HANDLE PSetupCreateDrvSetupParams( VOID ) /*++ Routine Description: This routine should be called at the beginning to do the initialization Arguments: None Return Value: A handle which will be used on any subsequent calls to the driver setup routines. Handle will be NULL on error --*/ { HDEVINFO hDevInfo; hDevInfo = CreatePrinterDevInfo(); if ( !hDevInfo ) return NULL; if ( !SetSelectDevParams(hDevInfo, FALSE, NULL) ) { PSetupDestroyDrvSetupParams(hDevInfo); return NULL; } return (HANDLE) hDevInfo; } HPROPSHEETPAGE PSetupCreateDrvSetupPage( IN HANDLE h, IN HWND hwnd ) /*++ Routine Description: Returns the print driver selection property page Arguments: h : Handle from PSetupCreateDrvSetupParams hwnd : Window handle that owns the UI pszManufacturer : Manufacturer to preselect pszModel : Model to preselect Return Value: Handle to the property page, NULL on failure -- use GetLastError() --*/ { HDEVINFO hDevInfo = (HDEVINFO) h; SP_INSTALLWIZARD_DATA InstallWizardData; if ( !SetDevInstallParams(hDevInfo, hwnd, NULL) ) { return NULL; } ZeroMemory(&InstallWizardData, sizeof(InstallWizardData)); InstallWizardData.ClassInstallHeader.cbSize = sizeof(InstallWizardData.ClassInstallHeader); InstallWizardData.ClassInstallHeader.InstallFunction = DIF_INSTALLWIZARD; InstallWizardData.DynamicPageFlags = DYNAWIZ_FLAG_PAGESADDED; InstallWizardData.hwndWizardDlg = hwnd; return SetupDiGetWizardPage(hDevInfo, NULL, &InstallWizardData, SPWPT_SELECTDEVICE, 0); } VOID PSetupDestroySelectedDriverInfo( IN PSELECTED_DRV_INFO pSelectedDrvInfo ) /*++ Routine Description: Frees all memory allocated for the SELECTED_DRV_INFO structure and the fields in it Arguments: pSelectedDrvInfo : Pointer to the structure to destory information Return Value: Nothing --*/ { if ( pSelectedDrvInfo ) { FreeStr(pSelectedDrvInfo->pszModelName); FreeStr(pSelectedDrvInfo->pszDriverSection); FreeStr(pSelectedDrvInfo->pszInfFile); FreeMem(pSelectedDrvInfo); } } PSELECTED_DRV_INFO SelectedDriverInfoFromDrvInfo( IN HDEVINFO hDevInfo, IN PSP_DRVINFO_DATA pDrvInfoData ) /*++ Routine Description: Gets the INF file, driver section name for a give driver info and builds a SELECTED_DRV_INFO Arguments: hDevInfo - Handle to the printer class device information list pDrvInfoData - Points to a SP_DRVINFO_DATA giving the driver model to retrive information Return Value: On success -- A non null pointer value to SELECTED_DRV_INFO On failure -- NULL, GetLastError to get error code --*/ { PSP_DRVINFO_DETAIL_DATA pDrvInfoDetailData; PSELECTED_DRV_INFO pSelectedDrvInfo; BOOL bRet = FALSE; DWORD dwNeeded; pSelectedDrvInfo = (PSELECTED_DRV_INFO) AllocMem(sizeof(*pSelectedDrvInfo)); pDrvInfoDetailData = (PSP_DRVINFO_DETAIL_DATA) AllocMem(sizeof(*pDrvInfoDetailData)); if ( !pSelectedDrvInfo || !pDrvInfoDetailData ) goto Cleanup; ZeroMemory(pSelectedDrvInfo, sizeof(*pSelectedDrvInfo)); pDrvInfoDetailData->cbSize = sizeof(*pDrvInfoDetailData); if ( !SetupDiGetDriverInfoDetail(hDevInfo, NULL, pDrvInfoData, pDrvInfoDetailData, sizeof(*pDrvInfoDetailData), &dwNeeded) ) { if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER ) { goto Cleanup; } FreeMem(pDrvInfoDetailData); pDrvInfoDetailData = (PSP_DRVINFO_DETAIL_DATA) AllocMem(dwNeeded); if ( !pDrvInfoDetailData ) goto Cleanup; pDrvInfoDetailData->cbSize = sizeof(SP_DRVINFO_DETAIL_DATA); if ( !SetupDiGetDriverInfoDetail(hDevInfo, NULL, pDrvInfoData, pDrvInfoDetailData, dwNeeded, &dwNeeded) ) { goto Cleanup; } } // // Get modelname, driversection, and inffile // pSelectedDrvInfo->pszModelName = AllocStr(pDrvInfoData->Description); pSelectedDrvInfo->pszDriverSection = AllocStr(pDrvInfoDetailData->SectionName); pSelectedDrvInfo->pszInfFile = AllocStr(pDrvInfoDetailData->InfFileName); if ( pSelectedDrvInfo->pszModelName && pSelectedDrvInfo->pszDriverSection && pSelectedDrvInfo->pszInfFile ) { bRet = TRUE; } Cleanup: if ( pDrvInfoDetailData ) { FreeMem(pDrvInfoDetailData); } if ( !bRet ) { PSetupDestroySelectedDriverInfo(pSelectedDrvInfo); return NULL; } return pSelectedDrvInfo; } BOOL CopyOEMInfFileAndGiveUniqueName( IN HANDLE h, IN LPTSTR pszInfFile ) /*++ Routine Description: This routine checks if an OEM driver list is being used and if so copies the OEM printer inf file to "\Inf\OEM.INF". Where n is the first unused file number. Arguments: h : Handle got by calling PSetupCreateDrvSetupParams pszInfFile : Fully qualified path of OEM inf file Return Value: TRUE if no error, FALSE else --*/ { SP_DEVINSTALL_PARAMS DevInstallParams; HANDLE hFile; TCHAR szNewFileName[MAX_PATH], szSystemDir[MAX_PATH]; DWORD i; DevInstallParams.cbSize = sizeof(DevInstallParams); DevInstallParams.DriverPath[0] = 0; // // Check DeviceInstallParams to see if OEM driver list is built // if ( !SetupDiGetDeviceInstallParams((HDEVINFO)h, NULL, &DevInstallParams) ) { return FALSE; } // // If DriverPath is clear then not an OEM driver if ( !DevInstallParams.DriverPath[0] ) { return TRUE; } if ( !GetWindowsDirectory(szSystemDir, sizeof(szSystemDir)/sizeof(szSystemDir[0])) ) { return FALSE; } if ( lstrlen(szSystemDir) + lstrlen(cszOEMInfGen) + 5 > sizeof(szNewFileName)/sizeof(szNewFileName[0]) ) { return FALSE; } for ( i = 0 ; i < 10000 ; ++i ) { wsprintf(szNewFileName, cszOEMInfGen, szSystemDir, i); // // By using the CREATE_NEW flag we reserve the file name and // will not end up overwriting another file which gets created // by another setup (some inf) thread // h = CreateFile(szNewFileName, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); if ( h != INVALID_HANDLE_VALUE ) { CloseHandle(h); return CopyFile(pszInfFile, szNewFileName, FALSE); } else if ( GetLastError() != ERROR_FILE_EXISTS ) { return FALSE; } } return FALSE; } PSELECTED_DRV_INFO GetSelectedDriverInfo( IN HDEVINFO hDevInfo ) /*++ Routine Description: Gets the selected diver information by calling the setupapi routines Arguments: hDevInfo - Handle to the printer class device information list Return Value: On success -- A non null pointer value to SELECTED_DRV_INFO On failure -- NULL, GetLastError to get error code --*/ { SP_DRVINFO_DATA DrvInfoData; DrvInfoData.cbSize = sizeof(DrvInfoData); if ( SetupDiGetSelectedDriver(hDevInfo, NULL, &DrvInfoData) ) { return SelectedDriverInfoFromDrvInfo(hDevInfo, &DrvInfoData); } return NULL; } PSELECTED_DRV_INFO PSetupGetSelectedDriverInfo( IN HANDLE h ) /*++ Routine Description: Gets the selected diver information by calling the setupapi routines Arguments: h : Handle got by calling PSetupCreateDrvSetupParams Return Value: On success -- A non null pointer value to SELECTED_DRV_INFO On failure -- NULL, GetLastError to get error code --*/ { return GetSelectedDriverInfo((HDEVINFO)h); } BOOL SelectDriver( IN HDEVINFO hDevInfo ) /*++ Routine Description: Display manufacturer/model information and have the user select a printer driver. GetSelectedDriver will give the selected driver. Arguments: hDevInfo - Handle to the printer class device information list Return Value: TRUE on success, FALSE on error --*/ { return SetupDiSelectDevice(hDevInfo, NULL); } BOOL PSetupSelectDriver( IN HANDLE h, IN HWND hwnd ) /*++ Routine Description: Display manufacturer/model information and have the user select a printer driver. Use PSetupGetSelectedDriverInfo Arguments: h : Handle got by call to PSetupCreateDrvSetupParams hwnd : Window handle Return Value: TRUE on success, FALSE on error --*/ { HDEVINFO hDevInfo = (HDEVINFO)h; return SetDevInstallParams(hDevInfo, hwnd, NULL) && SelectDriver(hDevInfo); } VOID GetDriverPath( IN HANDLE h, OUT LPTSTR pszDriverPath ) /*++ Routine Description: Gets the path where driver files should be searched first to copy from Arguments: h : Handle got from call to PSetupCreateDrvSetupParams pszDriverPath : buffer to copy the driver path to Return Value: Nothing --*/ { LPTSTR *List; DWORD dwCount; SP_DEVINSTALL_PARAMS DevInstallParams; // // If we have hDevInfo for printer check if DeviceInstallParams have // been set // DevInstallParams.cbSize = sizeof(DevInstallParams); DevInstallParams.DriverPath[0] = 0; // // First call DeviceInstaller API to findout if we should be looking // in a specific place for drivers (ex. HaveDisk case) // Second try to get from mru // // if ( SetupDiGetDeviceInstallParams((HDEVINFO)h, NULL, &DevInstallParams) && DevInstallParams.DriverPath[0] ) { lstrcpy(pszDriverPath, DevInstallParams.DriverPath); return; } if ( SetupQuerySourceList(SRCLIST_USER | SRCLIST_SYSTEM, &List, &dwCount) ) { lstrcpy(pszDriverPath, List[0]); SetupFreeSourceList(&List, dwCount); } else { // // Default put A:\ since we have to give something to setup // lstrcpy(pszDriverPath, TEXT("A:\\")); } } BOOL BuildClassDriverList( IN HDEVINFO hDevInfo ) /*++ Routine Description: Build the class driver list. Note: If driver list is already built this comes back immediately Arguments: hDevInfo : Handle to the printer class device information list Return Value: TRUE on success, FALSE on error --*/ { return SetupDiBuildDriverInfoList(hDevInfo, NULL, SPDIT_CLASSDRIVER); } BOOL PSetupRefreshDriverList( IN HANDLE h ) /*++ Routine Description: Destroy current driver list and build new one Arguments: h : Handle got by calling PSetupCreateDrvSetupParams Return Value: TRUE on success, FALSE on error --*/ { HDEVINFO hDevInfo = (HDEVINFO) h; SP_DEVINSTALL_PARAMS DevInstallParams; DevInstallParams.cbSize = sizeof(DevInstallParams); DevInstallParams.DriverPath[0] = 0; // // Check DeviceInstallParams to see if OEM driver list is built // if ( SetupDiGetDeviceInstallParams(hDevInfo, NULL, &DevInstallParams) && !DevInstallParams.DriverPath[0] ) { return TRUE; } // // Destroy current list and build another one // SetupDiDestroyDriverInfoList(hDevInfo, NULL, SPDIT_CLASSDRIVER); DevInstallParams.DriverPath[0] = sZero; return SetupDiSetDeviceInstallParams(hDevInfo, NULL, &DevInstallParams) && BuildClassDriverList(hDevInfo); } PSELECTED_DRV_INFO DriverInfoFromName( IN HDEVINFO hDevInfo, IN LPCTSTR pszModel ) /*++ Routine Description: Given a printer driver model name get the SELECTED_DRV_INFO for it for a given device info set Arguments: hDeVInfo : Handle to the device information set pszModel : Driver model name Return Value: Valid pointer to a SELECTED_DRV_INFO on success, NULL on error --*/ { SP_DRVINFO_DATA DrvInfoData; DWORD dwIndex; dwIndex = 0; DrvInfoData.cbSize = sizeof(DrvInfoData); while ( SetupDiEnumDriverInfo(hDevInfo, NULL, SPDIT_CLASSDRIVER, dwIndex, &DrvInfoData) ) { if ( !lstrcmp(pszModel, DrvInfoData.Description) ) { return SelectedDriverInfoFromDrvInfo(hDevInfo, &DrvInfoData); } DrvInfoData.cbSize = sizeof(DrvInfoData); ++dwIndex; } SetLastError(ERROR_UNKNOWN_PRINTER_DRIVER); return NULL; } PSELECTED_DRV_INFO PSetupDriverInfoFromName( IN HANDLE h, IN LPCTSTR pszModel ) /*++ Routine Description: Given a printer driver model name get the SELECTED_DRV_INFO for it Arguments: h : Handle got by calling PSetupCreateDrvSetupParams pszModel : Driver model name Return Value: Valid pointer to SELECTED_DRV_INFO on success (finding model in an INF) NULL else (do GetLastError() to get the last error) --*/ { HDEVINFO hDevInfo = (HDEVINFO)h; if ( BuildClassDriverList(hDevInfo) ) return DriverInfoFromName(hDevInfo, pszModel); else return NULL; } BOOL PreSelectDriver( IN HDEVINFO hDevInfo, IN LPCTSTR pszManufacturer, IN LPCTSTR pszModel ) /*++ Routine Description: Preselect a manufacturer and model for the driver dialog If same model is found select it, else if a match in manufacturer is found select first driver for the manufacturer. Arguments: hDevInfo : Handle to printer DeviceInfoSet pszManufacturer : Manufacterer name to preselect pszModel : Model name to preselect Return Value: TRUE on a model or manufacturer match FALSE else --*/ { SP_DRVINFO_DATA DrvInfoData; DWORD dwIndex, dwManf, dwMod; if ( !BuildClassDriverList(hDevInfo) ) { return FALSE; } dwIndex = 0; // // If no model/manf given select first driver // if ( pszManufacturer && *pszManufacturer && pszModel && *pszModel ) { dwManf = dwMod = MAX_DWORD; DrvInfoData.cbSize = sizeof(DrvInfoData); while ( SetupDiEnumDriverInfo(hDevInfo, NULL, SPDIT_CLASSDRIVER, dwIndex, &DrvInfoData) ) { if ( dwManf == MAX_DWORD && !lstrcmp(pszManufacturer, DrvInfoData.MfgName) ) { dwManf = dwIndex; } if ( !lstrcmp(pszModel, DrvInfoData.Description) ) { dwMod = dwIndex; break; // the for loop } DrvInfoData.cbSize = sizeof(DrvInfoData); ++dwIndex; } if ( dwMod != MAX_DWORD ) { dwIndex = dwMod; } else if ( dwManf != MAX_DWORD ) { dwIndex = dwManf; } else { SetLastError(ERROR_UNKNOWN_PRINTER_DRIVER); return FALSE; } } DrvInfoData.cbSize = sizeof(DrvInfoData); if ( SetupDiEnumDriverInfo(hDevInfo, NULL, SPDIT_CLASSDRIVER, dwIndex, &DrvInfoData) && SetupDiSetSelectedDriver(hDevInfo, NULL, &DrvInfoData) ) { return TRUE; } return FALSE; } BOOL PSetupPreSelectDriver( IN HANDLE h, IN LPCTSTR pszManufacturer, IN LPCTSTR pszModel ) /*++ Routine Description: Preselect a manufacturer and model for the driver dialog If same model is found select it, else if a match in manufacturer is found select first driver for the manufacturer. Arguments: hDevInfo : Handle to printer DeviceInfoSet pszManufacturer : Manufacterer name to preselect pszModel : Model name to preselect Return Value: TRUE on a model or manufacturer match FALSE else --*/ { HDEVINFO hDevInfo = (HDEVINFO)h; return PreSelectDriver(hDevInfo, pszManufacturer, pszModel); }