Programmierung > Delphi > Batterie > Handle

Das Handle zum Gerät "Batterie"

  • Beschreibung
  • SetupDiGetClassDevs
  • SetupDiEnumDeviceInterfaces
  • SetupDiGetDeviceInterfaceDetail
  • SetupDiDestroyDeviceInfoList
  • CreateFile
  • Die Funktion in Delphi 7
  • Download
  • Links
  • Beschreibung

    Um die Control Codes verwenden zu können wird ein Handle auf das Gerät, das heißt die Batterie, benötigt. Das Windows Dev Center enthält dafür ein Beispiel, welches die SetupAPI verwendet. Wenn ich das richtig verstehe, wird mit Hilfe der ClassGuid ein Handle auf die Geräteklasse und dann auf die Geräteschnittstellen geholt. Dann werden die Daten der Schnittstelle zu einem bestimmten Gerät ermittelt, um damit das Handle zum Gerät zu erhalten.

    Hier werden die Funktionen aus dem Windows Dev Center mit den Datenstrukturen kurz dargestellt. Ausserdem gibt es eine Funktion in Delphi 7, welche das Handle und den DevicePath der Batterie ermitteln.

    SetupDiGetClassDevs

    Die Funktion liefert ein Handle auf die Geräteinformationen einer bestimmten Geräteklasse des Computers zurück. Das MSDN Windows Dev Center liefert eine Definition in c++ mit Erläuterungen:

    HDEVINFO SetupDiGetClassDevs(
      _In_opt_ const GUID   *ClassGuid,
      _In_opt_       PCTSTR Enumerator,
      _In_opt_       HWND   hwndParent,
      _In_           DWORD  Flags
    );
    

    ClassGuid: Dies ist eine eindeutige Identfikationsnummer innerhalb des Computersystem. Unter Windows gibt es die Definition:

    Battery Devices
    Class = Battery
    ClassGuid = {72631e54-78a4-11d0-bcf7-00aa00b7b32a}
     
    This class includes battery devices and UPS devices.
    

    Enumerator: Dies ist ein Zeiger auf eine Aufzählung. Die Angabe ist optional und es kann die Konstante NULL verwendet werden. Dies ist im Beispiel der Fall.

    hwndParent: Dies ist das Handle auf ein übergeordnetes Fenster. Die Angabe ist optional und es kann die Konstannte NULL verwendet werden. Dies ist im Beispiel der Fall.

    Flags: Dies sind Filter für die Funktion. Möglich sind DIGCF_ALLCLASSES, DIGCF_DEVICEINTERFACE, DIGCF_DEFAULT, DIGCF_PRESENT und DIGCF_PROFILE. Im Beispiel werden DIGCF_PRESENT und DIGCF_DEVICEINTERFACE verwendet

    Die Deklaration der Funktion in Delphi:

    const
      csSetupApiModule = 'SETUPAPI.DLL';
     
    function SetupDiGetClassDevsA(
                       const ClassGuid  : PGUID;
                       Enumerator       : PAnsiChar;
                       hwndParent       : HWND;
                       Flags            : DWORD
                       ): HDEVINFO; stdcall; external csSetupApiModule;
    

    SetupDiEnumDeviceInterfaces

    Mit dieser Funktion werden die Schnittstellendaten geholt. Die c++-Definition der Funktion im MSDN Windows Dev Center:

    BOOL SetupDiEnumDeviceInterfaces(
      _In_           HDEVINFO                  DeviceInfoSet,
      _In_opt_       PSP_DEVINFO_DATA          DeviceInfoData,
      _In_     const GUID                      *InterfaceClassGuid,
      _In_           DWORD                     MemberIndex,
      _Out_          PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData
    );
    

    DeviceInfoSet: Ein Zeiger auf die Geräteinformationen. Typischerweise das Handle, welches man von SetupDiGetClassDevs erhält.

    DeviceInfoData: Ein Zeiger auf eine Datenstruktur, welcher optional ist und NULL sein kann. Dies ist in dem Beispiel der Fall.

    InterfaceClassGuid: Ein Zeiger auf die Schnittstellen-GUID. Das ist die selbe wie oben.

    MemberIndex: Der Index auf ein Mitglied der Geräteklasse. 0 ist die erste Batterie. Normalerweise sollte nur eine vorhanden sein.

    DeviceInterfaceData: Dies ist ein Zeiger auf einen zugewiesenen Puffer, welcher bei erfolgreichem Abschluss der Funktion eine Datenstruktur SP_DEVICE_INTERFACE_DATA mit Zugriffsinformationen zur Schnittstelle enthält.

    Bei erfolgreichem Abschluss der Funktion wird als Ergebnis TRUE geliefert, andernfalls FALSE. Den Fehlercode erhält man mit der Funktion GetLastError.

    DeviceInfoData

    typedef struct _SP_DEVINFO_DATA {
      DWORD     cbSize;
      GUID      ClassGuid;
      DWORD     DevInst;
      ULONG_PTR Reserved;
    } SP_DEVINFO_DATA, *PSP_DEVINFO_DATA;
    

    cbSize: Größe der Struktur in Bytes.

    ClassGuid: Die Schnittstellen-GUID.

    DevInst: Die Infos zum Unterknoten.

    DeviceInterfaceData

    typedef struct _SP_DEVICE_INTERFACE_DATA {
      DWORD     cbSize;
      GUID      InterfaceClassGuid;
      DWORD     Flags;
      ULONG_PTR Reserved;
    } SP_DEVICE_INTERFACE_DATA, *PSP_DEVICE_INTERFACE_DATA;
    

    Diese Struktur wird unten an die Funktion SetupDiGetDeviceInterfaceDetail weiter gegeben.

    Die Deklarationen in Delphi:

    type
      _SP_DEVINFO_DATA =
      record
        cbSize    : DWORD;
        ClassGuid : TGUID;
        DevInst   : DWORD;
        Reserved  : ULONG;
      end;
      TSP_DEVINFO_DATA = _SP_DEVINFO_DATA;
      PSP_DEVINFO_DATA = ^_SP_DEVINFO_DATA;
     
    type
      _SP_DEVICE_INTERFACE_DATA =
      record
        cbSize             : DWORD;
        InterfaceClassGuid : TGUID;
        Flags              : DWORD;
        Reserved           : ULONG;
      end;
      TSP_DEVICE_INTERFACE_DATA = _SP_DEVICE_INTERFACE_DATA;
      PSP_DEVICE_INTERFACE_DATA = ^_SP_DEVICE_INTERFACE_DATA;
     
    function SetupDiEnumDeviceInterfaces(
                       DeviceInfoSet            : HDEVINFO;
                       DeviceInfoData           : PSP_DEVINFO_DATA;
                       const InterfaceClassGuid : PGUID;
                       MemberIndex              : DWORD;
                       DeviceInterfaceData      : PSP_DEVICE_INTERFACE_DATA
                       ): Boolean; stdcall; external csSetupApiModule;
    

    SetupDiGetDeviceInterfaceDetail

    Diese Funktion ermittelt Einzelheiten zur Schnittstelle wie den Pfad, welcher für die Funktion CreateFile unten benötigt wird. Die c++-Definition im MSDN Windows Dev Center:

    BOOL SetupDiGetDeviceInterfaceDetail(
      _In_      HDEVINFO                         DeviceInfoSet,
      _In_      PSP_DEVICE_INTERFACE_DATA        DeviceInterfaceData,
      _Out_opt_ PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData,
      _In_      DWORD                            DeviceInterfaceDetailDataSize,
      _Out_opt_ PDWORD                           RequiredSize,
      _Out_opt_ PSP_DEVINFO_DATA                 DeviceInfoData
    );
    

    DeviceInfoSet: Wieder der Zeiger auf die Geräteinformationen. Typischerweise das Handle, welches man von SetupDiGetClassDevs erhält.

    DeviceInterfaceData: Ein Zeiger auf die Datenstruktur SP_DEVICE_INTERFACE_DATA, welche typischwerweise von der Funktion SetupDiEnumDeviceInterfaces zurück gegeben wird.

    DeviceInterfaceDetailData: Ein Zeiger zu der Struktur SP_DEVICE_INTERFACE_DETAIL_DATA, welche die Informationen zur angegebenen Schnittstelle enthält. Der Parameter ist optional und kann NULL sein.

    DeviceInterfaceDetailDataSize: Die Größe der Struktur DeviceInterfaceDetailData, wobei vom DevicePath nur das erste Zeichen mitgezählt wird. Ist DeviceInterfaceDetailData mit NULL angegeben, ist die Größe 0.

    RequiredSize: Dies ist ein Zeiger auf die Größe des Puffers, welcher für die Aufnahme der DeviceInterfaceDetailData notwendig ist. Der Zeiger ist optional und kann NULL sein.

    DeviceInfoData: Ein Zeiger auf einen Puffer, welcher NULL sein kann. Dies ist im Beispiel der Fall.

    Bei erfolgreichem Abschluss der Funktion wird als Ergebnis TRUE geliefert, andernfalls FALSE. Den Fehlercode erhält man mit der Funktion GetLastError.

    Die Funktion wird zweimal aufgerufen. Beim ersten Aufruf ist DeviceInterfaceDetailData NULL um die Größe des Puffers zu ermitteln. Anschliessend wird der Puffer bereitgestellt und der Zeiger darauf gesetzt.

    DeviceInterfaceDetailData

    typedef struct _SP_DEVICE_INTERFACE_DETAIL_DATA {
      DWORD cbSize;
      TCHAR DevicePath[ANYSIZE_ARRAY];
    } SP_DEVICE_INTERFACE_DETAIL_DATA, *PSP_DEVICE_INTERFACE_DETAIL_DATA;
    

    cbSize: Größe der Struktur in Bytes.

    DevicePath: Ein nullterminierter String, welcher den Schnittstellenpfad enthält, wecher für die Funktion CreateFile benötigt wird.

    Die Deklarationen in Delphi:

    type
      _SP_DEVICE_INTERFACE_DETAIL_DATA_A =
      packed record
        cbSize     : DWORD;
        DevicePath : array [0..0] of AnsiChar;
      end;
      TSP_DEVICE_INTERFACE_DETAIL_DATA_A = _SP_DEVICE_INTERFACE_DETAIL_DATA_A;
      PSP_DEVICE_INTERFACE_DETAIL_DATA_A = ^_SP_DEVICE_INTERFACE_DETAIL_DATA_A;
     
    function SetupDiGetDeviceInterfaceDetailA(
                       DeviceInfoSet                 : HDEVINFO;
                       DeviceInterfaceData           : PSP_DEVICE_INTERFACE_DATA;
                       DeviceInterfaceDetailData     : PSP_DEVICE_INTERFACE_DETAIL_DATA_A;
                       DeviceInterfaceDetailDataSize : DWORD;
                       RequiredSize                  : PDWORD;
                       DeviceInfoData                : PSP_DEVINFO_DATA
                       ): Boolean; stdcall; external csSetupApiModule;
    

    _SP_DEVICE_INTERFACE_DETAIL_DATA_A muss als packed record deklariert werden. Dies hat Einfluss auf seine Größe. Sie verringert sich von 8 auf 5 Byte. Ohne packed erhält man beim Aufruf der Funktion den Fehler ERROR_INVALID_USER_BUFFER, 1784 (0x6F8), The supplied user buffer is not valid for the requested operation.

    SetupDiDestroyDeviceInfoList

    Diese Funktion gibt die Informationen und durch die aufgerufenen Funktionen reservierten Speicher. Die c++-Definition im MSDN Windows Dev Center lautet:

    BOOL SetupDiDestroyDeviceInfoList(
      _In_ HDEVINFO DeviceInfoSet
    );
    

    DeviceInfoSet: Dies ist das Handle, welches mit der Funktion SetupDiGetClassDevs ermittelt worden war.

    Die Deklarationen in Delphi:

    function SetupDiDestroyDeviceInfoList(
                       DeviceInfoSet: HDEVINFO
                       ): Boolean; stdcall; external csSetupApiModule;
    

    CreateFile

    Die c++-Definition im MSDN Windows Dev Center:

    HANDLE WINAPI CreateFile(
      _In_     LPCTSTR               lpFileName,
      _In_     DWORD                 dwDesiredAccess,
      _In_     DWORD                 dwShareMode,
      _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
      _In_     DWORD                 dwCreationDisposition,
      _In_     DWORD                 dwFlagsAndAttributes,
      _In_opt_ HANDLE                hTemplateFile
    );
    

    lpFileName: Der Name des Gerätes, welches geöffnet werden soll. Das ist hier der DevicePath aus den DeviceInterfaceDetailData aus der Funktion SetupDiGetDeviceInterfaceDetail

    dwDesiredAccess: Die Flags für den Zugriff. Im Beispiel wird GENERIC_READ | GENERIC_WRITE verwendet.

    dwShareMode: Die Flags für die Freigabe. Im Beispiel wird FILE_SHARE_READ | FILE_SHARE_WRITE verwendet.

    lpSecurityAttributes: Ein Zeiger auf die Sicherheitsattribute. Der Parameter ist optional und kann NULL sein. Dies ist im Beispiel der Fall.

    dwCreationDisposition: Eine Reaktion, wenn das Gerät existiert oder nicht. Im Beispiel ist der Wert OPEN_EXISTING, das bedeutet, öffnen wenn es existiert.

    dwFlagsAndAttributes: Attribute und Flags für das Gerät. Im Beispiel ist dies FILE_ATTRIBUTE_NORMAL.

    hTemplateFile: Ein Handle zu einem Template. Der Parameter ist optional und kann NULL sein. Dies ist im Beispiel der Fall.

    Die Funktion ist in Delphi in der Unit Windows deklariert.

    Die Funktion in Delphi 7

    function GetBatteryHandle(out DevicePath: String): THandle;
    var
      hdev       : HDEVINFO;
      idev       : DWORD;
      did        : TSP_DEVICE_INTERFACE_DATA;
      pdidd      : PSP_DEVICE_INTERFACE_DETAIL_DATA_A;
      cbRequired : DWORD;
      hBattery   : THandle;
    begin
      Result := 0;
     
      hdev := SetupDiGetClassDevsA(@GUID_DEVCLASS_BATTERY,
                                   nil,
                                   0,
                                   DIGCF_PRESENT or DIGCF_DEVICEINTERFACE);
     
      if (hdev <> INVALID_HANDLE_VALUE)
      then begin
        idev := 0; // Erste Batterie
        ZeroMemory(@did, SizeOf(did));
        did.cbSize := SizeOf(TSP_DEVICE_INTERFACE_DATA);
     
        if (SetupDiEnumDeviceInterfaces(hdev,
                                        nil,
                                        @GUID_DEVCLASS_BATTERY,
                                        idev,
                                        @did))
        then try
          cbRequired := 0;
     
          // Result ist FALSE weil die Größe für die Antwort zu klein ist.
          SetupDiGetDeviceInterfaceDetailA(hdev,
                                           @did,
                                           nil,
                                           0,
                                           @cbRequired,
                                           nil);
     
          if (ERROR_INSUFFICIENT_BUFFER = GetLastError)
          then begin
            pdidd := AllocMem(cbRequired + SizeOf(TSP_DEVICE_INTERFACE_DETAIL_DATA_A));
            pdidd.cbSize := SizeOf(TSP_DEVICE_INTERFACE_DETAIL_DATA_A);
    
            if (pdidd <> nil)
            then try
              if (SetupDiGetDeviceInterfaceDetailA(hdev,
                                                   @did,
                                                   pdidd,
                                                   cbRequired,
                                                   @cbRequired,
                                                   nil))
              then begin
                hBattery := CreateFile(pdidd.DevicePath,
                                       GENERIC_READ or GENERIC_WRITE,
                                       FILE_SHARE_READ or FILE_SHARE_WRITE,
                                       nil,
                                       OPEN_EXISTING,
                                       FILE_ATTRIBUTE_NORMAL,
                                       0);
                if (hBattery <> INVALID_HANDLE_VALUE)
                then begin
                  Result     := hBattery;
                  DevicePath := PAnsiChar(@pdidd^.DevicePath);
                end;
              end;
            finally
              FreeMem(pdidd);
            end;
          end;
        finally
          SetupDiDestroyDeviceInfoList(hdev);
        end;
      end;
    end;
    

    Download

    Demo mit Delphi 7 compiliert
    battery_handle_exe.7z (152 kB) - MD5 (1kB)

    Source der Demo
    battery_handle_src.7z (3 kB) - MD5 (1kB)