如何正确检索电池序列号?

Res*_*ess 7 delphi winapi deviceiocontrol

在Delphi 7中,我正在开发一个库,该库实现了一个对象,该对象封装了有关连接到系统的电池的信息.除了检索电池的序列号外,它运行良好.

我用于此次调用的代码如下:

function TBattery.GetSerialNumber(hbat: THandle): boolean;
var
  bqi:          TBatteryQueryInformation;
  Serial:       PWideChar;
  SerialSize,
  dwOut:        DWORD;
begin
  Result := False;

  if hbat <> INVALID_HANDLE_VALUE then
  begin
    ZeroMemory(@bqi, SizeOf(bqi));
    dwOut := 0;

    bqi.BatteryTag := FBatteryTag;
    bqi.InformationLevel := BatterySerialNumber;

    SerialSize := 2048;
    GetMem(Serial, SerialSize);
    try
      ZeroMemory(Serial, SerialSize);

      Result := DeviceIoControl(hbat, IOCTL_BATTERY_QUERY_INFORMATION, @bqi,
                                SizeOf(bqi), Serial, SerialSize, @dwOut, nil);

      if Result then
        FSerialNumber := Serial;
    finally
      FreeMem(Serial, SerialSize);
    end;
  end;
end;
Run Code Online (Sandbox Code Playgroud)

不幸的是,DeviceIoControl()总是返回False,如果我GetLastError()之后检查然后它返回错误87,"参数不正确."

这没有多大意义,因为如果我简单地将InformationLevelfrom 更改为BatterySerialNumberto BatteryUniqueID,那么代码就能很好地工作.另外,我之前hbat在代码中的其他调用中使用了handle()的句柄GetSerialNumber,它们都运行正常,并且在此之后我也可以调用其他函数,所以这不是问题.

有任何想法吗?我真的很茫然.

RRU*_*RUZ 12

这个问题似乎与dwOut作为@dwOut传递的变量有关,这个变量表示定义为的var lpBytesReturned参数DeviceIoControl

function DeviceIoControl(hDevice: THandle; dwIoControlCode: DWORD; lpInBuffer: Pointer;
  nInBufferSize: DWORD; lpOutBuffer: Pointer; nOutBufferSize: DWORD;
  var lpBytesReturned: DWORD; lpOverlapped: POverlapped): BOOL; stdcall;
Run Code Online (Sandbox Code Playgroud)

所以替换你的代码

  Result := DeviceIoControl(hbat, IOCTL_BATTERY_QUERY_INFORMATION, @bqi,
                            SizeOf(bqi), Serial, SerialSize, dwOut, nil);
Run Code Online (Sandbox Code Playgroud)

必须解决问题.

WinAPI的

另请查看从此msdn条目转换为delphi的此代码,Enumerating Battery Devices它可以帮助您检测代码的任何其他问题.

uses
  SetupApi,
  Windows,
  SysUtils;

type

  BATTERY_QUERY_INFORMATION_LEVEL = (
    BatteryInformation,
    BatteryGranularityInformation,
    BatteryTemperature,
    BatteryEstimatedTime,
    BatteryDeviceName,
    BatteryManufactureDate,
    BatteryManufactureName,
    BatteryUniqueID,
    BatterySerialNumber);
  TBatteryQueryInformationLevel = BATTERY_QUERY_INFORMATION_LEVEL;

  _BATTERY_QUERY_INFORMATION = record
    BatteryTag: ULONG;
    InformationLevel: BATTERY_QUERY_INFORMATION_LEVEL;
    AtRate: Longint;
  end;
  BATTERY_QUERY_INFORMATION = _BATTERY_QUERY_INFORMATION;
  PBATTERY_QUERY_INFORMATION = ^BATTERY_QUERY_INFORMATION;
  TBatteryQueryInformation = BATTERY_QUERY_INFORMATION;


const
  GUID_DEVCLASS_BATTERY:TGUID='{72631E54-78A4-11D0-BCF7-00AA00B7B32A}';
  //DEFINE_GUID( GUID_DEVCLASS_BATTERY, 0x72631E54, 0x78A4, 0x11D0, 0xBC, 0xF7, 0x00, 0xAA, 0x00, 0xB7, 0xB3, 0x2A );
  METHOD_BUFFERED     = 0;
  FILE_DEVICE_BATTERY = $00000029;
  FILE_READ_ACCESS    = $0001;    // for files and pipes

  IOCTL_BATTERY_QUERY_TAG =
    (FILE_DEVICE_BATTERY shl 16) or (FILE_READ_ACCESS shl 14) or ($10 shl 2) or (METHOD_BUFFERED);
  IOCTL_BATTERY_QUERY_INFORMATION =
    (FILE_DEVICE_BATTERY shl 16) or (FILE_READ_ACCESS shl 14) or ($11 shl 2) or (METHOD_BUFFERED);

function GetBatteryInfo(InformationLevel : BATTERY_QUERY_INFORMATION_LEVEL) : string;
var
   cbRequired : DWORD;
   hdev     : HDEVINFO;
   idev     : Integer;
   did      : TSPDeviceInterfaceData;
   pdidd    : PSPDeviceInterfaceDetailData;
   hBattery : THandle;
   bqi      : TBatteryQueryInformation;
   dwWait, dwOut : DWORD;
   lpOutBuffer: PWideChar;
begin
  // enumerate the batteries
  hdev :=  SetupDiGetClassDevs(@GUID_DEVCLASS_BATTERY, nil, 0,  DIGCF_PRESENT OR DIGCF_DEVICEINTERFACE);
  if ( INVALID_HANDLE_VALUE <>  THandle(hdev) ) then
  begin
      idev:=0;//first battery
      ZeroMemory(@did, SizeOf(did));
      did.cbSize := SizeOf(did);
      if (SetupDiEnumDeviceInterfaces(hdev, nil, GUID_DEVCLASS_BATTERY, idev, did)) then
      begin
        try
          cbRequired := 0;
          SetupDiGetDeviceInterfaceDetail(hdev, @did, nil, 0, cbRequired, nil);
         if (ERROR_INSUFFICIENT_BUFFER= GetLastError()) then
         begin
            pdidd:=AllocMem(cbRequired);
            try
              pdidd.cbSize := SizeOf(TSPDeviceInterfaceDetailData);
              if (SetupDiGetDeviceInterfaceDetail(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 (INVALID_HANDLE_VALUE <> hBattery) then
                 begin
                  try
                    ZeroMemory(@bqi, SizeOf(bqi));
                     // With the tag, you can query the battery info.
                    dwWait := 0;
                      if (DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_TAG,  @dwWait, sizeof(dwWait), @bqi.BatteryTag, sizeof(bqi.BatteryTag), dwOut, nil)) then
                      begin
                        lpOutBuffer:=AllocMem(MAX_PATH);
                        try
                          ZeroMemory(lpOutBuffer,MAX_PATH);
                          bqi.InformationLevel:=InformationLevel;
                          if DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_INFORMATION, @bqi, SizeOf(BATTERY_QUERY_INFORMATION), lpOutBuffer, 255, dwOut,nil) then
                            Result:= WideCharToString(lpOutBuffer);
                        finally
                          FreeMem(lpOutBuffer);
                        end;
                      end;
                  finally
                    CloseHandle(hBattery)
                  end;
                 end;
              end;
            finally
              FreeMem(pdidd);
            end;
         end;
        finally
          SetupDiDestroyDeviceInfoList(hdev);
        end;
      end;
  end;
end;

begin
  try
    if not LoadsetupAPI then exit;
     Writeln(GetBatteryInfo(BatterySerialNumber));
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  readln;
end.
Run Code Online (Sandbox Code Playgroud)

WMI

最后请注意,您可以使用WMI检索相同的信息,在本例中使用BatteryStaticDataWMI类

    {$APPTYPE CONSOLE}

    uses
      SysUtils,
      ActiveX,
      ComObj,
      Variants;

    // Battery Static Data

    procedure  GetBatteryStaticDataInfo;
    const
      WbemUser            ='';
      WbemPassword        ='';
      WbemComputer        ='localhost';
      wbemFlagForwardOnly = $00000020;
    var
      FSWbemLocator : OLEVariant;
      FWMIService   : OLEVariant;
      FWbemObjectSet: OLEVariant;
      FWbemObject   : OLEVariant;
      oEnum         : IEnumvariant;
      iValue        : LongWord;
    begin;
      FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
      FWMIService   := FSWbemLocator.ConnectServer(WbemComputer, 'root\WMI', WbemUser, WbemPassword);
      FWbemObjectSet:= FWMIService.ExecQuery('SELECT SerialNumber FROM BatteryStaticData','WQL',wbemFlagForwardOnly);
      oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
      while oEnum.Next(1, FWbemObject, iValue) = 0 do
      begin
        Writeln(Format('SerialNumber    %s',[String(FWbemObject.SerialNumber)]));// String

        Writeln('');
        FWbemObject:=Unassigned;
      end;
    end;


    begin
     try
        CoInitialize(nil);
        try
          GetBatteryStaticDataInfo;
        finally
          CoUninitialize;
        end;
     except
        on E:EOleException do
            Writeln(Format('EOleException %s %x', [E.Message,E.ErrorCode])); 
        on E:Exception do
            Writeln(E.Classname, ':', E.Message);
     end;
     Writeln('Press Enter to exit');
     Readln;      
    end.
Run Code Online (Sandbox Code Playgroud)