/*++ Copyright (c) 2000 Microsoft Corporation Module Name: setupapi.hpp Abstract: Wrapper class library for setup api Author: Vijay Jayaseelan (vijayj) 04 Aug 2000 Revision History: None --*/ #pragma once // // Disable the compiler warning for long names // #pragma warning( disable : 4786 ) extern "C" { #include #include #include } #include #include #include #include // // Wide string output routine // std::string& ToAnsiString(std::string &lhs, const std::wstring &rhs); std::ostream& operator<<(std::ostream &lhs, const std::basic_string &rhs); std::ostream& operator<<(std::ostream &lhs, PCWSTR rhs); // // Exception classes // template class BaseException { public: BaseException(){} BaseException(const std::basic_string &Info) : ExceptionInfo(Info) {} virtual ~BaseException(){} virtual void Dump(std::ostream &os) = 0; protected: std::basic_string ExceptionInfo; }; template class InvalidValueIndex : public BaseException{ public: InvalidValueIndex(unsigned int Idx) : Index(Idx){} void Dump(std::ostream &os) { os << "Invalid value index : (" << std::dec << Index << ")" << std::endl; } private: unsigned int Index; }; template class InvalidValueKey : public BaseException{ public: InvalidValueKey(const std::basic_string &SecName, const std::basic_string &Key) : SectionName(SecName), KeyName(Key){} void Dump(std::ostream &os) { os << "Invalid value key name (" << KeyName << ")" << " in " << SectionName << " section." << std::endl; } private: std::basic_string SectionName, KeyName; }; // // Abstracts a Win32 error // template class W32Exception : public BaseException { public: W32Exception(DWORD ErrCode = GetLastError()) : ErrorCode(ErrCode){} void Dump(std::ostream &os) { T MsgBuffer[4096]; MsgBuffer[0] = NULL; DWORD CharCount; if (sizeof(T) == sizeof(WCHAR)) { CharCount = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, ErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (PWSTR)MsgBuffer, sizeof(MsgBuffer)/sizeof(WCHAR), NULL); if (CharCount) { std::wstring Msg((PWSTR)MsgBuffer); os << Msg; } else { os << std::hex << ErrorCode; } } else { CharCount = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, ErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (PSTR)MsgBuffer, sizeof(MsgBuffer)/sizeof(CHAR), NULL); if (CharCount) { std::string Msg((PCSTR)MsgBuffer); os << Msg; } else { os << std::hex << ErrorCode; } } } DWORD GetErrorCode() const { return ErrorCode; } protected: DWORD ErrorCode; }; template class Section; template class SectionValues { public: SectionValues(Section &Sec, ULONG LineIdx, bool New = false); SectionValues(Section &Sec, const std::basic_string &Key, ULONG LineIdx, bool New = false); void PutValue(ULONG Index, const std::basic_string &Val) { Values[Index] = Val; GetContainer().GetContainer().SetDirty(); } const std::basic_string& GetValue(ULONG Index) const { return Values[Index]; } void AppendValue(const std::basic_string &Val) { Values.push_back(Val); GetContainer().GetContainer().SetDirty(); } void ClearValues() { Values.clear(); GetContainer().GetContainer().SetDirty(); } ULONG Count() const { return Values.size(); } Section& GetContainer() { return Container; } const std::basic_string& GetName() const { return Name; } ULONG GetIndex() const { return Index; } friend std::ostream& operator<<(std::ostream &os, SectionValues &rhs); protected: // // data members // ULONG Index; std::basic_string Name; std::vector< std::basic_string > Values; Section &Container; }; template struct InvalidInfSection : public BaseException { public: InvalidInfSection(const std::basic_string &SecName, const std::basic_string &InfName) : Name(SecName), FileName(InfName){} std::basic_string Name; std::basic_string FileName; void Dump(std::ostream &os) { os << "InvalidInfSection : " << Name << " in " << FileName << std::endl; } }; // // forward declaration // template class InfFile; template class Section { public: Section(InfFile &file, const std::basic_string &name); Section(InfFile &file, const std::basic_string &name, bool NoKey) : File(file), Name(name), Keyless(NoKey) {} ~Section(){} const std::basic_string& GetName() const{ return Name; } SectionValues& GetValue(const std::basic_string &Key) { std::vector< SectionValues *>::iterator Iter = Lines.begin(); SectionValues *Values = NULL; while (Iter != Lines.end()) { Values = (*Iter); if (sizeof(T) == sizeof(CHAR)) { if (!_stricmp((PCSTR)Values->GetName().c_str(), (PCSTR)Key.c_str())) { break; } } else { if (!_wcsicmp((PCWSTR)Values->GetName().c_str(), (PCWSTR)Key.c_str())) { break; } } Iter++; } if (Iter == Lines.end()) { throw new InvalidValueKey(Name, Key); } return *Values; } // // Note : For the following scenario // // [Section] // a // b,c // // a & b are treated as Keys i.e. for keyless sections // the first value is treated as a key // bool IsKeyPresent(const std::basic_string &Key) const { bool Result = false; if (!IsKeyless()) { std::vector< SectionValues *>::const_iterator Iter = Lines.begin(); SectionValues *Values = NULL; while (Iter != Lines.end()) { Values = (*Iter); if (sizeof(T) == sizeof(CHAR)) { if (!_stricmp((PCSTR)Values->GetName().c_str(), (PCSTR)Key.c_str())) { break; } } else { if (!_wcsicmp((PCWSTR)Values->GetName().c_str(), (PCWSTR)Key.c_str())) { break; } } Iter++; } Result = (Iter != Lines.end()); } else { std::vector< SectionValues *>::const_iterator Iter = KeylessLines.begin(); SectionValues *Values = NULL; while (Iter != KeylessLines.end()) { Values = (*Iter); if (sizeof(T) == sizeof(CHAR)) { if (!_stricmp((PCSTR)Values->GetValue(0).c_str(), (PCSTR)Key.c_str())) { break; } } else { if (!_wcsicmp((PCWSTR)Values->GetValue(0).c_str(), (PCWSTR)Key.c_str())) { break; } } Iter++; } Result = (Iter != KeylessLines.end()); } return Result; } SectionValues& GetValue(ULONG Index) { return *(KeylessLines[Index]); } InfFile& GetContainer() { return File; } bool IsKeyless() const { return Keyless; } Section& operator+=(Section &rhs) { if (IsKeyless() == rhs.IsKeyless()) { // // add entries with key // std::vector< SectionValues *>::iterator Iter = rhs.Lines.begin(); while (Iter != rhs.Lines.end()) { Lines.push_back(*Iter); Iter++; } // // add entries without key // std::vector< SectionValues *>::iterator KlIter = rhs.KeylessLines.begin(); while (KlIter != rhs.KeylessLines.end()) { KeylessLines.push_back(*KlIter); KlIter++; } } else { throw new InvalidInfSection(File.GetName(), rhs.GetName()); } return *this; } SectionValues* AddLine(const std::basic_string &Key) { SectionValues *Value = NULL; if (Key.length() && !IsKeyless()) { Value = new SectionValues(*this, Key, 0, true); if (Value) { Lines.push_back(Value); } } else { Value = new SectionValues(*this, KeylessLines.size(), true); if (Value) { KeylessLines.push_back(Value); } } File.SetDirty(); return Value; } friend std::ostream& operator<<(std::ostream &os, Section &rhs); // // Callback function pointer for // working on each section element // typedef void (*ELEMENT_WORKER)( SectionValues &Values, void *ContextData ); void DoForEach(ELEMENT_WORKER Worker, void *ContextData); // // Iterator // class Iterator{ public: Iterator(std::vector< SectionValues *> *Collect = NULL) { Collection = Collect; if (Collection) { Iter = (*Collection).begin(); } } Iterator& begin() { if (Collection) { Iter = Collection->begin(); } return *this; } bool end() { return Collection? (Iter == Collection->end()) : true; } Iterator& operator++(int) { if (Collection) { Iter++; } return *this; } friend SectionValues * operator*(Iterator &lhs) { return lhs.Collection ? *(lhs.Iter): NULL; } friend Section; protected: void Init(std::vector< SectionValues *> *Collect) { Collection = Collect; Iter = (*Collection).begin(); } private: // // data members // std::vector< SectionValues * >::iterator Iter; std::vector< SectionValues *> *Collection; }; Iterator begin(void) { Iterator Iter; if (IsKeyless()) { if (KeylessLines.size()) { Iter.Init(&KeylessLines); } } else { if (Lines.size()) { Iter.Init(&Lines); } } return Iter; } protected: // // data members // InfFile &File; std::basic_string Name; std::vector< SectionValues *> Lines; std::vector< SectionValues *> KeylessLines; bool Keyless; }; template class InvalidInfFile : public BaseException { public: InvalidInfFile() : ErrorCode(0){} InvalidInfFile(const std::basic_string &Name) : FileName(Name), ErrorCode(0) {} InvalidInfFile(const std::basic_string &Name, DWORD ErrCode) : FileName(Name), ErrorCode(ErrCode) {} void Dump(std::ostream &os) { os << "Invalid INF file " << FileName; if (ErrorCode) { os << " (" << std::dec << ErrorCode << ") "; } os << std::endl; } protected: std::basic_string FileName; DWORD ErrorCode; }; template class InvalidInfFormat : public InvalidInfFile { public: InvalidInfFormat(const std::basic_string &Name, UINT Line) : InvalidInfFile(Name), ErrorLine(Line) {} void Dump(std::ostream &os) { os << "Invalid INF format at " << std::dec << ErrorLine << " line of " << FileName << std::endl; } UINT ErrorLine; }; // // Inf file abstraction // template class InfFile { public: InfFile(const std::basic_string &name); virtual ~InfFile() { if (InfHandle && (InfHandle != INVALID_HANDLE_VALUE)) { SetupCloseInfFile(InfHandle); } if (Dirty) { //CommitChanges(); } SetupApiUseCount--; if (!SetupApiUseCount) { FreeLibrary(SetupApiModuleHandle); GetInfSections = NULL; } } const std::basic_string& GetName() const { return Name; } friend bool operator==(const InfFile &lhs, const InfFile &rhs) { return lhs.GetName() == rhs.GetName(); } friend bool operator!=(const InfFile &lhs, const InfFile &rhs) { return !(lhs == rhs); } void GetLines(Section &Sec, std::vector< SectionValues *> &Lines, std::vector< SectionValues *> &KeylessLines); void GetValues(Section &Sec, SectionValues &SecValues, std::vector< std::basic_string > &Values); friend std::ostream& operator<<(std::ostream &os, InfFile &rhs) { InfFile::Iterator Iter = rhs.begin(); while (!Iter.end()) { os << **Iter << std::endl; Iter++; } return os; } Section* AddSection(const std::basic_string &SecName, bool Keyless) { Section *NewSection = NULL; try { NewSection = GetSection(SecName); } catch(...) { } if (!NewSection) { Sections[SecName] = NewSection = new Section(*this, SecName, Keyless); SetDirty(); } return NewSection; } Section* GetSection(const std::basic_string &SecName) { std::map< std::basic_string, Section *>::iterator Iter = Sections.find(SecName); Section* Sec = NULL; if (Iter != Sections.end()) { Sec = (*Iter).second; } return Sec; } void GetSections(std::map< std::basic_string, Section *> &Secs) { Secs = Sections; } void SetDirty() { Dirty = true; } const HINF GetInfHandle() const { return InfHandle; } // // Callback function pointer for // working on each section // typedef void (*ELEMENT_WORKER)( Section &Section, void *ContextData ); void DoForEach(ELEMENT_WORKER Worker, void *ContextData); // // Iterator // class Iterator{ public: Iterator(std::map< std::basic_string, Section *> *Collect = NULL) { Collection = Collect; if (Collection) { Iter = (*Collection).begin(); } } Iterator& begin() { if (Collection) { Iter = Collection->begin(); } return *this; } bool end() { return Collection? (Iter == Collection->end()) : true; } Iterator& operator++(int) { if (Collection) { Iter++; } return *this; } friend Section * operator*(Iterator &lhs) { return lhs.Collection ? (*(lhs.Iter)).second: NULL; } friend Section; protected: void Init(std::map< std::basic_string, Section *> *Collect) { Collection = Collect; Iter = (*Collection).begin(); } private: // // data members // std::map< std::basic_string, Section *>::iterator Iter; std::map< std::basic_string, Section *> *Collection; }; Iterator begin(void) { return Iterator(&(this->Sections)); } protected: HINF OpenFile() { HINF InfHandle = NULL; UINT ErrorLine = 0; if (sizeof(T) == sizeof(CHAR)) { InfHandle = SetupOpenInfFileA((const CHAR*)(GetName().c_str()), NULL, INF_STYLE_WIN4, &ErrorLine); } else { InfHandle = SetupOpenInfFileW((const WCHAR*)(GetName().c_str()), NULL, INF_STYLE_WIN4, &ErrorLine); } if (InfHandle == INVALID_HANDLE_VALUE) { DWORD ErrorCode = ::GetLastError(); if (ErrorLine) { throw new InvalidInfFormat(GetName(), ErrorLine); } else { throw new InvalidInfFile(GetName(), ErrorCode); } } return InfHandle; } typedef BOOL (* GetInfSectionsRoutine)(HINF, T*, UINT, UINT *); // // data members // std::basic_string Name; HINF InfHandle; bool Dirty; static GetInfSectionsRoutine GetInfSections; static HMODULE SetupApiModuleHandle; static ULONG SetupApiUseCount; std::map< std::basic_string, Section *> Sections; }; template SectionValues::SectionValues(Section &Sec, const std::basic_string &Key, ULONG LineIdx, bool New) : Container(Sec), Name(Key), Index(LineIdx) { if (!New) { GetContainer().GetContainer().GetValues(Sec, *this, Values); } } template SectionValues::SectionValues(Section &Sec, ULONG LineIdx, bool New) : Container(Sec), Index(LineIdx) { BYTE Buffer[64] = {0}; if (sizeof(T) == sizeof(CHAR)) { Name = std::basic_string((const T*)_ltoa(Index, (char *)Buffer, 10)); } else { Name = std::basic_string((const T*)_ltow(Index, (wchar_t*)Buffer, 10)); } if (!New) { GetContainer().GetContainer().GetValues(Sec, *this, Values); } } template std::ostream& operator<<(std::ostream &os, SectionValues &rhs) { os << rhs.GetName() << " = "; std::vector< std::basic_string >::iterator Iter = rhs.Values.begin(); while (Iter != rhs.Values.end()) { os << *Iter << ", "; Iter++; } os << std::endl; return os; } template Section::Section(InfFile &file, const std::basic_string &name) : Name(name), File(file), Keyless(false) { INFCONTEXT InfContext; BOOL Result; const HINF InfHandle = GetContainer().GetInfHandle(); if (sizeof(T) == sizeof(CHAR)) { Result = SetupFindFirstLineA((HINF)InfHandle, (PCSTR)GetName().c_str(), NULL, &InfContext); } else { Result = SetupFindFirstLineW((HINF)InfHandle, (PCWSTR)GetName().c_str(), NULL, &InfContext); } //std::cout << Name << " is " << Keyless << std::endl; if (Result) { BYTE Buffer[4096], Buffer1[4096]; bool KeyPresent = false; // // NOTE : singular values in section are treated as keys // by setupapi so take care of such cases correctly // as keyless entries // // ISSUE : because of the way we are trying to determine // keyless section a sections with first entry a = a will // be treated as keyless section wrongly. // if (sizeof(T) == sizeof(CHAR)) { *((PSTR)Buffer) = '\0'; *((PSTR)Buffer1) = '\0'; if (SetupGetStringFieldA(&InfContext, 0, (PSTR)Buffer, sizeof(Buffer) / sizeof(T), NULL) && SetupGetStringFieldA(&InfContext, 1, (PSTR)Buffer1, sizeof(Buffer1) / sizeof(T), NULL)) { KeyPresent = (_stricmp((PCSTR)Buffer, (PCSTR)Buffer1) != 0); } } else { *((PWSTR)Buffer) = L'\0'; *((PWSTR)Buffer1) = L'\0'; if (SetupGetStringFieldW(&InfContext, 0, (PWSTR)Buffer, sizeof(Buffer) / sizeof(T), NULL) && SetupGetStringFieldW(&InfContext, 1, (PWSTR)Buffer1, sizeof(Buffer1) / sizeof(T), NULL)) { KeyPresent = (_wcsicmp((PCWSTR)Buffer, (PCWSTR)Buffer1) != 0); } } // If we cannot read 0th value, then we // assume that the whole section is // doesnot have entries with key // Keyless = !KeyPresent; } GetContainer().GetLines(*this, Lines, KeylessLines); //std::cout << Name << " is " << Keyless << std::endl; } template std::ostream& operator<<(std::ostream &os, Section &rhs){ os << "[" << rhs.GetName() << "]" << std::endl; Section::Iterator Iter = rhs.begin(); SectionValues *Values; while (!Iter.end()) { Values = *Iter; if (Values) { os << *Values; } Iter++; } return os; } template void Section::DoForEach(Section::ELEMENT_WORKER Worker, void *ContextData) { if (IsKeyless()) { std::vector< SectionValues * >::iterator Iter = KeylessLines.begin(); SectionValues *Value; while (Iter != KeylessLines.end()) { Value = *Iter; if (Value) { Worker(*Value, ContextData); } Iter++; } } else { std::vector< SectionValues *>::iterator Iter = Lines.begin(); SectionValues *Value; while (Iter != Lines.end()) { Value = *Iter; if (Value) { Worker(*Value, ContextData); } Iter++; } } } template InfFile::InfFile(const std::basic_string &name) : Name(name) { Dirty = false; InfHandle = OpenFile(); if (!GetInfSections) { SetupApiModuleHandle = LoadLibrary(TEXT("setupapi.dll")); if (SetupApiModuleHandle) { GetInfSections = (GetInfSectionsRoutine)(GetProcAddress(SetupApiModuleHandle, "pSetupGetInfSections")); if (!GetInfSections) { GetInfSections = (GetInfSectionsRoutine)(GetProcAddress(SetupApiModuleHandle, "SetupGetInfSections")); } } if (!GetInfSections) { throw new W32Exception(); } } SetupApiUseCount++; // // get hold of all the sections // UINT CurrSize = 4096; UINT SizeNeeded = 0; DWORD LastError = 0; WCHAR *Buffer = new WCHAR[CurrSize]; memset(Buffer, 0, CurrSize); BOOL Result = GetInfSections(InfHandle, Buffer, CurrSize, &SizeNeeded); if (!Result && (SizeNeeded > CurrSize)) { delete []Buffer; Buffer = new WCHAR[SizeNeeded]; memset(Buffer, 0, SizeNeeded); CurrSize = SizeNeeded; Result = GetInfSections(InfHandle, Buffer, CurrSize, &SizeNeeded); } std::vector > SectionNames; if (Result) { while (Buffer && *Buffer) { std::basic_string WNextSection((const WCHAR*)Buffer); if (sizeof(T) == sizeof(CHAR)) { std::basic_string NextSection; ToAnsiString((std::basic_string &)NextSection, WNextSection); SectionNames.push_back(NextSection); } else { std::basic_string NextSection((const T *)WNextSection.c_str()); SectionNames.push_back(NextSection); } Buffer += (WNextSection.length() + 1); } } else { LastError = ::GetLastError(); } if (Result && SectionNames.size()) { std::vector >::iterator NameIter = SectionNames.begin(); while (NameIter != SectionNames.end()) { // std::cout << *NameIter << std::endl; Sections[*NameIter] = new Section(*this, *NameIter); NameIter++; } } if (!Result) { throw new InvalidInfFile(GetName(), LastError); } } template void InfFile::DoForEach( InfFile::ELEMENT_WORKER Worker, void *ContextData ) { std::map< std::basic_string, Section *>::iterator Iter = Sections.begin(); while (Iter != Sections.end()) { Worker(*(*Iter).second, ContextData); Iter++; } } template void InfFile::GetLines( Section &Sec, std::vector< SectionValues *> &Values, std::vector< SectionValues *> &KeylessValues ) { std::vector< SectionValues* >::iterator MapIter = Values.begin(); // // delete the old values (if any) // while (MapIter != Values.end()) { if (*MapIter) { delete (*MapIter); } MapIter++; } Values.clear(); std::vector< SectionValues* >::iterator KeylessIter = KeylessValues.begin(); // // delete the old values (if any) // while (KeylessIter != KeylessValues.end()) { if (*KeylessIter) { delete (*KeylessIter); } KeylessIter++; } KeylessValues.clear(); // // locate the first line // INFCONTEXT InfContext; BOOL Result; DWORD LineCount; if (sizeof(T) == sizeof(CHAR)) { LineCount = SetupGetLineCountA(InfHandle, (PCSTR)Sec.GetName().c_str()); } else { LineCount = SetupGetLineCountW(InfHandle, (PCWSTR)Sec.GetName().c_str()); } if (LineCount) { if (sizeof(T) == sizeof(CHAR)) { Result = SetupFindFirstLineA(InfHandle, (PCSTR)Sec.GetName().c_str(), NULL, &InfContext); } else { Result = SetupFindFirstLineW(InfHandle, (PCWSTR)Sec.GetName().c_str(), NULL, &InfContext); } if (Result) { BYTE Buffer[4096]; bool Read = false; BOOL NextLine = TRUE; DWORD Index = 0; bool Keyless = Sec.IsKeyless(); for (Index=0; (NextLine && (Index < LineCount)); Index++) { Buffer[0] = Buffer[1] = 0; Read = false; if (!Keyless) { if (sizeof(T) == sizeof(CHAR)) { if (SetupGetStringFieldA(&InfContext, 0, (PSTR)Buffer, sizeof(Buffer) / sizeof(T), NULL)) { Read = true; } } else { if (SetupGetStringFieldW(&InfContext, 0, (PWSTR)Buffer, sizeof(Buffer) / sizeof(T), NULL)) { Read = true; } } } if (Read) { std::basic_string Key((const T *)Buffer); //std::cout << Key << std::endl; Values.push_back(new SectionValues(Sec, Key, Index)); } else { KeylessValues.push_back(new SectionValues(Sec, Index)); } NextLine = SetupFindNextLine(&InfContext, &InfContext); } if (!NextLine && (Index < LineCount)) { throw new InvalidInfSection(Sec.GetName(), GetName()); } } else { throw new InvalidInfSection(Sec.GetName(), GetName()); } } } template void InfFile::GetValues( Section &Sec, SectionValues &SecValues, std::vector > &Values ) { const std::basic_string &SecName = Sec.GetName(); const std::basic_string &Key = SecValues.GetName(); INFCONTEXT InfContext; BOOL Result = TRUE; ULONG Lines = 0; Values.clear(); if (sizeof(T) == sizeof(CHAR)) { Lines = SetupGetLineCountA(InfHandle, (PCSTR)SecName.c_str()); } else { Lines = SetupGetLineCountW(InfHandle, (PCWSTR)SecName.c_str()); } if (Lines) { if (sizeof(T) == sizeof(CHAR)) { Result = SetupGetLineByIndexA(InfHandle, (PCSTR)SecName.c_str(), SecValues.GetIndex(), &InfContext); } else { Result = SetupGetLineByIndexW(InfHandle, (PCWSTR)SecName.c_str(), SecValues.GetIndex(), &InfContext); } if (Result) { DWORD FieldCount = SetupGetFieldCount(&InfContext); BYTE Buffer[2048]; for (DWORD Index=0; Index < FieldCount; Index++) { Buffer[0] = Buffer[1] = 0; if (sizeof(T) == sizeof(CHAR)) { if (SetupGetStringFieldA(&InfContext, Index + 1, (PSTR)Buffer, sizeof(Buffer) / sizeof(T), NULL)) { Values.push_back((const T *)Buffer); } } else { if (SetupGetStringFieldW(&InfContext, Index + 1, (PWSTR)Buffer, sizeof(Buffer) / sizeof(T), NULL)) { Values.push_back((const T *)Buffer); } } } } else { throw new InvalidInfSection(SecName, GetName()); } } } typedef InfFile InfFileA; typedef InfFile InfFileW;