unit Parseinf;

interface

uses Classes, windows, SysUtils, Dialogs, listdriver, mysetupApi, StrUtils,
  shellapi, controls, cDataStructs,
  inifiles, CommCtrl;

type
  Tver = record
    item: array [0 .. 7] of integer;
  end;


Const
  _Version = 'VERSION';
  _Manufacturer = 'MANUFACTURER';
  _Strings = 'STRINGS';
  _StringsSection = '[STRINGS]';
  _ManufacturerSection = '[MANUFACTURER]';

  _VersionSection = '[VERSION]';
  _Class = 'CLASS';
  _Provider = 'PROVIDER';
  _CatalogFile = 'CatalogFile';
  _DriverVer = 'DRIVERVER';

  _SourceDisksFilesSection = '[SourceDisksFiles]';
  _DestinationDirsSection = '[DestinationDirs]';

  DriverDesc = 'DriverDesc';
  ProviderName = 'ProviderName';
  DriverDate = 'DriverDate';
  DriverVersion = 'DriverVersion';
  MatchingDeviceId = 'MatchingDeviceId';
  InfPath = 'InfPath';
  InfSection = 'InfSection';
  InfSectionExt = 'InfSectionExt';

  NT_ZZ: array [1 .. 4] of string = ('.NTX86', 'NTAMD64', 'NTIA64', '.NT');
  NT_VER: array [1 .. 8] of string = ('.5.0', '.5.1', '.5.2', '.5', '.6.0',
    '.6.1', '.6.2', '.6');

 
  // Manufacturer

// function CRC32(Source:AnsiString):Integer;
function FileSize(Path: String): integer;
function IsOnWow64: Boolean; // определение разрядности 64бит

function GetTypestr(str: pwidechar): integer;
function GetSystemDirectory: string;
function GetTempDirectory: String;
// procedure RemoveFolder(path: string);
procedure RemoveFolder2(Path: string);

procedure IndexInf(var ALStrings: array of tstringlist;
  INF_folder, INF_filename, OS_ByDP: AnsiString);

procedure IndexInfwithEmptyMFG(var ALStrings: array of tstringlist;
  INF_folder, INF_filename, OS_ByDP: AnsiString;var EmptyMFG: AnsiString);

function get_OS_id(GB_Major: DWORD; GB_64bit: Boolean): DWORD;
function getDP_OS(Dpdir: string): DWORD;
function SET_DP_OS(Dpname: string): string;

function GetDeviceList(): TDriverList;
function DeleteDevice(Index: integer; HWID0: string): Boolean;

function GetFirstHWID(input: string): string;
function GetPreparedHWID(input: string): string;

function ExtractMultiString(const Value: AnsiString): AnsiString;

function Is2Larger(Ver1, Ver2: Tver): Boolean;
function GetTver(input: string): Tver;
function CompareTver(Ver1, Ver2: Tver): integer;
//function GetBuildTime: TDateTime;
procedure ScanDir(StartDir, mask: String; recurse: Boolean; List: tstringlist);

function GetOS_MFG(in_MFG: AnsiString): AnsiString;
implementation

uses unit1;

function getDP_OS(Dpdir: string): DWORD;
var
  i: integer;
begin
  result := 0;

  for i := 1 to 3 do
    if pos('\' + GB_DRP_DIR + '\' + GB_DRP_DIR + '\', Dpdir) > 0 then
    begin
      result := i;
      exit;
    end;

  if pos('\' + GB_DRP_DIR + '\', Dpdir) > 0 then
  begin
    result := 0;
    exit;
  end;

end;

function SET_DP_OS(Dpname: string): string;
var
  ktip: integer;
begin

  ktip := getDP_OS(Dpname);

  result := ChangeFileExt(Dpname, '_' + GB_DRP_DIR + INI_ext);

end;

function FileSize(Path: String): integer;
var
  searchResult: TSearchRec;
begin

  if FindFirst(Path, faAnyFile, searchResult) = 0 then
    result := searchResult.SIZE
  else
    result := -1;

  FindClose(searchResult);
end;

function IsOnWow64: Boolean;
var
  IsWow64Process: function(hProcess: THandle; var Wow64Process: BOOL)
    : BOOL; stdcall;
  Wow64Process: BOOL;
begin
  result := False;
  IsWow64Process := GetProcAddress(GetModuleHandle(Kernel32), 'IsWow64Process');
  if Assigned(IsWow64Process) then
    if IsWow64Process(GetCurrentProcess, Wow64Process) then
    begin
      result := Wow64Process;
    end;
end;

function GetTypestr(str: pwidechar): integer;
// var
begin
  result := 0;
  if (str = nil) then
  begin
    result := 0;
    exit; // anee no?iea ionoay, oi ii eaaa pchar aie?ai auou ?aaai nil
  end;
  While not(str[0] = #0) do
  begin
    case str[0] of
      ';':
        begin
          result := 0;
          exit;
        end;
      '[':
        begin
          result := 1;
          exit;
        end;
      '=':
        begin
          result := 2;
          exit;
        end;
      '%':
        begin
          result := 2;
          exit;
        end;
    end;
    inc(str);
  end;
end;

function GetSystemDirectory: string;
var
  SysDir: PChar;
begin
  SysDir := StrAlloc(MAX_PATH);
  windows.GetSystemDirectory(SysDir, MAX_PATH);
  result := string(SysDir);
  if result[Length(result)] <> '\' then
    result := result + '\';
  StrDispose(SysDir);
end;

function GetTempDirectory: String;
var
  tempFolder: array [0 .. MAX_PATH] of Char;
begin
  GetTempPath(MAX_PATH, @tempFolder);
  result := StrPas(tempFolder);
end;

procedure RemoveFolder2(Path: string);
var
  lpFileOp: TSHFileOpStruct;
begin
  FillChar(lpFileOp, SizeOf(lpFileOp), 0);
  lpFileOp.Wnd := 0;
  lpFileOp.wFunc := FO_DELETE;
  if Path[Length(Path)] = '\' then
    Path[Length(Path)] := #0;
  lpFileOp.pFrom := PChar(Path + #0);
  // showmessage(path+#0);
  // MessageBox(0,pchar(path),nil,mb_ok);
  lpFileOp.fFlags := FOF_NOCONFIRMATION or FOF_SILENT;
  SHFileOperation(lpFileOp);
end;

{ procedure RemoveFolder(path: string);
  var
  sr: TSearchRec;
  begin
  if FindFirst(path + '\*.*', faAnyFile, sr) = 0 then
  begin
  repeat
  if sr.Attr and faDirectory = 0 then
  begin
  DeleteFile(path + '\' + sr.name);
  end
  else
  begin
  if pos('.', sr.name) <= 0 then
  RemoveFolder(path + '\' + sr.name);
  end;
  until
  FindNext(sr) <> 0;
  end;
  FindClose(sr);
  RemoveDirectory(PChar(path));
  end; }

function get_OS_id(GB_Major: DWORD; GB_64bit: Boolean): DWORD;
var
  // INIfileDP:TIniFile;
  needOS: DWORD;
  // TMPList:Tstringlist;

begin
  needOS := 0;
  case GB_Major of
    5:
      needOS := 1;
    6:
      needOS := 2;
  end;

  if GB_64bit then
    needOS := 3;

  result := needOS;
end;

function ExtractMultiString(const Value: AnsiString): AnsiString;
var
  P: PAnsiChar;
  RES: AnsiString;
begin
  P := @Value[1];
  while P^ <> #0 do
  begin
    if RES <> '' then
      RES := RES + ', ';
    RES := RES + P;
    inc(P, lstrlenA(P) + 1);
  end;
  result := RES;
end;

function DeleteDevice(Index: integer; HWID0: string): Boolean;
var
  hAllDevices: Pointer;
  Data: TSPDevInfoData;
  // NewDriver:TDriverDef;
  // hkey1:HKEY;
  dwInfo: DWORD;
  dwRequired: cardinal;
  k: string;
  // BUFF:array[0..511] of char;
  // dwSize,
  // dwType:Cardinal;
  // BFSIZE:dword;
  TypeDev: DWORD;

  HWIDS: string;
begin

  result := False;

  // BFSIZE:=512;
  // ShowMessage(HWID0);
  // Memo1.Clear;
  // Strings1:=TStringList.Create;

  { hDev:=SetupDiCreateDeviceInfoList(nil, 0);
    If cardinal(hDev)=INVALID_HANDLE_VALUE then
    begin
    ShowMessage('Error in SetupDiCreateDeviceInfoList');
    exit;
    end; }
  hAllDevices := SetupDiGetClassDevsExA(nil, nil, 0, DIGCF_PRESENT or
    DIGCF_ALLCLASSES, nil, nil, nil);
  // hAllDevices:=SetupDiGetClassDevsA(nil,nil,0,DIGCF_ALLCLASSES); //DIGCF_PRESENT or

  If cardinal(hAllDevices) = INVALID_HANDLE_VALUE then
  begin
    ShowMessage('Error in SetupDiGetClassDevsExA');
    result := False;
    exit;
  end;
  FillChar(Data, SizeOf(SP_DEVINFO_DATA), 0);
  Data.cbSize := SizeOf(SP_DEVINFO_DATA);
  dwInfo := 0;
  If not SetupDiEnumDeviceInfo(hAllDevices, dwInfo, Data) then
  begin
    ShowMessage('Error in SetupDiEnumDeviceInfo');
    result := False;
    exit;
  end;

  // buf:=StrAlloc(BFSIZE);
  dwRequired := 0;

  if SetupDiEnumDeviceInfo(hAllDevices, index, Data) then
  begin

    // dwType:=0;
    dwRequired := 512;
    k := '';
    SetLength(k, dwRequired);
    If SetupDiGetDeviceRegistryPropertyA(hAllDevices, Data, SPDRP_HARDWAREID,
      TypeDev, @k[1], dwRequired, dwRequired) then
    begin
      HWIDS := AnsiUpperCase(ExtractMultiString(k)) + ',';
    end;

    dwRequired := 512;
    k := '';
    SetLength(k, dwRequired);
    If SetupDiGetDeviceRegistryPropertyA(hAllDevices, Data, SPDRP_COMPATIBLEIDS,
      TypeDev, @k[1], dwRequired, dwRequired) then
    begin
      HWIDS := HWIDS + AnsiUpperCase(ExtractMultiString(k));
    end;

    result := SetupDiRemoveDevice(hAllDevices, Data);

    // ShowMessage(HWIDS);
  end; // While SetupDiEnumDeviceInfo(hAllDevices, dwInfo, Data) do

  SetupDiDestroyDeviceInfoList(hAllDevices);

  // StaticText1.Caption:='Время обработки: '+IntToStr(GetTickCount-TIKTAK)+' мс  Найдено устройств: ' +IntToStr(Result.count);
end;

function GetDeviceList(): TDriverList;
var
  hAllDevices: Pointer;

  Data: TSPDevInfoData;
  // Data:SP_DEVINFO_DATA;
  NewDriver: TDriverDef;
  hkey1: HKEY;

  dwInfo: DWORD;
  dwRequired: cardinal;

  // buf:PChar;
  // buf2:pchar;
  k: AnsiString;
  BUFF: array [0 .. 511] of ansichar;
  //BUFF: PAnsichar; //
  //Buff_pchar:pansichar;

  dwSize, dwType: cardinal;
  BFSIZE: DWORD;
  TypeDev: DWORD;

  dwStatus, dwProblemNumber: DWORD;
  Date00: AnsiString;
  Classstring: AnsiString;
begin
  ShortDateFormat := 'MM/DD/YYYY';
  DateSeparator := '/';

  BFSIZE := 512;

  //buff:=AnsiStrAlloc(BFSIZE+5);
  FillChar(buff, 500, 0);

  // Memo1.Clear;
  // Strings1:=TStringList.Create;

  { hDev:=SetupDiCreateDeviceInfoList(nil, 0);
    If cardinal(hDev)=INVALID_HANDLE_VALUE then
    begin
    ShowMessage('Error in SetupDiCreateDeviceInfoList');
    exit;
    end; }
  // hAllDevices:=SetupDiGetClassDevEX(nil, nil, 0, DIGCF_PRESENT,  nil, nil, nil);
  hAllDevices := SetupDiGetClassDevsA(nil, nil, 0, DIGCF_ALLCLASSES or
    DIGCF_PRESENT);

  If cardinal(hAllDevices) = INVALID_HANDLE_VALUE then
  begin
    ShowMessage('Error in SetupDiGetClassDevsExA');
    result := nil;
    exit;
  end;
  FillChar(Data, SizeOf(SP_DEVINFO_DATA), 0);
  Data.cbSize := SizeOf(SP_DEVINFO_DATA);
  dwInfo := 0;
  {If not SetupDiEnumDeviceInfo(hAllDevices, dwInfo, Data) then
  begin
    ShowMessage('Error in SetupDiEnumDeviceInfo');
    result := nil;
    exit;
  end;           }

  // buf:=StrAlloc(BFSIZE);
  dwRequired := 0;
  result := TDriverList.Create(TDriverDef);
  // Result.
  NewDriver:=nil;
  While SetupDiEnumDeviceInfo(hAllDevices, dwInfo, Data) do
  begin
     //Application.ProcessMessages;

    NewDriver := result.Add;

    // buf:=StrAlloc(BFSIZE);
    dwRequired := 0;

    // SetupDiGetDeviceRegistryPropertyA(DeviceListHandle, DeviceInfoData,
    // PropertyCode, dwPropertyRegDataType, @Result[1],
    // dwRequiredSize, dwRequiredSize);
    //Buff_pchar:= new Ansichar[513];


    //FillChar(buff,512,0);
    dwType := 0;
    If SetupDiGetDeviceRegistryPropertyA(hAllDevices, Data, SPDRP_FRIENDLYNAME,
      dwType, @BUFF[0], BFSIZE, dwRequired)
      //dwType, @BUFF_pchar[0], BFSIZE, dwRequired)
    // If SetupDiGetDeviceRegistryPropertyA(hAllDevices, @Data, SPDRP_FRIENDLYNAME, nil, @bufF, BFSIZE, @dwRequired)
    then
    begin
      NewDriver.SPDRP_FRIENDLYNAME := BUFF;
    end
    else If SetupDiGetDeviceRegistryPropertyA(hAllDevices, Data,
      SPDRP_DEVICEDESC, dwType, @BUFF[0], BFSIZE, dwRequired) then
    begin
      NewDriver.SPDRP_DEVICEDESC := BUFF;
    end;


    dwRequired := 512;
    k := '';
    SetLength(k, dwRequired);
    If SetupDiGetDeviceRegistryPropertyA(hAllDevices, Data, SPDRP_HARDWAREID,
      TypeDev, @k[1], dwRequired, dwRequired) then
    begin
      NewDriver.SPDRP_HARDWAREID := AnsiUpperCase(ExtractMultiString(k));
    end;

    dwRequired := 512;
    k := '';
    SetLength(k, dwRequired);
    If SetupDiGetDeviceRegistryPropertyA(hAllDevices, Data, SPDRP_COMPATIBLEIDS,
      TypeDev, @k[1], dwRequired, dwRequired) then
    begin
      NewDriver.SPDRP_COMPATIBLEIDS := AnsiUpperCase(ExtractMultiString(k));
    end;

    // buf:=StrAlloc(BFSIZE);
    // FillChar(buf^, BFSIZE, #0);
    dwType := 0;
    dwRequired := 0;
    If SetupDiGetDeviceRegistryPropertyA(hAllDevices, Data, SPDRP_MFG, dwType,
      @BUFF[0], BFSIZE, dwRequired) then
    begin
      NewDriver.SPDRP_MFG := BUFF;
    end;


    // buf:=StrAlloc(BFSIZE);
    // FillChar(buf^, BFSIZE, #0);
    dwRequired := 0;
    If SetupDiGetDeviceRegistryPropertyA(hAllDevices, Data, SPDRP_DRIVER,
      dwType, @BUFF[0], BFSIZE, dwRequired) then
      NewDriver.SPDRP_DRIVER := BUFF;


    if CM_Get_DevNode_Status(dwStatus, dwProblemNumber, Data.DevInst, 0) = 0
    then
    begin
      NewDriver.status := dwStatus;
      NewDriver.problem := dwProblemNumber;
    end;

    // ShowMessage(BUFF);

    // RegOpenKeyExA(HKEY_LOCAL_MACHINE,
    // PChar('SYSTEM\CurrentControlSet\Control\Class\' + BUFF), 0, KEY_READ or KEY_WOW64_64KEY,
    // hkey1);//

    // 11geter GetLastError

    Classstring := 'SYSTEM\CurrentControlSet\Control\Class\' + BUFF;
    // StrCat(Classstring,buff);
    // Classstring:='SYSTEM\CurrentControlSet\Control\Class\' + @BUFF;
    if RegOpenKeyExA(HKEY_LOCAL_MACHINE, PAnsiChar(Classstring), 0,
      KEY_QUERY_VALUE or KEY_WOW64_32KEY, hkey1) = ERROR_SUCCESS then
    // KEY_QUERY_VALUE or KEY_WOW64_32KEY
    begin
      dwType := REG_SZ;
      dwSize := 512;

      // GetMem(Buf2, dwSize);
      // if RegOpenKeyEx(HKEY_LOCAL_MACHINE, pchar('SYSTEM\CurrentControlSet\Control\Class\'+str1), 0, KEY_QUERY_VALUE or KEY_WOW64_32KEY, hkey1)=ERROR_SUCCESS then

      if RegQueryValueExA(hkey1, DriverDesc, nil, @dwType, @BUFF[0], @dwSize) = ERROR_SUCCESS
      then
        NewDriver.DriverDesc := BUFF;

        //ShowMessage('BUG');


      dwSize := 512;
      if RegQueryValueExA(hkey1, ProviderName, nil, @dwType, @BUFF[0], @dwSize) = ERROR_SUCCESS
      then
        NewDriver.ProviderName := BUFF;

      dwSize := 512;
      // ShowMessage(DriverDate);
      if RegQueryValueExA(hkey1, DriverDate, nil, @dwType, @BUFF[0], @dwSize) = ERROR_SUCCESS
      then
      begin
        // NewDriver.DriverDate:=bufF;
        // ShowMessage(BUFF);
        Date00 := BUFF;
        if Length(Date00) > 0 then
        begin
          Date00 := AnsiReplaceStr(Date00, '-', '/');
          if Date00[2] = '/' then
            Date00 := '0' + Date00;

          NewDriver.DriverDate := StrToDate(Date00);
        end
      end
      else

        NewDriver.DriverDate := 0;

      dwSize := 512;
      if RegQueryValueExA(hkey1, DriverVersion, nil, @dwType, @BUFF[0], @dwSize) = ERROR_SUCCESS
      then
        NewDriver.DriverVersion := BUFF;

      dwSize := 512;
      if RegQueryValueExA(hkey1, MatchingDeviceId, nil, @dwType, @BUFF[0], @dwSize)
        = ERROR_SUCCESS then
        NewDriver.MatchingDeviceId := AnsiUpperCase(BUFF);

      dwSize := 512;
      if RegQueryValueExA(hkey1, InfPath, nil, @dwType, @BUFF[0], @dwSize) = ERROR_SUCCESS
      then
        NewDriver.InfPath := BUFF;

      dwSize := 512;
      if RegQueryValueExA(hkey1, InfSection, nil, @dwType, @BUFF[0], @dwSize) = ERROR_SUCCESS
      then
        NewDriver.InfSection := BUFF;

      dwSize := 512;
      if RegQueryValueExA(hkey1, InfSectionExt, nil, @dwType, @BUFF[0], @dwSize) = ERROR_SUCCESS
      then
        NewDriver.InfSectionExt := BUFF;

    end;

    inc(dwInfo);
  end;

  SetupDiDestroyDeviceInfoList(hAllDevices);

  // StaticText1.Caption:='Время обработки: '+IntToStr(GetTickCount-TIKTAK)+' мс  Найдено устройств: ' +IntToStr(Result.count);
end;

function GetOS_MFG(in_MFG: AnsiString): AnsiString;
var
  Point: PAnsiChar;
  ntpos, i: integer;
  res1, res2: integer;
  MFG: AnsiString;

  // 'X86','X64','X32','WXP','WNT5','WNT6',
begin
  MFG := AnsiUpperCase(in_MFG);
  Point := PAnsiChar(MFG);
  result := '';
  res1 := 0;
  res2 := 0;

  for i := 1 to 4 do
  begin
    ntpos := pos(NT_ZZ[i], Point);
    if ntpos > 0 then
    begin
      res1 := i;
      break;
    end;
  end;

  for i := 1 to 8 do
  begin
    ntpos := pos(NT_VER[i], Point);
    if ntpos > 0 then
    begin
      res2 := i;
      break;
    end;
  end;

  if res2 in [4 .. 8] then
    result := 'NT6_';

  if res2 in [1 .. 4] then
    result := 'NT5_';

  if res2 = 0 then
    result := 'ALL_';

  if res1 in [0, 1, 4] then
    result := result + '32BIT';

  if res1 in [2, 3] then
    result := result + '64BIT';
  // ShowMessage(Point);
  if (res1 = 0) and (res2 = 0) then
    result := '';
end;

procedure IndexInf(var ALStrings: array of tstringlist;
  INF_folder, INF_filename, OS_ByDP: AnsiString);
label GETNEXTLINE;
Var
  hinf1: Pointer;
  aContext: INFCONTEXT;

  aReturnBuffer: array [0 .. 128] of ansichar;
  aReturnBuffer2: array [0 .. 128] of ansichar;

  aReturnBufferSize: DWORD;
  needsize: DWORD;

  TNAME, THWID: AnsiString;
  DRV_Ver: AnsiString;
  DRV_Date: AnsiString;
  Tinffilename: AnsiString;
  Tinfdir: AnsiString;

  Manucount: integer;
  FielCount: integer;
  i, L: integer;

  FirstMFG, CurMFG: AnsiString;
  ConHWID: INFCONTEXT;
  ConHWID2: INFCONTEXT;
  // CurrentIndex:TDriverIndex;
  // CurrentIndexList:TDriverIndexList;
  // hcount,X3:integer;
  IsHWNext: Boolean;

  sectionWR: AnsiString;
  I_sec, KB: integer;

begin

  Tinffilename := ExtractFileName(INF_filename);
  Tinfdir := ExtractFilePath(INF_filename);
  // TinfDir:=ExtractFilePath(INF_filename);

  hinf1 := SetupOpenInfFileA(PAnsiChar(INF_folder + INF_filename), nil,
    INF_STYLE_WIN4, nil);
  if (DWORD(hinf1) = INVALID_HANDLE_VALUE) then
  begin
    // ShowMessage('INVALID_HANDLE_VALUE'+#13+'Exiting Procedure !!!');
    // memo2.Lines.Add('INVALID_HANDLE_VALUE'+#13+'Exiting Procedure !!!');
    // memo2.Lines.Add(INF_filename);

    exit;
  end;

  // TDriverDate:=0;
  if SetupFindFirstLineA(hinf1, _Version, _DriverVer, aContext) then
  begin
    DRV_Date := '';
    aReturnBufferSize := 128;
    SetupGetStringFieldA(aContext, 1, @aReturnBuffer[0],
      aReturnBufferSize, nil);
    if aReturnBufferSize > 0 then
      DRV_Date := aReturnBuffer;
    DRV_Ver := '';
    SetupGetStringFieldA(aContext, 2, @aReturnBuffer[0],
      aReturnBufferSize, nil);
    if aReturnBufferSize > 0 then
      DRV_Ver := aReturnBuffer;

  end;

  if SetupFindFirstLineA(hinf1, _Manufacturer, nil, ConHWID) then
  begin
    Manucount := SetupGetLineCountA(hinf1, _Manufacturer);

    for i := 0 to Manucount - 1 do
    begin
      FielCount := SetupGetFieldCount(ConHWID);
      // Mymemo.Add('Fieldcount='+IntToStr(FielCount));
      For L := 1 to FielCount do
      begin
        aReturnBufferSize := 127;
        SetupGetStringFieldA(ConHWID, L, @aReturnBuffer[0],
          aReturnBufferSize, nil);

        if L > 1 then
          CurMFG := FirstMFG + '.' + aReturnBuffer
        else
        begin
          CurMFG := aReturnBuffer;
          FirstMFG := aReturnBuffer;
        end;

        // Hcount:=SetupGetLineCountA(hinf1,pchar(CurM));
        // Mymemo.Add('hcount='+IntToStr(Hcount));
        IsHWNext := SetupFindFirstLineA(hinf1, PAnsiChar(CurMFG), nil, ConHWID2);

        KB := 0;
        sectionWR := GetOS_MFG(CurMFG);
        if sectionWR = '' then
          sectionWR := OS_ByDP;
        for I_sec := 0 to 5 do
          if pos(AnsiUpperCase(NT_ALL[I_sec]), AnsiUpperCase(sectionWR)) > 0
          then
            KB := I_sec;

        // if (KB>6) or (KB<1) then ShowMessage(curm);
        // for X3:=0 to Hcount-1 do
        while IsHWNext do
        begin
          aReturnBufferSize := 127;
          needsize := 0;
          SetupGetStringFieldA(ConHWID2, 2, @aReturnBuffer2, aReturnBufferSize,
            @needsize);
          if needsize < 3 then
            SetupGetStringFieldA(ConHWID2, 3, @aReturnBuffer2,
              aReturnBufferSize, @needsize);

          if needsize < 3 then
            goto GETNEXTLINE;

          THWID := aReturnBuffer2;

          SetupGetStringFieldA(ConHWID2, 0, @aReturnBuffer,
            aReturnBufferSize, nil);
          TNAME := aReturnBuffer;

          ALStrings[KB].Add(
            // CurrentIndexList:=TDriverIndexList.Create;
            THWID + #0 + Tinfdir + #0 + Tinffilename + #0 + CurMFG + #0 + DRV_Date
            + #0 + DRV_Ver + #0 + TNAME + #0);
          // result1.Add(THWID,CurrentIndex);

        GETNEXTLINE:
          IsHWNext := SetupFindNextLine(ConHWID2, ConHWID2);
        end;

      end;
      SetupFindNextLine(ConHWID, ConHWID);
    end;

  end;

  SetupCloseInfFile(hinf1);

end;

procedure IndexInfwithEmptyMFG(var ALStrings: array of tstringlist;
  INF_folder, INF_filename, OS_ByDP: AnsiString;var EmptyMFG: AnsiString);
label GETNEXTLINE;
Var
  hinf1: Pointer;
  aContext: INFCONTEXT;

  aReturnBuffer: array [0 .. 128] of ansichar;
  aReturnBuffer2: array [0 .. 128] of ansichar;

  aReturnBufferSize: DWORD;
  needsize: DWORD;

  TNAME, THWID: AnsiString;
  DRV_Ver: AnsiString;
  DRV_Date: AnsiString;
  Tinffilename: AnsiString;
  Tinfdir: AnsiString;

  Manucount: integer;
  Emptycount:integer;
  FielCount: integer;
  i, L: integer;

  FirstMFG, CurMFG: AnsiString;
  ConHWID: INFCONTEXT;
  ConHWID2: INFCONTEXT;
  // CurrentIndex:TDriverIndex;
  // CurrentIndexList:TDriverIndexList;
  // hcount,X3:integer;
  IsHWNext: Boolean;

  sectionWR: AnsiString;
  I_sec, KB: integer;

begin

  Tinffilename := ExtractFileName(INF_filename);
  Tinfdir := ExtractFilePath(INF_filename);
  // TinfDir:=ExtractFilePath(INF_filename);

  hinf1 := SetupOpenInfFileA(PAnsiChar(INF_folder + INF_filename), nil,
    INF_STYLE_WIN4, nil);
  if (DWORD(hinf1) = INVALID_HANDLE_VALUE) then
  begin
    // ShowMessage('INVALID_HANDLE_VALUE'+#13+'Exiting Procedure !!!');
    // memo2.Lines.Add('INVALID_HANDLE_VALUE'+#13+'Exiting Procedure !!!');
    // memo2.Lines.Add(INF_filename);

    exit;
  end;

  // TDriverDate:=0;
  if SetupFindFirstLineA(hinf1, _Version, _DriverVer, aContext) then
  begin
    DRV_Date := '';
    aReturnBufferSize := 128;
    SetupGetStringFieldA(aContext, 1, @aReturnBuffer[0],
      aReturnBufferSize, nil);
    if aReturnBufferSize > 0 then
      DRV_Date := aReturnBuffer;
    DRV_Ver := '';
    SetupGetStringFieldA(aContext, 2, @aReturnBuffer[0],
      aReturnBufferSize, nil);
    if aReturnBufferSize > 0 then
      DRV_Ver := aReturnBuffer;

  end;

  if SetupFindFirstLineA(hinf1, _Manufacturer, nil, ConHWID) then
  begin
    Manucount := SetupGetLineCountA(hinf1, _Manufacturer);

    for i := 0 to Manucount - 1 do
    begin
      FielCount := SetupGetFieldCount(ConHWID);
      // Mymemo.Add('Fieldcount='+IntToStr(FielCount));
      For L := 1 to FielCount do
      begin
        aReturnBufferSize := 127;
        SetupGetStringFieldA(ConHWID, L, @aReturnBuffer[0],
          aReturnBufferSize, nil);

        if L > 1 then
          CurMFG := FirstMFG + '.' + aReturnBuffer
        else
        begin
          CurMFG := aReturnBuffer;
          FirstMFG := aReturnBuffer;
        end;

        //определяем пустые секции
        //emptycount:=0;
        //ptycount := SetupGetLineCountA(hinf1, pansichar(curMFG));

        //if Emptycount=0 then
        EmptyMFG:=EmptyMFG+#13#10+CurMFG;

        // Hcount:=SetupGetLineCountA(hinf1,pchar(CurM));
        // Mymemo.Add('hcount='+IntToStr(Hcount));
        IsHWNext := SetupFindFirstLineA(hinf1, PAnsiChar(CurMFG), nil, ConHWID2);

        KB := 0;
        sectionWR := GetOS_MFG(CurMFG);
        if sectionWR = '' then
          sectionWR := OS_ByDP;
        for I_sec := 0 to 5 do
          if pos(AnsiUpperCase(NT_ALL[I_sec]), AnsiUpperCase(sectionWR)) > 0
          then
            KB := I_sec;

        // if (KB>6) or (KB<1) then ShowMessage(curm);
        // for X3:=0 to Hcount-1 do
        while IsHWNext do
        begin
          aReturnBufferSize := 127;
          needsize := 0;
          SetupGetStringFieldA(ConHWID2, 2, @aReturnBuffer2, aReturnBufferSize,
            @needsize);
          if needsize < 3 then
            SetupGetStringFieldA(ConHWID2, 3, @aReturnBuffer2,
              aReturnBufferSize, @needsize);

          if needsize < 3 then
            goto GETNEXTLINE;

          THWID := aReturnBuffer2;

          SetupGetStringFieldA(ConHWID2, 0, @aReturnBuffer,
            aReturnBufferSize, nil);
          TNAME := aReturnBuffer;

          ALStrings[KB].Add(
            // CurrentIndexList:=TDriverIndexList.Create;
            THWID + #0 + Tinfdir + #0 + Tinffilename + #0 + CurMFG + #0 + DRV_Date
            + #0 + DRV_Ver + #0 + TNAME + #0);
          // result1.Add(THWID,CurrentIndex);

        GETNEXTLINE:
          IsHWNext := SetupFindNextLine(ConHWID2, ConHWID2);
        end;

      end;
      SetupFindNextLine(ConHWID, ConHWID);
    end;

  end;

  SetupCloseInfFile(hinf1);

end;


function GetFirstHWID(input: string): string;
var
  dotpos: integer;
begin
  dotpos := posex(',', input, 0);

  if dotpos > 0 then
    result := copy(input, 0, dotpos - 1)
  else
    result := input;
end;

function GetPreparedHWID(input: string): string;
begin
  result := AnsiReplaceStr(input, ',', #39 + ',' + #39);
  result := AnsiReplaceStr(result, ' ', '');
  result := #39 + result + #39;
end;


function GetTver(input: string): Tver;
var
  poscomma: integer;
  posdot: integer;
  P1: PChar;
  minor: integer;
begin
  // form1.Memo1.Lines.Add(input);
  // for i:=0 to 7 do
  // Result.item[i]:=0;
  // item:array[0..7] of integer;
  // input:=trim(input);

  P1 := @input[1];
  // p1:=INPUT;
  poscomma := pos(',', P1);
  if poscomma > 0 then
    inc(P1, poscomma);

  // ShowMessage(p1);
  posdot := pos('.', P1);

  minor := 0;
  FillChar(result, SizeOf(result), 0);

  while posdot > 0 do
  begin
    result.item[minor] := strtoint(copy(P1, 0, posdot - 1));

    inc(minor);
    inc(P1, posdot);
    posdot := pos('.', P1);
  end;

  if minor > 0 then
    result.item[minor] := strtoint(copy(P1, 0, Length(P1)));
end;

function Is2Larger(Ver1, Ver2: Tver): Boolean;
var
  i: integer;
begin
  result := False;
  for i := 0 to 7 do
    if (Ver1.item[i] < Ver2.item[i]) then
    begin
      result := true;
      exit;
    end
    else if (Ver1.item[i] > Ver2.item[i]) then
    begin
      result := False;
      exit;
    end;

end;

function CompareTver(Ver1, Ver2: Tver): integer;
var
  i: integer;
begin
  for i := 0 to 7 do
    if (Ver1.item[i] < Ver2.item[i]) then
    begin
      result := 1;
      exit;
    end
    else if (Ver1.item[i] > Ver2.item[i]) then
    begin
      result := -1;
      exit;
    end;
  result := 0;
end;

{function GetBuildTime: TDateTime;
type
  UShort = Word;

  TImageDosHeader = packed record
    e_magic: UShort; // магическое число
    e_cblp: UShort; // количество байт на последней странице файла
    e_cp: UShort; // количество страниц вфайле
    e_crlc: UShort; // Relocations
    e_cparhdr: UShort; // размер заголовка в параграфах
    e_minalloc: UShort; // Minimum extra paragraphsneeded
    e_maxalloc: UShort; // Maximum extra paragraphsneeded
    e_ss: UShort; // начальное( относительное ) значение регистра SS
    e_sp: UShort; // начальное значениерегистра SP
    e_csum: UShort; // контрольная сумма
    e_ip: UShort; // начальное значение регистра IP
    e_cs: UShort; // начальное( относительное ) значение регистра CS
    e_lfarlc: UShort; // адрес в файле на таблицу переадресации
    e_ovno: UShort; // количество оверлеев
    e_res: array [0 .. 3] of UShort; // Зарезервировано
    e_oemid: UShort; // OEM identifier (for e_oeminfo)
    e_oeminfo: UShort; // OEM information; e_oemid specific
    e_res2: array [0 .. 9] of UShort; // Зарезервировано
    e_lfanew: LongInt; // адрес в файле нового .exeзаголовка
  end;

  TImageResourceDirectory = packed record
    Characteristics: DWORD;
    TimeDateStamp: DWORD;
    MajorVersion: Word;
    MinorVersion: Word;
    NumberOfNamedEntries: Word;
    NumberOfIdEntries: Word;
    // IMAGE_RESOURCE_DIRECTORY_ENTRY DirectoryEntries[];
  end;

  PImageResourceDirectory = ^TImageResourceDirectory;

var
  hExeFile: HFile;
  ImageDosHeader: TImageDosHeader;
  Signature: cardinal;
  ImageFileHeader: TImageFileHeader;
  ImageOptionalHeader: TImageOptionalHeader;
  ImageSectionHeader: TImageSectionHeader;
  ImageResourceDirectory: TImageResourceDirectory;
  Temp: cardinal;
  i: integer;
begin
  hExeFile := CreateFile(PChar(ParamStr(0)), GENERIC_READ, FILE_SHARE_READ, nil,
    OPEN_EXISTING, 0, 0);
  try
    ReadFile(hExeFile, ImageDosHeader, SizeOf(ImageDosHeader), Temp, nil);
    SetFilePointer(hExeFile, ImageDosHeader.e_lfanew, nil, FILE_BEGIN);
    ReadFile(hExeFile, Signature, SizeOf(Signature), Temp, nil);
    ReadFile(hExeFile, ImageFileHeader, SizeOf(ImageFileHeader), Temp, nil);
    ReadFile(hExeFile, ImageOptionalHeader, SizeOf(ImageOptionalHeader),
      Temp, nil);
    for i := 0 to ImageFileHeader.NumberOfSections - 1 do
    begin
      ReadFile(hExeFile, ImageSectionHeader, SizeOf(ImageSectionHeader),
        Temp, nil);
      if StrComp(@ImageSectionHeader.Name, '.rsrc') = 0 then
        break;
    end;
    SetFilePointer(hExeFile, ImageSectionHeader.PointerToRawData, nil,
      FILE_BEGIN);
    ReadFile(hExeFile, ImageResourceDirectory, SizeOf(ImageResourceDirectory),
      Temp, nil);
  finally
    FileClose(hExeFile);
  end;

  result := FileDateToDateTime(ImageResourceDirectory.TimeDateStamp);
end;    }

{

  procedure ScanDir(StartDir, Mask: String; recurse:boolean; List: TStringlist);
  var
  SearchRec: TSearchRec;
  begin
  if StartDir[Length(StartDir)] <> '\' then StartDir := StartDir + '\';
  if FindFirst(StartDir + '*.*', faAnyFile, SearchRec) = 0 then
  begin
  repeat
  //   Application.ProcessMessages;
  if not(DirectoryExists(StartDir + SearchRec.Name)) then
  //if (SearchRec.Attr and faDirectory) <> faDirectory  then
  begin
  if UpperCase(ExtractFileExt(SearchRec.Name)) = UpperCase(Mask) then
  List.Add(StartDir + SearchRec.Name+'='+IntToStr(SearchRec.Size))
  end
  else
  if not(DirectoryExists(StartDir + SearchRec.Name)) and recurse then
  //if (((SearchRec.Attr and faDirectory) <> 0) and recurse) then
  begin
  if (SearchRec.Name <> '..') and (SearchRec.Name <> '.') then
  ScanDir(StartDir + SearchRec.Name + '',Mask,recurse,List);
  end;
  until FindNext(SearchRec) <> 0;
  FindClose(SearchRec);
  end;
  end; }

procedure ScanDir(StartDir, mask: String; recurse: Boolean; List: tstringlist);
var
  SearchRec: TSearchRec;
begin
  if StartDir[Length(StartDir)] <> '\' then
    StartDir := StartDir + '\';
  if FindFirst(StartDir + '*.*', faAnyFile, SearchRec) = 0 then
  begin
    repeat
      // Application.ProcessMessages;

      // form1.Memo1.Lines.Add(SearchRec.Name+IntToStr(SearchRec.Attr));
      // if ((SearchRec.Attr - faDirectory) > 0) then
      if not(DirectoryExists(StartDir + SearchRec.Name)) then
      begin
        if UpperCase(ExtractFileExt(SearchRec.Name)) = UpperCase(mask) then
          List.Add(StartDir + SearchRec.Name + '=' + inttostr(SearchRec.SIZE))
      end
      else if (((SearchRec.Attr and faDirectory) <> 0) and recurse) then
      // if recurse then
      begin
        if (SearchRec.Name <> '..') and (SearchRec.Name <> '.') then
          ScanDir(StartDir + SearchRec.Name + '', mask, recurse, List);
      end;
    until FindNext(SearchRec) <> 0;
    FindClose(SearchRec);
  end;
end;


end.
