0

I'm taking my first steps in Delphi, I've created a small program that uses the GetSystemPowerStatus function to get battery status information.

To load the function, I used the Winapi.Windows unit.

Everything worked fine, but the EXE file after compilation took up 150 kb.

From what I understand from this post, using the Winapi.Windows unit is the cause of the increase, but I have not found how to access the GetSystemPowerStatus function without using the unit.

Is this possible, and how can it be done?


Well, after Remy Lebeau's answer I did it like this:

program GetBatteryData;

{$APPTYPE CONSOLE}

uses
  System.SysUtils;

type
  TSystemPowerStatus = record
  ACLineStatus: Byte;
  BatteryFlag: Byte;
  BatteryLifePercent: Byte;
  SystemStatusFlag: Byte;
  BatteryLifeTime: UInt32;
  BatteryFullLifeTime: UInt32;
end;

var
  PowerState: TSystemPowerStatus;
  Param: String;

function GetSystemPowerStatus(var lpSystemPowerStatus: TSystemPowerStatus): LongBool; stdcall; external 'Kernel32';

procedure ShowHelp;
begin
  Writeln('Usage:');
  Writeln(#32#32'GetBatteryData <query>'#10#13);
  Writeln('queries:'#10#13);
  Writeln(#32#32'ACState'#9'Get State of AC power');
  Writeln(#32#32'LeftPercent'#9'Get the left percent on battery');
  Writeln(#32#32'StateFlag'#9'Get the system flag of battery');
end;

function BatteryStateACPower(ACState: Byte): String;
begin
  case ACState of
    0: Result := 'Not Charged';
    1: Result := 'Charged';
    255: Result := 'Unknown battery state';
  end;
end;

function BatteryStatePercent(LeftPercent: Byte): String;
begin
  case LeftPercent of
    0..100: Result := IntToStr(LeftPercent);
    255: Result := 'Unknown battery state';
  end;
end;

function BatteryStateFlags(Flag: Byte): String;
begin
  case Flag of
    1: Result := 'High';
    2: Result := 'Low';
    4: Result := 'Critical';
    8: Result := 'Charged';
    128: Result := 'No battery in system';
    255: Result := 'Unknown battery state';
  end;
end;

begin
  try
    Param := '';
    if ParamCount = 0 then
    begin
      Writeln('No Parameter'#10#13);
      ShowHelp;
    end else
    begin
      Param := Uppercase(ParamStr(1));
      GetSystemPowerStatus(PowerState);
      if Param = 'ACSTATE' then
          Write(BatteryStateACPower(PowerState.ACLineStatus))
          else
            if Param = 'LEFTPERCENT' then Write(BatteryStatePercent(PowerState.BatteryLifePercent))
              else
                if Param = 'STATEFLAG' then Write(BatteryStateFlags(PowerState.BatteryFlag))
                  else
                    begin
                      Writeln('Invalid Parameter'#10#13);
                      ShowHelp;
                    end;
    end;

  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

But still, after compiling the exe file takes up over 230 kb. Is this the final size, or might it be possible to reduce its size?

3
  • You have linked to a Q&A that explains how to declare an import, without using a unit that does it for you. The answer to the literal question is: Yes, this is possible. If that is not what you meant to ask, then please edit your question after reading How to Ask. Commented Apr 7, 2022 at 9:49
  • Right, I mean to ask how to do it... I did not notice that I omitted the word 'how'... fixed. Commented Apr 7, 2022 at 9:52
  • You have the source code to the Windows unit that shows you precisely how to do this. There is also documentation of the external keyword. You can do this yourself if you just have some self belief. Commented Apr 7, 2022 at 10:29

1 Answer 1

2

If you don't want to use the Winapi.Windows unit, you will just have to declare the function in your own code. See Importing Functions from Libraries in Delphi's documentation.

For example:

type
  SYSTEM_POWER_STATUS = record
    ACLineStatus: Byte;
    BatteryFlag: Byte;
    BatteryLifePercent: Byte;
    SystemStatusFlag: Byte;
    BatteryLifeTime: UInt32;
    BatteryFullLifeTime: UInt32;
  end; 

function GetSystemPowerStatus(var lpSystemPowerStatus: SYSTEM_POWER_STATUS): LongBool; stdcall; external 'Kernel32';

This is similar (but different) to how the Winapi.Windows unit declares the function. The main differences being:

  • it splits the declaration and linkage of the function between the interface and implementation sections of the unit
  • it uses an alias for the record type
  • it uses aliases for Win32 data types
unit Winapi.Windows;

...

interface

...

type
  PSystemPowerStatus = ^TSystemPowerStatus;
  _SYSTEM_POWER_STATUS = record
    ACLineStatus : Byte;
    BatteryFlag : Byte;
    BatteryLifePercent : Byte;
    Reserved1 : Byte;
    BatteryLifeTime : DWORD;
    BatteryFullLifeTime : DWORD;
  end;
  {$EXTERNALSYM _SYSTEM_POWER_STATUS}
  TSystemPowerStatus = _SYSTEM_POWER_STATUS;
  SYSTEM_POWER_STATUS = _SYSTEM_POWER_STATUS;
  {$EXTERNALSYM SYSTEM_POWER_STATUS}

function GetSystemPowerStatus(var lpSystemPowerStatus: TSystemPowerStatus): BOOL; stdcall;
{$EXTERNALSYM GetSystemPowerStatus}

...

const
  ...
  kernel32  = 'kernel32.dll';
  ...

...

implementation

...
function GetSystemPowerStatus; external kernel32 name 'GetSystemPowerStatus';
...

end.
Sign up to request clarification or add additional context in comments.

3 Comments

Thank you for your investment! Indeed, I knew how to set the SYSTEM_POWER_STATUS type, but I could not figure out from the Windows.pas unit how to import the function.
I have included the declaration from the Winapi.Windows unit, so you can see how it compares to what I suggested.
Indeed, as a novice user, I was literally lost in the plethora of statements and pointers... Thank you for your help!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.