在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,那么代码的效果非常好.另外,我在GetSerialNumber之前的代码中的其他调用中使用了电池的句柄(hbat)并且它们都工作正常,并且在此之后我也可以调用其他调用,所以这不是问题.
有任何想法吗?我真的很茫然.
解决方法
这个问题似乎与dwOut变量有关,它以@dwOut的形式传递,这个变量代表
DeviceIoControl
的var lpBytesReturned参数,定义为
function DeviceIoControl(hDevice: THandle; dwIoControlCode: DWORD; lpInBuffer: Pointer; nInBufferSize: DWORD; lpOutBuffer: Pointer; nOutBufferSize: DWORD; var lpBytesReturned: DWORD; lpOverlapped: POverlapped): BOOL; stdcall;
所以替换你的代码
Result := DeviceIoControl(hbat,dwOut,nil);
必须解决问题.
WinAPI的
另请查看此msdn条目Enumerating Battery Devices
中转换为delphi的代码,它可以帮助您检测代码的任何其他问题.
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,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,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,GUID_DEVCLASS_BATTERY,idev,did)) then begin try cbrequired := 0; SetupDiGetDeviceInterfaceDetail(hdev,@did,cbrequired,nil); if (ERROR_INSUFFICIENT_BUFFER= GetLastError()) then begin pdidd:=AllocMem(cbrequired); try pdidd.cbSize := SizeOf(TSPDeviceInterfaceDetailData); if (SetupDiGetDeviceInterfaceDetail(hdev,pdidd,nil)) then begin hBattery :=CreateFile(pdidd.DevicePath,GENERIC_READ OR GENERIC_WRITE,FILE_SHARE_READ OR FILE_SHARE_WRITE,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),nil)) then begin lpOutBuffer:=AllocMem(MAX_PATH); try ZeroMemory(lpOutBuffer,MAX_PATH); bqi.InformationLevel:=InformationLevel; if DeviceIoControl(hBattery,SizeOf(BATTERY_QUERY_INFORMATION),lpOutBuffer,255,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.