/*++ Copyright (C) 1997-1999 Microsoft Corporation Module Name: CDriver.cpp Abstract: This module implements CDriver and CService classes Author: William Hsieh (williamh) created Revision History: --*/ #include "devmgr.h" #include "cdriver.h" const TCHAR* tszStringFileInfo = TEXT("StringFileInfo\\%04X%04X\\"); const TCHAR* tszFileVersion = TEXT("FileVersion"); const TCHAR* tszLegalCopyright = TEXT("LegalCopyright"); const TCHAR* tszCompanyName = TEXT("CompanyName"); const TCHAR* tszTranslation = TEXT("VarFileInfo\\Translation"); const TCHAR* tszStringFileInfoDefault = TEXT("StringFileInfo\\040904B0\\"); BOOL CDriver::Create( CDevice* pDevice, PSP_DRVINFO_DATA pDrvInfoData ) { HKEY hKey; TCHAR InfPath[MAX_PATH]; TCHAR InfName[MAX_PATH]; ASSERT(pDevice); m_pDevice = pDevice; m_OnLocalMachine = pDevice->m_pMachine->IsLocal(); CMachine* pMachine = m_pDevice->m_pMachine; ASSERT(pMachine); // // We can't get the digital signer on remote machines // if (!m_OnLocalMachine) { return TRUE; } m_hSDBDrvMain = SdbInitDatabase(SDB_DATABASE_MAIN_DRIVERS, NULL); // // Open drvice's driver registry key to get the InfPath // hKey = pMachine->DiOpenDevRegKey(*m_pDevice, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_READ); if (INVALID_HANDLE_VALUE != hKey) { DWORD regType; DWORD Len = sizeof(InfName); CSafeRegistry regDrv(hKey); // // Get the inf path from the driver key // if (regDrv.GetValue(REGSTR_VAL_INFPATH, ®Type, (PBYTE)InfName, &Len)) { if (GetSystemWindowsDirectory(InfPath, ARRAYLEN(InfPath))) { if (lstrlen(InfPath)) { // // Tack on an extra back slash if one is needed // if (_T('\\') != InfPath[lstrlen(InfPath) - 1]) { lstrcat(InfPath, TEXT("\\")); } lstrcat(InfPath, TEXT("INF\\")); lstrcat(InfPath, InfName); pMachine->GetDigitalSigner(InfPath, m_DigitalSigner); } } } } return TRUE; } BOOL CDriver::BuildDriverList( PSP_DRVINFO_DATA pDrvInfoData, BOOL bFunctionAndFiltersOnly ) { SP_DRVINFO_DATA DrvInfoData; HSPFILEQ hFileQueue = INVALID_HANDLE_VALUE; SP_DEVINSTALL_PARAMS DevInstParams; // // If we already built up the list of driver files then we don't need // to do it again. // if (m_DriverListBuilt) { return m_listDriverFile.GetCount(); } ASSERT(m_pDevice); if (!m_OnLocalMachine) { AddFunctionAndFilterDrivers(m_pDevice); return m_listDriverFile.GetCount(); } CMachine* pMachine = m_pDevice->m_pMachine; ASSERT(pMachine); hFileQueue = SetupOpenFileQueue(); // // Only build up the list of files from the INF if bFunctionAndFiltersOnly // is not TRUE. // if (!bFunctionAndFiltersOnly) { DevInstParams.cbSize = sizeof(DevInstParams); if (!pDrvInfoData) { pMachine->DiGetDeviceInstallParams(*m_pDevice, &DevInstParams); // // Set the DI_FLAGSEX_INSTALLEDDRIVER flag before calling SetupDiBuildDriverInfoList. // This will have it only put the installed driver into the list. // DevInstParams.FlagsEx |= (DI_FLAGSEX_INSTALLEDDRIVER | DI_FLAGSEX_ALLOWEXCLUDEDDRVS); if (pMachine->DiSetDeviceInstallParams(*m_pDevice, &DevInstParams) && pMachine->DiBuildDriverInfoList(*m_pDevice, SPDIT_CLASSDRIVER)) { DrvInfoData.cbSize = sizeof(DrvInfoData); // // There should only be one driver in this list. If there isn't any // drivers in this list then there must not be a driver currently // installed on this device. // if (pMachine->DiEnumDriverInfo(*m_pDevice, SPDIT_CLASSDRIVER, 0, &DrvInfoData)) { // // Set this as the selected driver // pMachine->DiSetSelectedDriver(*m_pDevice, &DrvInfoData); pDrvInfoData = &DrvInfoData; } else { // // We did not find a match...so just destroy it. // pMachine->DiDestroyDriverInfoList(*m_pDevice, SPDIT_CLASSDRIVER); pDrvInfoData = NULL; } } } if (pDrvInfoData) { // // Get a list of all the files installed for this device // if (INVALID_HANDLE_VALUE != hFileQueue) { DevInstParams.FileQueue = hFileQueue; DevInstParams.Flags |= DI_NOVCP; if (pMachine->DiSetDeviceInstallParams(*m_pDevice, &DevInstParams) && pMachine->DiCallClassInstaller(DIF_INSTALLDEVICEFILES, *m_pDevice)) { // // Dereference the file queue so that we can close it // DevInstParams.FileQueue = NULL; DevInstParams.Flags &= ~DI_NOVCP; pMachine->DiSetDeviceInstallParams(*m_pDevice, &DevInstParams); } } if (pDrvInfoData == &DrvInfoData) { pMachine->DiDestroyDriverInfoList(*m_pDevice, SPDIT_CLASSDRIVER); } } } // // Add the funtion and device and class upper and lower filters, sometimes // these aren't added via the INF file directly so this makes sure they // show up in the list. // AddFunctionAndFilterDrivers(m_pDevice, hFileQueue); if (hFileQueue != INVALID_HANDLE_VALUE) { // // Scan the file queue. // DWORD ScanResult; SetupScanFileQueue(hFileQueue, SPQ_SCAN_USE_CALLBACK_SIGNERINFO, NULL, ScanQueueCallback, (PVOID)this, &ScanResult ); // // Close the file queue // SetupCloseFileQueue(hFileQueue); } m_DriverListBuilt = TRUE; return m_listDriverFile.GetCount(); } void CDriver::AddDriverFile( CDriverFile* pNewDrvFile ) { // // Check to see if this driver already exists in the list. // POSITION pos = m_listDriverFile.GetHeadPosition(); while (NULL != pos) { CDriverFile* pDrvFile = m_listDriverFile.GetNext(pos); if (lstrcmpi(pDrvFile->GetFullPathName(), pNewDrvFile->GetFullPathName()) == 0) { // // This file already exists in the list so just return without // adding it. // return; } } m_listDriverFile.AddTail(pNewDrvFile); } void CDriver::AddFunctionAndFilterDrivers( CDevice* pDevice, HSPFILEQ hFileQueue ) { TCHAR ServiceName[MAX_PATH]; ULONG BufferLen; HKEY hKey; DWORD regType; // // Get the function driver // if (pDevice->m_pMachine->DiGetDeviceRegistryProperty(*pDevice, SPDRP_SERVICE, NULL, (PBYTE)ServiceName, sizeof(ServiceName), NULL )) { CreateFromService(pDevice, ServiceName, hFileQueue); } // // Add the upper and lower device filters // for (int i = 0; i<2; i++) { BufferLen = 0; pDevice->m_pMachine->DiGetDeviceRegistryProperty( *pDevice, i ? SPDRP_LOWERFILTERS : SPDRP_UPPERFILTERS, NULL, NULL, BufferLen, &BufferLen ); if (BufferLen != 0) { PTSTR Buffer = new TCHAR[BufferLen+2]; if (Buffer) { ZeroMemory(Buffer, BufferLen+2); if (pDevice->m_pMachine->DiGetDeviceRegistryProperty( *pDevice, i ? SPDRP_LOWERFILTERS : SPDRP_UPPERFILTERS, NULL, (PBYTE)Buffer, BufferLen, &BufferLen )) { for (PTSTR SingleItem = Buffer; *SingleItem; SingleItem += (lstrlen(SingleItem) + 1)) { CreateFromService(pDevice, SingleItem, hFileQueue); } } delete Buffer; } } } // // Add the upper and lower class filters // GUID ClassGuid; pDevice->ClassGuid(ClassGuid); hKey = m_pDevice->m_pMachine->DiOpenClassRegKey(&ClassGuid, KEY_READ, DIOCR_INSTALLER); if (INVALID_HANDLE_VALUE != hKey) { CSafeRegistry regClass(hKey); for (int i = 0; i<2; i++) { BufferLen = 0; regClass.GetValue(i ? REGSTR_VAL_LOWERFILTERS : REGSTR_VAL_UPPERFILTERS, ®Type, NULL, &BufferLen ); if (BufferLen != 0) { PTSTR Buffer = new TCHAR[BufferLen+2]; if (Buffer) { ZeroMemory(Buffer, BufferLen+2); if (regClass.GetValue(i ? REGSTR_VAL_LOWERFILTERS : REGSTR_VAL_UPPERFILTERS, ®Type, (PBYTE)Buffer, &BufferLen )) { for (PTSTR SingleItem = Buffer; *SingleItem; SingleItem += (lstrlen(SingleItem) + 1)) { CreateFromService(pDevice, SingleItem, hFileQueue); } } delete Buffer; } } } } } void CDriver::CreateFromService( CDevice* pDevice, PCTSTR ServiceName, HSPFILEQ hFileQueue ) { SC_HANDLE hscManager = NULL; SC_HANDLE hscService = NULL; if (!ServiceName) { return; } try { BOOL ComposePathNameFromServiceName = TRUE; // open default database on local machine for now hscManager = OpenSCManager(m_OnLocalMachine ? NULL : pDevice->m_pMachine->GetMachineFullName(), NULL, GENERIC_READ); if (NULL != hscManager) { hscService = OpenService(hscManager, ServiceName, GENERIC_READ); if (NULL != hscService) { QUERY_SERVICE_CONFIG qsc; DWORD BytesRequired; // first, probe for buffer size if (!QueryServiceConfig(hscService, NULL, 0, &BytesRequired) && ERROR_INSUFFICIENT_BUFFER == GetLastError()) { TCHAR FullPath[MAX_PATH]; BufferPtr BufPtr(BytesRequired); LPQUERY_SERVICE_CONFIG pqsc; pqsc = (LPQUERY_SERVICE_CONFIG)(PBYTE)BufPtr; DWORD Size; if (QueryServiceConfig(hscService, pqsc, BytesRequired, &Size) && pqsc->lpBinaryPathName && (TEXT('\0') != pqsc->lpBinaryPathName[0])) { ComposePathNameFromServiceName = FALSE; // // Make sure we have a valid full path. // if (GetFullPathFromImagePath(pqsc->lpBinaryPathName, FullPath, ARRAYLEN(FullPath))) { if (hFileQueue != INVALID_HANDLE_VALUE) { // // Add the file to the queue. // TCHAR TargetPath[MAX_PATH]; lstrcpy(TargetPath, FullPath); PTSTR p = (PTSTR)StrRChr(TargetPath, NULL, TEXT('\\')); if (p) { *p = TEXT('\0'); } SetupQueueCopy(hFileQueue, NULL, NULL, MyGetFileTitle(FullPath), NULL, NULL, TargetPath, NULL, 0 ); } else { // // No file queue was passed in so just manually // add this to our list of driver files. // SafePtr DrvFilePtr; CDriverFile* pDrvFile = new CDriverFile(); DrvFilePtr.Attach(pDrvFile); // // We will set the GetWin32Error to 0xFFFFFFFF which will // cause the UI to say 'not available' for the // signature. // if (pDrvFile->Create(FullPath, m_OnLocalMachine, 0xFFFFFFFF, NULL, m_hSDBDrvMain)) { AddDriverFile(pDrvFile); DrvFilePtr.Detach(); } } } } } CloseServiceHandle(hscService); hscService = NULL; } CloseServiceHandle(hscManager); hscManager = NULL; } if (ComposePathNameFromServiceName) { TCHAR FullPathName[MAX_PATH]; TCHAR SysDir[MAX_PATH]; if ((GetSystemDirectory(SysDir, ARRAYLEN(SysDir)) != 0) && SUCCEEDED(StringCchCopy(FullPathName, ARRAYLEN(FullPathName), SysDir)) && SUCCEEDED(StringCchCat(FullPathName, ARRAYLEN(FullPathName), TEXT("\\drivers\\"))) && SUCCEEDED(StringCchCat(FullPathName, ARRAYLEN(FullPathName), ServiceName)) && SUCCEEDED(StringCchCat(FullPathName, ARRAYLEN(FullPathName), TEXT(".sys")))) { if (hFileQueue != INVALID_HANDLE_VALUE) { // // Add the file to the queue. // TCHAR TargetPath[MAX_PATH]; lstrcpy(TargetPath, FullPathName); PTSTR p = (PTSTR)StrRChr(TargetPath, NULL, TEXT('\\')); if (p) { *p = TEXT('\0'); } SetupQueueCopy(hFileQueue, NULL, NULL, MyGetFileTitle(FullPathName), NULL, NULL, TargetPath, NULL, 0 ); } else { // // No file queue was passed in so just manually // add this to our list of driver files. // SafePtr DrvFilePtr; CDriverFile* pDrvFile = new CDriverFile(); DrvFilePtr.Attach(pDrvFile); // // We will set the GetWin32Error to 0xFFFFFFFF which will // cause the UI to say 'not available' for the // signature. // if (pDrvFile->Create(FullPathName, m_OnLocalMachine, 0xFFFFFFFF, NULL, m_hSDBDrvMain)) { AddDriverFile(pDrvFile); DrvFilePtr.Detach(); } } } } } catch (CMemoryException* e) { if (hscService) { CloseServiceHandle(hscService); } if (hscManager) { CloseServiceHandle(hscManager); } throw; } } CDriver::~CDriver() { if (!m_listDriverFile.IsEmpty()) { POSITION pos = m_listDriverFile.GetHeadPosition(); while (NULL != pos) { CDriverFile* pDrvFile = m_listDriverFile.GetNext(pos); delete pDrvFile; } m_listDriverFile.RemoveAll(); } if (m_hSDBDrvMain) { SdbReleaseDatabase(m_hSDBDrvMain); } } BOOL CDriver::operator ==( CDriver& OtherDriver ) { CDriverFile* pThisDrvFile; CDriverFile* pOtherDrvFile; BOOL DrvFileFound = FALSE; PVOID ThisContext, OtherContext; if (GetFirstDriverFile(&pThisDrvFile, ThisContext)) { do { DrvFileFound = FALSE; if (OtherDriver.GetFirstDriverFile(&pOtherDrvFile, OtherContext)) { do { if (*pThisDrvFile == *pOtherDrvFile) { DrvFileFound = TRUE; break; } } while (OtherDriver.GetNextDriverFile(&pOtherDrvFile, OtherContext)); } } while (DrvFileFound && GetNextDriverFile(&pThisDrvFile, ThisContext)); return DrvFileFound; } else { // if both do not have driver file, they are equal. return !OtherDriver.GetFirstDriverFile(&pOtherDrvFile, OtherContext); } } // // Can not throw a exception from this function because it is a callback // UINT CDriver::ScanQueueCallback( PVOID Context, UINT Notification, UINT_PTR Param1, UINT_PTR Param2 ) { try { if (SPFILENOTIFY_QUEUESCAN_SIGNERINFO == Notification && Param1) { CDriver* pDriver = (CDriver*)Context; if (pDriver) { SafePtr DrvFilePtr; CDriverFile* pDrvFile = new CDriverFile(); DrvFilePtr.Attach(pDrvFile); // // When creating the CDriver set the Win32Error to 0xFFFFFFFF // if the user is loged in as a guest. This is because we // cannot tell if a file is digitally signed if the user is // a guest. If the user is not a guest then use the Win32Error // returned from setupapi. // if (pDrvFile->Create((LPCTSTR)((PFILEPATHS_SIGNERINFO)Param1)->Target, pDriver->IsLocal(), pDriver->m_pDevice->m_pMachine->IsUserAGuest() ? 0xFFFFFFFF : ((PFILEPATHS_SIGNERINFO)Param1)->Win32Error, ((PFILEPATHS_SIGNERINFO)Param1)->DigitalSigner, pDriver->m_hSDBDrvMain )) { pDriver->AddDriverFile(pDrvFile); DrvFilePtr.Detach(); } } } } catch (CMemoryException* e) { e->Delete(); return ERROR_NOT_ENOUGH_MEMORY; } return NO_ERROR; } BOOL CDriver::GetFirstDriverFile( CDriverFile** ppDrvFile, PVOID& Context ) { ASSERT(ppDrvFile); if (!m_listDriverFile.IsEmpty()) { POSITION pos = m_listDriverFile.GetHeadPosition(); *ppDrvFile = m_listDriverFile.GetNext(pos); Context = pos; return TRUE; } Context = NULL; *ppDrvFile = NULL; return FALSE; } BOOL CDriver::GetNextDriverFile( CDriverFile** ppDrvFile, PVOID& Context ) { ASSERT(ppDrvFile); POSITION pos = (POSITION)Context; if (NULL != pos) { *ppDrvFile = m_listDriverFile.GetNext(pos); Context = pos; return TRUE; } *ppDrvFile = NULL; return FALSE; } void CDriver::GetDriverSignerString( String& strDriverSigner ) { if (m_DigitalSigner.IsEmpty()) { strDriverSigner.LoadString(g_hInstance, IDS_NO_DIGITALSIGNATURE); } else { strDriverSigner = m_DigitalSigner; } } BOOL CDriver::GetFullPathFromImagePath( LPCTSTR ImagePath, LPTSTR FullPath, UINT FullPathLength ) { TCHAR OriginalCurrentDirectory[MAX_PATH]; LPTSTR pRelativeString; LPTSTR lpFilePart; if (!ImagePath || (ImagePath[0] == TEXT('\0'))) { return FALSE; } // // If we aren't on a local machine then just return the file name and not // the full path. // if (!m_OnLocalMachine) { lstrcpyn(FullPath, MyGetFileTitle(ImagePath), FullPathLength); return TRUE; } // // First check if the ImagePath happens to be a valid full path. // if (GetFileAttributes(ImagePath) != 0xFFFFFFFF) { ::GetFullPathName(ImagePath, FullPathLength, FullPath, &lpFilePart); return TRUE; } pRelativeString = (LPTSTR)ImagePath; // // If the ImagePath starts with "\SystemRoot" or "%SystemRoot%" then // remove those values. // if (StrCmpNI(ImagePath, TEXT("\\SystemRoot\\"), lstrlen(TEXT("\\SystemRoot\\"))) == 0) { pRelativeString += lstrlen(TEXT("\\SystemRoot\\")); } else if (StrCmpNI(ImagePath, TEXT("%SystemRoot%\\"), lstrlen(TEXT("%SystemRoot%\\"))) == 0) { pRelativeString += lstrlen(TEXT("%SystemRoot%\\")); } // // At this point pRelativeString should point to the image path relative to // the windows directory. // if (!GetSystemWindowsDirectory(FullPath, FullPathLength)) { return FALSE; } if (!GetCurrentDirectory(ARRAYLEN(OriginalCurrentDirectory), OriginalCurrentDirectory)) { OriginalCurrentDirectory[0] = TEXT('\0'); } if (!SetCurrentDirectory(FullPath)) { return FALSE; } ::GetFullPathName(pRelativeString, FullPathLength, FullPath, &lpFilePart); if (OriginalCurrentDirectory[0] != TEXT('\0')) { SetCurrentDirectory(OriginalCurrentDirectory); } return TRUE; } BOOL CDriverFile::Create( LPCTSTR ServiceName, BOOL LocalMachine, DWORD Win32Error, LPCTSTR DigitalSigner, HSDB hSDBDrvMain ) { if (!ServiceName || (TEXT('\0') == ServiceName[0])) { return FALSE; } m_Win32Error = Win32Error; if (DigitalSigner) { m_strDigitalSigner = DigitalSigner; } // // For remote machine, we can not verify if the driver file exits. // we only show the driver name. // if (LocalMachine) { m_Attributes = GetFileAttributes(ServiceName); if (0xFFFFFFFF != m_Attributes) { m_strFullPathName = ServiceName; } else { // // The driver is a service. Do not search for the current director -- // GetFullPathName is useless here. // Search for Windows dir and System directory // TCHAR BaseDir[MAX_PATH]; BaseDir[0] = TEXT('\0'); if (GetSystemWindowsDirectory(BaseDir, ARRAYLEN(BaseDir))) { int Len; Len = lstrlen(BaseDir); if (Len) { if (_T('\\') != BaseDir[Len - 1]) { lstrcat(BaseDir, TEXT("\\")); } lstrcat(BaseDir, MyGetFileTitle(ServiceName)); m_Attributes = GetFileAttributes(BaseDir); } if (0xFFFFFFFF == m_Attributes) { if (GetSystemDirectory(BaseDir, ARRAYLEN(BaseDir))) { Len = lstrlen(BaseDir); if (Len) { if (_T('\\') != BaseDir[Len - 1]) { lstrcat(BaseDir, TEXT("\\")); } lstrcat(BaseDir, MyGetFileTitle(ServiceName)); m_Attributes = GetFileAttributes(BaseDir); } } } // // hopeless, we could find the path // if (0xFFFFFFFF == m_Attributes) { return FALSE; } m_strFullPathName = BaseDir; } else { return FALSE; } } m_HasVersionInfo = GetVersionInfo(); } else { m_strFullPathName = ServiceName; // //we do not have version info // m_HasVersionInfo = FALSE; } if (!m_strFullPathName.IsEmpty() && hSDBDrvMain != NULL) { TAGREF tagref = TAGREF_NULL; HAPPHELPINFOCONTEXT hAppHelpInfoContext = NULL; SDBENTRYINFO entryinfo; DWORD cbSize; tagref = SdbGetDatabaseMatch(hSDBDrvMain, (LPTSTR)m_strFullPathName, INVALID_HANDLE_VALUE, NULL, 0 ); if (tagref != TAGREF_NULL) { // // This driver is in the database. // m_IsDriverBlocked = TRUE; // // Call SdbReadDriverInformation to get the database GUID and the // driver GUID for this entry. // ZeroMemory(&entryinfo, sizeof(entryinfo)); if (SdbReadDriverInformation(hSDBDrvMain, tagref, &entryinfo)) { // // Open up the App help information database and query for the // html link. // hAppHelpInfoContext = SdbOpenApphelpInformation(&(entryinfo.guidDB), &(entryinfo.guidID)); if (hAppHelpInfoContext) { cbSize = 0; PBYTE pBuffer = NULL; cbSize = SdbQueryApphelpInformation(hAppHelpInfoContext, ApphelpHelpCenterURL, NULL, 0); if (cbSize && (pBuffer = new BYTE[cbSize])) { cbSize = SdbQueryApphelpInformation(hAppHelpInfoContext, ApphelpHelpCenterURL, (LPVOID)pBuffer, cbSize); if (cbSize) { m_strHtmlHelpID = (LPTSTR)pBuffer; } delete pBuffer; } SdbCloseApphelpInformation(hAppHelpInfoContext); } } } } return TRUE; } BOOL CDriverFile::GetVersionInfo() { DWORD Size, dwHandle; Size = GetFileVersionInfoSize((LPTSTR)(LPCTSTR)m_strFullPathName, &dwHandle); if (!Size) { return FALSE; } BufferPtr BufPtr(Size); PVOID pVerInfo = BufPtr; if (GetFileVersionInfo((LPTSTR)(LPCTSTR)m_strFullPathName, dwHandle, Size, pVerInfo)) { // get VarFileInfo\Translation PVOID pBuffer; UINT Len; String strStringFileInfo; if (!VerQueryValue(pVerInfo, (LPTSTR)tszTranslation, &pBuffer, &Len)) { strStringFileInfo = tszStringFileInfoDefault; } else { strStringFileInfo.Format(tszStringFileInfo, *((WORD*)pBuffer), *(((WORD*)pBuffer) + 1)); } String str; str = strStringFileInfo + tszFileVersion; if (VerQueryValue(pVerInfo, (LPTSTR)(LPCTSTR)str, &pBuffer, &Len)) { m_strVersion = (LPTSTR)pBuffer; str = strStringFileInfo + tszLegalCopyright; if (VerQueryValue(pVerInfo, (LPTSTR)(LPCTSTR)str, &pBuffer, &Len)) { m_strCopyright = (LPTSTR)pBuffer; str = strStringFileInfo + tszCompanyName; if (VerQueryValue(pVerInfo, (LPTSTR)(LPCTSTR)str, &pBuffer, &Len)) { m_strProvider = (LPTSTR)pBuffer; } } } } return TRUE; } BOOL CDriverFile::operator ==( CDriverFile& OtherDrvFile ) { return \ m_HasVersionInfo == OtherDrvFile.HasVersionInfo() && (GetFullPathName() == OtherDrvFile.GetFullPathName() || (GetFullPathName() && OtherDrvFile.GetFullPathName() && !lstrcmpi(GetFullPathName(), OtherDrvFile.GetFullPathName()) ) ) && (GetProvider() == OtherDrvFile.GetProvider() || (GetProvider() && OtherDrvFile.GetProvider() && !lstrcmpi(GetProvider(), OtherDrvFile.GetProvider()) ) ) && (GetCopyright() == OtherDrvFile.GetCopyright() || (GetCopyright() && OtherDrvFile.GetCopyright() && !lstrcmpi(GetCopyright(), OtherDrvFile.GetCopyright()) ) ) && (GetVersion() == OtherDrvFile.GetVersion() || (GetVersion() && OtherDrvFile.GetVersion() && !lstrcmpi(GetVersion(), OtherDrvFile.GetVersion()) ) ); }