The problem is with the dwOut variable, which is passed as @dwOut, this variable represents the var lpBytesReturned DeviceIoControl , which is defined as
function DeviceIoControl(hDevice: THandle; dwIoControlCode: DWORD; lpInBuffer: Pointer; nInBufferSize: DWORD; lpOutBuffer: Pointer; nOutBufferSize: DWORD; var lpBytesReturned: DWORD; lpOverlapped: POverlapped): BOOL; stdcall;
So, replacing your code with
Result := DeviceIoControl(hbat, IOCTL_BATTERY_QUERY_INFORMATION, @bqi, SizeOf(bqi), Serial, SerialSize, dwOut, nil);
Need to fix the problem.
Winapi
Also check out this code translated into delphi from this msdn Enumerating Battery Devices entry, which will help you detect any additional problems with your code.
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
Finally, as a note, you can use WMI to get the same information, in this case using the BatteryStaticData WMI class
{$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.