2012-05-07 12 views
5

Me gustaría saber cómo puedo obtener todos los archivos del controlador para un dispositivo en particular, como lo hace el Administrador de dispositivos.Obtención de archivos de controlador para un dispositivo en particular

Tengo el siguiente código:

procedure TdlgMain.Test(const DeviceIndex: Integer); 
var 
    PnPHandle: HDEVINFO; 
    DevData: TSPDevInfoData; 
    DeviceInterfaceData: TSPDeviceInterfaceData; 
    FunctionClassDeviceData: PSPDeviceInterfaceDetailData; 
    Success: LongBool; 
    Devn: Integer; 
    BytesReturned: DWORD; 
    SerialGUID: TGUID; 
begin 
    ZeroMemory(@DevData, SizeOf(SP_DEVINFO_DATA)); 
    DevData.cbSize := SizeOf(SP_DEVINFO_DATA); 

    ZeroMemory(@DeviceInterfaceData, SizeOf(TSPDeviceInterfaceData)); 
    DeviceInterfaceData.cbSize := SizeOf(TSPDeviceInterfaceData); 

    if not SetupDiEnumDeviceInfo(hAllDevices, 
    DeviceIndex, DevData) then Exit; 

    SerialGUID := DevData.ClassGuid; 

    PnPHandle := SetupDiGetClassDevs(@SerialGUID, nil, 0, DIGCF_PRESENT or DIGCF_DEVICEINTERFACE); 
    if PnPHandle = Pointer(INVALID_HANDLE_VALUE) then 
    Exit; 

    Devn := 0; 
    repeat 
    DeviceInterfaceData.cbSize := SizeOf(TSPDeviceInterfaceData); 
    Success := SetupDiEnumDeviceInterfaces(PnPHandle, nil, SerialGUID, Devn, DeviceInterfaceData); 
    if Success then 
    begin 
     DevData.cbSize := SizeOf(DevData); 
     BytesReturned := 0; 
     // get size required for call 
     SetupDiGetDeviceInterfaceDetail(PnPHandle, @DeviceInterfaceData, nil, 0, BytesReturned, @DevData); 
     if (BytesReturned <> 0) and (GetLastError = ERROR_INSUFFICIENT_BUFFER) then 
     begin 
     // allocate buffer and initialize it for call 
     FunctionClassDeviceData := AllocMem(BytesReturned); 
     FunctionClassDeviceData.cbSize := SizeOf(TSPDeviceInterfaceDetailData); 
     //FunctionClassDeviceData.cbSize := BytesReturned; 
     if SetupDiGetDeviceInterfaceDetail(PnPHandle, @DeviceInterfaceData, 
      FunctionClassDeviceData, BytesReturned, BytesReturned, @DevData) then 
     begin 
      ShowMessage(FunctionClassDeviceData.DevicePath); 
     end else 
      RaiseLastOSError(); 
     FreeMem(FunctionClassDeviceData); 
     end; 
    end; 
    Inc(Devn); 
    until not Success; 
    SetupDiDestroyDeviceInfoList(PnPHandle); 

Pero el ShowMessage() o bien no es llamado en absoluto o devuelve \. ¿Cómo obtengo los archivos correctamente?

Eché un vistazo a devcon desde WinDDK, pero tampoco devuelve los archivos.

Gracias.

Respuesta

5

Me di cuenta. No hay una API que lo haga por ti, debes analizar los archivos INF para lograr el resultado. Aquí hay una solución rápida y sucia para todos ustedes, que están interesados.

procedure TdlgMain.Test(const DeviceIndex: Integer); 
var 
    Paths: TStringList; 
    I: Integer; 

    function GetWinDir: string; inline; 
    var 
    dir: array [0 .. MAX_PATH] of Char; 
    begin 
    GetWindowsDirectory(dir, MAX_PATH); 
    Result := IncludeTrailingBackslash(StrPas(dir)); 
    end; 

    function GetSpecialFolderPath(const folder: Integer): string; inline; 
    const 
    SHGFP_TYPE_CURRENT = 0; 
    var 
    path: array [0 .. MAX_PATH] of Char; 
    begin 
    if SUCCEEDED(SHGetFolderPath(0, folder, 0, SHGFP_TYPE_CURRENT, @path[0])) 
    then 
     Result := IncludeTrailingBackslash(path) 
    else 
     Result := ''; 
    end; 

    function LocateInfFile(const F: String): String; inline; 
    var 
    T: String; 
    begin 
    Result := ''; 

    if (Pos(SysUtils.PathDelim, F) > 0) then 
    begin 
     Result := F; 
     Exit; 
    end; 

    T := GetWinDir(); 
    if (FileExists(T + 'inf\' + F)) then 
     Result := T + 'inf\' + F 
    else if (FileExists(T + 'system32\' + F)) then 
     Result := T + 'system32\' + F; 
    end; 

    procedure ReadSectionNoKeys(const AFile, ASection: String; 
    const SL: TStringList); 
    var 
    TheFile: TStringList; 
    Line: String; 
    TrimEnd: Boolean; 
    Idx, Tmp: Integer; 
    begin 
    TrimEnd := False; 

    TheFile := TStringList.Create(); 
    try 
     TheFile.LoadFromFile(AFile); 
     Idx := TheFile.IndexOf('[' + ASection + ']'); 
     if (Idx <> -1) then 
     begin 
     Idx := Idx + 1; 
     while True do 
     begin 
      Line := Trim(TheFile[Idx]); 
      Inc(Idx); 
      if (Pos(';', Line) = 1) then 
      continue; 

      if (Pos('[', Line) > 0) then 
      Break; 

      Tmp := Pos(',', Line); 
      if (Tmp > 0) then 
      TrimEnd := True 
      else 
      begin 
      Tmp := PosEx(';', Line, 3); 
      if (Tmp > 0) then 
       TrimEnd := True; 
      end; 

      if (Line <> '') then 
      begin 
      if (TrimEnd) then 
      begin 
       Line := Trim(Copy(Line, 1, Tmp - 1)); 
       TrimEnd := False; 
      end; 

      SL.Add(Line); 
      end; 

      if (Idx = (TheFile.Count - 1)) then 
      Break; 
     end; 
     end; 
    finally 
     TheFile.Free(); 
    end; 
    end; 

    function IniReadStr(const Ini: TIniFile; const S, L, D: String): String; 
    var 
    T: Integer; 
    begin 
    Result := Ini.ReadString(S, L, D); 

    T := Pos(';', Result); 
    if (T > 0) then 
     Result := Trim(Copy(Result, 1, T - 1)); 
    end; 

    procedure ParseInfFile(const InfFile, SectionName: String); 
    var 
    I: TIniFile; 
    SL, FilesList: TStringList; 
    X, Y, Tmp: Integer; 
    Pth, S, S1: String; 
    begin 
    I := TIniFile.Create(InfFile); 
    try 
     if (SectionName <> '') and (I.SectionExists(SectionName)) then 
     begin 
     // Check if the section has a value called "CopyFiles". 
     if (I.ValueExists(SectionName, 'CopyFiles')) then 
     begin 
      // It has. Read it to a string and separate by commas. 
      SL := TStringList.Create(); 
      try 
      SL.CommaText := IniReadStr(I, SectionName, 'CopyFiles', ''); 

      // Now, every line of the string list is a section name. Check 
      // the destination directory of each. 
      if (I.SectionExists('DestinationDirs')) then 
       for X := 0 to SL.Count - 1 do 
       begin 
       S := IniReadStr(I, 'DestinationDirs', SL[X], ''); 
       if (S = '') then 
        S := IniReadStr(I, 'DestinationDirs', 'DefaultDestDir', ''); 

       if (S <> '') then 
       begin 
        // Split the path by comma, if any. 
        Tmp := Pos(',', S); 
        S1 := ''; 
        if (Tmp > 0) then 
        begin 
        S1 := Trim(Copy(S, Tmp + 1, Length(S))); 
        S := Trim(Copy(S, 1, Tmp - 1)); 
        end; 

        // Convert the numeric value of S to a proper directory. 
        Pth := ''; 
        if (S = '10') then 
        Pth := GetWinDir(); 
        if (S = '11') then 
        Pth := GetWinDir() + 'system32\'; 
        if (S = '12') then 
        Pth := GetWinDir() + 'system32\drivers\'; 
        if (S = '50') then 
        Pth := GetWinDir() + 'system\'; 
        if (S = '30') then 
        Pth := ExtractFileDrive(GetWinDir()); 
        if (StrToInt(S) >= 16384) then 
        Pth := GetSpecialFolderPath(StrToInt(S)); 

        if (S1 <> '') then 
        Pth := IncludeTrailingBackslash(Pth + S1); 

        // If we got the path, read the files. 
        if (Pth <> '') then 
        begin 
        FilesList := TStringList.Create(); 
        try 
         ReadSectionNoKeys(InfFile, SL[X], FilesList); 
         for Y := 0 to FilesList.Count - 1 do 
         if (Paths.IndexOf(Pth + FilesList[Y]) = -1) then 
          Paths.Add(Pth + FilesList[Y]); 
        finally 
         FilesList.Free(); 
        end; 
        end; 
       end; 
       end; 
      finally 
      SL.Free(); 
      end; 
     end; 

     // Check if there're "Include" and "Needs" values. 
     if ((I.ValueExists(SectionName, 'Include')) and 
      (I.ValueExists(SectionName, 'Needs'))) then 
     begin 
      // Split both by comma. 
      SL := TStringList.Create(); 
      FilesList := TStringList.Create(); 
      try 
      SL.CommaText := IniReadStr(I, SectionName, 'Include', ''); 
      FilesList.CommaText := IniReadStr(I, SectionName, 'Needs', ''); 
      if (SL.Text <> '') and (FilesList.Text <> '') then 
       for X := 0 to SL.Count - 1 do 
       for Y := 0 to FilesList.Count - 1 do 
        ParseInfFile(LocateInfFile(SL[X]), FilesList[Y]); 
      finally 
      FilesList.Free(); 
      SL.Free(); 
      end; 
     end; 
     end; 
    finally 
     I.Free(); 
    end; 
    end; 

begin 
    Paths := TStringList.Create(); 
    try 
    ParseInfFile(LocateInfFile(DeviceHelper.InfName), DeviceHelper.InfSection); 
    Paths.Sort(); 

    ListView_InsertGroup(lvAdvancedInfo.Handle, 'Driver Files', 2); 
    for I := 0 to Paths.Count - 1 do 
     ListView_AddItemsInGroup(lvAdvancedInfo, '', Paths[I], 2); 
    finally 
    Paths.Free(); 
    end; 
end; 
Cuestiones relacionadas