2012-05-30 3 views
7

В 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; 

К сожалению, DeviceIoControl() всегда возвращает False и если я проверяю GetLastError() потом затем возвращается с ошибкой 87, «неправильный параметр.»

Это не имеет большого смысла, потому что код работает отлично, если я просто изменяю InformationLevel от BatterySerialNumber до BatteryUniqueID, скажем. Кроме того, я использовал ручку для батареи (hbat) в других вызовах кода до GetSerialNumber, и все они работают нормально, и я могу позвонить другим после того, как этот сбой также не удался, так что это не проблема.

Любые идеи? Я действительно в недоумении.

+0

Существуют ли какие-либо другие программы в вашей системе, чтобы получить серийный номер? Это может быть до изворотливой реализации DSDT, откуда читается серийный номер, в разделе _BIF, я думаю. – James

+1

Почему вы передаете переменную dwOut как @dwOut? попробуйте использовать этот код вместо 'Результат: = DeviceIoControl (hbat, IOCTL_BATTERY_QUERY_INFORMATION, @bqi, SizeOf (bqi), Serial, SerialSize, dwOut, nil); « – RRUZ

+0

@RRUZ прав (это параметр« var »), а также дважды проверьте, что' BatterySerialNumber' = 8 –

ответ

12

Проблема кажется связана с dwOut переменной, которая передается как @dwOut, эта переменная представляет собой параметр вар lpBytesReturned в DeviceIoControl который определяется как

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

Так заменяя ваш код

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

Необходимо устранить проблему.

WinAPI

Также проверьте этот код переводится в Дельфы с данной записи MSDN 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. 

WMI

Наконец, как в сторону, обратите внимание, вы можете использовать WMI, чтобы получить ту же информацию, в этом случае, используя класс BatteryStaticData WMI

{$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. 
+0

Я принял предложения и перепутал с моим кодом, я все равно получаю ту же ошибку. Я также взял ваш API-код (поскольку у моего тестового окна нет этого класса WMI, доступного в этом пространстве имен) прямо в новый проект, скомпилировал его и запустил ... ту же ошибку. Я обыскал свой ящик для разных реализаций SetupApi.pas, попробовал их по очереди ... ту же ошибку на обоих тестовых ящиках (один на рабочем столе, другой - на Panasonic Toughbook). В какой версии Delphi вы построили это? Где вы получили SetupApi.pas? (Я использовал три версии из Project Jedi, Delphi 7 не поставлялся с SetupApi.pas.) – Restless

+0

Код WinApi был протестирован в delphi 7 с помощью модуля SetupApi из JVCL, включенного в папку 'jvcl \ run'. – RRUZ

+0

Я боялся, что ты это скажешь; это то, что я использовал, тоже. Я просто сделал это вдвойне, установив опцию SetupApi в этот файл явно, а также удостоверился, что другие включенные библиотеки не были изменены с исходных дат. Все еще нет любви, что странно, поскольку, как я уже упоминал, все вызовы 'IOCTL_BATTERY_QUERY_INFORMATION' отлично работают ... кроме _this_ one. – Restless

0

В итоге, код @RRUZ и я опубликовал работу под Windows 7, а также другие сторонние приложения. Они не работают для получения серийного номера в Windows XP. Я также тестировал под WinXP и 7 с базовыми установками ОС на одном и том же оборудовании с одинаковыми результатами (успех под Windows 7, а не под Windows XP).

Похоже, что под WinXP значение для пользователя IOCTL_BATTERY_QUERY_INFORMATIONInformationLevel не поддерживается, но это не задокументировано непосредственно в документах Windows SDK. Задокументировано, что недопустимые записи должны возвращать ошибку 1 (ERROR_INVALID_FUNCTION) для GetLastError(), но в этом случае вместо этого возвращается 87 (для недопустимого параметра). Я полагаю, что это потому, что это значение в перечислении недействительно, поэтому оно делает параметр недействительным, но я не совсем уверен.

Спасибо всем за помощь, особенно @RRUZ за то, что он прошел дальше и дальше!

(Как видно, из уникального идентификатора батареи можно извлечь серийный номер (используя BatteryUniqueID в качестве члена InformationLevel) и удалив имя производителя и имя устройства из уникального идентификатора. Это ужасный взлом, но это полужизущий обходной путь для Windows XP.)

Смежные вопросы