Adding a shortcut to a programmatically added system menu option - delphi

In my application, I have a base form in which various items are added to the system menu, for example
AppendMenu (SysMenu, MF_SEPARATOR, 0, '');
AppendMenu (SysMenu, MF_STRING, SC_Sticky, 'Sticky');
AppendMenu (SysMenu, MF_STRING, SC_Original, 'Original');
How does one add keyboard shortcuts to these menu options (eg Alt-F2, Alt-F3)?
I can't use the standard method of using an accelerator (ie &Sticky for Alt-S) as the real menu captions are in Hebrew and accelerators don't seem to work properly with this language.

Here's an example that uses an accelerator table:
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, AppEvnts;
type
TForm1 = class(TForm)
ApplicationEvents1: TApplicationEvents;
procedure FormCreate(Sender: TObject);
procedure ApplicationEvents1Message(var Msg: tagMSG; var Handled: Boolean);
procedure FormDestroy(Sender: TObject);
private
FAccelTable: HACCEL;
FAccels: array[0..1] of TAccel;
protected
procedure WMSysCommand(var Message: TWMSysCommand); message WM_SYSCOMMAND;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
const
SC_Sticky = 170;
SC_Original = 180;
procedure TForm1.FormCreate(Sender: TObject);
var
SysMenu: HMENU;
begin
SysMenu := GetSystemMenu(Handle, False);
AppendMenu (SysMenu, MF_SEPARATOR, 0, '');
AppendMenu (SysMenu, MF_STRING, SC_Sticky, 'Sticky'#9'Alt+F2');
AppendMenu (SysMenu, MF_STRING, SC_Original, 'Original'#9'Alt+F3');
FAccels[0].fVirt := FALT or FVIRTKEY;
FAccels[0].key := VK_F2;
FAccels[0].cmd := SC_Sticky;
FAccels[1].fVirt := FALT or FVIRTKEY;
FAccels[1].key := VK_F3;
FAccels[1].cmd := SC_Original;
FAccelTable := CreateAcceleratorTable(FAccels, 2);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
DestroyAcceleratorTable(FAccelTable);
end;
procedure TForm1.ApplicationEvents1Message(var Msg: tagMSG;
var Handled: Boolean);
begin
TranslateAccelerator(Handle, FAccelTable, Msg);
inherited;
end;
procedure TForm1.WMSysCommand(var Message: TWMSysCommand);
begin
inherited;
case Message.CmdType of
SC_Sticky: ShowMessage('sticky');
SC_Original: ShowMessage('original');
end;
end;

Related

How can I do PING threads, reading OnReply event in Delphi 6?

I have a problem with Delphi 6 and Indy's TIdIcmpClient component.
I get this message when compiling the following code, in the marked line (51):
FPing.OnReply := OnPingReply;
[Error] fire.pas(51): Incompatible types: 'TComponent' and 'TIdIcmpClient'
How should I fix it?
unit fire;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdRawBase, IdRawClient,
IdIcmpClient;
type
TForm1 = class(TForm)
ListBox1: TListBox;
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
type
TMyThread = class(TThread)
private
FIndex: Integer;
FPing: TIdIcmpClient;
FIP: string;
protected
procedure Execute; override;
procedure OnPingReply(ASender: TIdIcmpClient; AReplyStatus: TReplyStatus);
public
constructor Create(AIndex: Integer);
destructor Destroy; override;
end;
constructor TMyThread.Create(AIndex: Integer);
begin
inherited Create(False);
FIndex := AIndex;
FIP := '192.168.1.' + IntToStr(FIndex + 1);
FPing := TIdIcmpClient.Create(nil);
FPing.Host:=FIP;
FPing.ReceiveTimeout:=1500;
FPing.OnReply := OnPingReply;
end;
destructor TMyThread.Destroy;
begin
FPing.Free;
inherited;
end;
//var// icmp:array[0..10] of TIdIcmpClient;
// ip:string;
procedure TMyThread.Execute; // aici e ce face thread-ul
var
i: Integer;
begin
FPing.Ping;
// ICMP.Ping('a',1000);
// Sleep(1300);
// form1.memo1.lines.add(IntToStr(findex)+' '+ICMP.ReplyStatus.fromipaddress);
for i := 1 to 1 do
begin
// 'findex' este indexul thread-ului din matrice
form1.memo1.lines.add(inttostr(findex)+' Thread running...');
application.ProcessMessages;
Sleep(1000);
end;
end;
procedure TMyThread.OnPingReply(ASender: TIdIcmpClient; AReplyStatus: TReplyStatus);
begin
if AReplyStatus.BytesReceived > 0 then
form1.memo1.Lines.add(FIP+ ' is reachable')
else
form1.memo1.Lines.add(FIP+ ' is not reachable: ');
end;
procedure TForm1.Button1Click(Sender: TObject);
var
MyThreads: array[0..10] of TMyThread;
// icmp:array[0..10] of TIdIcmpClient;
i: Integer;
begin
{ for i := 0 to 10 do //10 fire
begin
icmp[i]:=tidicmpclient.create(nil);
icmp[i].ReceiveTimeout:=1200;
ip:=Format('%s.%d', ['192.168.1', i]);
ICMP[i].Host :=ip;
end; }
for i := 0 to 10 do //10 fire
begin
MyThreads[i] := TMyThread.Create(i);
MyThreads[i].Resume;
application.ProcessMessages;
end;
// Readln;
for i := 0 to 10 do
begin
MyThreads[i].Free;
// icmp[i].Free;
end;
end;
end.
I expected it to be compilable, but I don't see the reason why it is not.
Your event handler is declared wrong. The ASender parameter needs to be TComponent rather than TIdIcmpClient, and the AReplyStatus parameter needs to be const:
procedure OnPingReply(ASender: TComponent; const AReplyStatus: TReplyStatus);
That being said, you don't need to use the OnReply event at all in this situation. TIdIcmpClient operates synchronously, so you can simply use the TIdIcmpClient.ReplyStatus property after the TIdIcmpClient.Ping() method exits:
procedure TMyThread.Execute; // aici e ce face thread-ul
var
...
begin
FPing.Ping;
if FPing.ReplyStatus.BytesReceived > 0 then
...
else
...
...
end;
Also, you must synchronize with the main UI thread when accessing UI controls in a worker thread. You can use TThread.Synchronize() method for that.
And, you do not need to call Application.ProcessMessages() in a worker thread. Doing so will have no effect on the main UI thread.
With all of that said, try something more like this:
unit fire;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdRawBase, IdRawClient,
IdIcmpClient;
type
TForm1 = class(TForm)
ListBox1: TListBox;
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
procedure AddText(const AText: String);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
type
TMyThread = class(TThread)
private
FIndex: Integer;
FPing: TIdIcmpClient;
FIP: string;
FText: String;
procedure AddTextToUI(const AText: String);
procedure DoSyncText;
protected
procedure Execute; override;
public
constructor Create(AIndex: Integer);
destructor Destroy; override;
end;
constructor TMyThread.Create(AIndex: Integer);
begin
inherited Create(False);
FIndex := AIndex;
FIP := '192.168.1.' + IntToStr(FIndex + 1);
FPing := TIdIcmpClient.Create(nil);
FPing.Host := FIP;
FPing.ReceiveTimeout := 1500;
end;
destructor TMyThread.Destroy;
begin
FPing.Free;
inherited;
end;
procedure TMyThread.AddTextToUI(const AText: String);
begin
FText := AText;
Synchronize(DoSyncText);
end;
procedure TMyThread.DoSyncText;
begin
Form1.AddText(FText);
end;
procedure TMyThread.Execute; // aici e ce face thread-ul
begin
AddTextToUI(IntToStr(FIndex) + ' Thread running...');
try
FPing.Ping;
except
AddTextToUI('Error pinging ' + FIP);
Exit;
end;
if FPing.ReplyStatus.BytesReceived > 0 then
AddTextToUI(FIP + ' is reachable')
else
AddTextToUI(FIP + ' is not reachable');
end;
procedure TForm1.Button1Click(Sender: TObject);
var
MyThreads: array[0..10] of TMyThread;
I: Integer;
begin
for I := Low(MyThreads) to High(MyThreads) do //10 fire
begin
MyThreads[I] := TMyThread.Create(I);
end;
for I := Low(MyThreads) to High(MyThreads) do
begin
MyThreads[i].WaitFor;
MyThreads[i].Free;
end;
end;
procedure TForm1.AddText(const AText: String);
begin
Memo1.Lines.Add(AText);
end;
end.

Why the famous workaround for closing a popup menu with Esc is not working with a private handle?

I made a component to use tray icons in my application and when the icon shows the popup menu, it can't be closed with Esc key. Then I found a workaround here, by David Heffernan. I integrate the code in my component and now the menu can be closed with Esc but after I popup the menu my application become compleately dead, I can't access anything on the main form, even the system buttons doesn't work any more.
Here is the code to reproduce the problem:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Menus, ShellApi;
const WM_ICONTRAY = WM_USER+1;
type
TForm1 = class(TForm)
PopupMenu1: TPopupMenu;
Test1: TMenuItem;
Test2: TMenuItem;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
IconData: TNotifyIconData;
protected
procedure PrivateWndProc(var Msg: TMessage); virtual;
public
PrivateHandle:HWND;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
PrivateHandle:=AllocateHWnd(PrivateWndProc);
// add an icon to tray
IconData.cbSize:=SizeOf(IconData);
IconData.Wnd:=PrivateHandle;
IconData.uID:=1;
IconData.uFlags:=NIF_MESSAGE + NIF_ICON;
IconData.uCallbackMessage:=WM_ICONTRAY;
IconData.hIcon:=Application.Icon.Handle;
Shell_NotifyIcon(NIM_ADD, #IconData);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
IconData.uFlags:=0;
Shell_NotifyIcon(NIM_DELETE, #IconData);
DeallocateHWnd(PrivateHandle);
end;
procedure TForm1.PrivateWndProc(var Msg: TMessage);
var p:TPoint;
begin
if (Msg.Msg = WM_ICONTRAY) and (Msg.LParam=WM_RBUTTONUP) then
begin
GetCursorPos(p);
SetForegroundWindow(PrivateHandle);
PopupMenu1.Popup(p.x,p.y);
PostMessage(PrivateHandle, WM_NULL, 0, 0);
end;
end;
end.
I guess you just missed to call DefWindowProc. Try this:
procedure TForm1.PrivateWndProc(var Msg: TMessage);
begin
if (Msg.Msg = WM_ICONTRAY) and (Msg.lParam = WM_RBUTTONUP) then
begin
...
end
else
Msg.Result := DefWindowProc(PrivateHandle, Msg.Msg, Msg.wParam, Msg.lParam);
end;

RemObjects Hydra Plugin Can't Handle WM_DEVICECHANGE Windows Messages Directly

I Creating a Hydra Host Application and a Hydra Plugin. I put a Procedure for Handling a Windows Message in Plugin; but in this case we can't handle this windows message. for solving this problem we can handle It in Host App and then we must talk with pluging via passing an Interface.
In this case I want to find a direct way for handle windows messages in Hydra Plugin. Please help me for solving this problem.
Update 1 for this Question:
this is a simple code for testing:
Plugin Side:
unit VisualPlugin;
interface
uses
{ vcl: } Windows, Messages, SysUtils, Variants, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls,
{ Hydra: } uHYVisualPlugin, uHYIntf;
type
TVisualPlugin1 = class(THYVisualPlugin)
private
procedure WMDEVICECHANGE(var Msg: TMessage); message WM_DEVICECHANGE;
end;
implementation
uses
{ Hydra: } uHYPluginFactories;
{$R *.dfm}
procedure Create_VisualPlugin1(out anInstance: IInterface);
begin
anInstance := TVisualPlugin1.Create(NIL);
end;
resourcestring
sDescription = '';
const
sRequiredPrivilege = '';
sUserData = '';
{ TVisualPlugin1 }
procedure TVisualPlugin1.WMDEVICECHANGE(var Msg: TMessage);
begin
// ===================================
// This Line Of Code Can't Be Run!!!!!!
ShowMessage('USB Changed');
// ===================================
end;
initialization
THYPluginFactory.Create(HInstance, 'VisualPlugin1', Create_VisualPlugin1,
TVisualPlugin1, 1, 0, sRequiredPrivilege, sDescription, sUserData);
end.
PluginController in Plugin Side:
unit hcPluginController;
interface
uses
{vcl:} SysUtils, Classes,
{Hydra:} uHYModuleController, uHYIntf, uHYCrossPlatformInterfaces;
type
TPluginController = class(THYModuleController)
private
public
end;
var
PluginController : TPluginController;
implementation
uses
{Hydra:} uHYRes;
{$R *.dfm}
procedure HYGetCrossPlatformModule(out result: IHYCrossPlatformModule); stdcall;
begin
result := PluginController as IHYCrossPlatformModule;
end;
function HYGetModuleController : THYModuleController;
begin
result := PluginController;
end;
exports
HYGetCrossPlatformModule,
HYGetModuleController name name_HYGetModuleController;
resourcestring
sDescription = '';
const
sRequiredPrivilege = '';
initialization
PluginController := TPluginController.Create('Plugin.Library', 1, 0, sRequiredPrivilege, sDescription);
finalization
FreeAndNil(PluginController);
end.
Host Application Side:
unit fMain;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, uHYModuleManager, uHYIntf, ExtCtrls, StdCtrls;
type
TMainForm = class(TForm)
HYModuleManager1: THYModuleManager;
Panel1: TPanel;
btnLoadPlugin: TButton;
procedure btnLoadPluginClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
public
{ Public declarations }
end;
var
MainForm: TMainForm;
implementation
{$R *.dfm}
var
AppDir: string;
fPlugin: IHYVisualPlugin;
const
PluginDll = 'Plugin.dll';
PluginName = 'VisualPlugin1';
procedure TMainForm.btnLoadPluginClick(Sender: TObject);
begin
if HYModuleManager1.FindModule(AppDir + PluginDll) = nil then
HYModuleManager1.LoadModule(AppDir + PluginDll);
HYModuleManager1.CreateVisualPlugin(PluginName, fPlugin, Panel1);
end;
procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
HYModuleManager1.ReleaseInstance(fPlugin);
HYModuleManager1.UnloadModules;
end;
procedure TMainForm.FormCreate(Sender: TObject);
begin
AppDir := ExtractFilePath(Application.ExeName);
end;
end.
Not sure about the real cause of the problem, but you can use RegisterDeviceNotification function to achieve same result:
type
DEV_BROADCAST_DEVINTERFACE = record
dbcc_size: DWORD;
dbcc_devicetype: DWORD;
dbcc_reserved: DWORD;
dbcc_classguid: TGUID;
dbcc_name: short;
end;
const
DEVICE_NOTIFY_ALL_INTERFACE_CLASSES = $4;
DBT_DEVTYP_DEVICEINTERFACE = $5;
function RegisterNotification(Handle: THandle): HDEVNOTIFY;
var
Filter: DEV_BROADCAST_DEVINTERFACE;
begin
ZeroMemory(#Filter, SizeOf(DEV_BROADCAST_DEVINTERFACE));
Filter.dbcc_size := SizeOf(DEV_BROADCAST_DEVINTERFACE);
Filter.dbcc_devicetype := DBT_DEVTYP_DEVICEINTERFACE;
Filter.dbcc_reserved := 0;
Filter.dbcc_name := 0;
Result := RegisterDeviceNotification(Handle, #Filter, DEVICE_NOTIFY_WINDOW_HANDLE or DEVICE_NOTIFY_ALL_INTERFACE_CLASSES);
end;
Now inside plugin you need something like this:
TVisualPlugin = class(THYVisualPlugin)
protected
NofitifyHandle: HDEVNOTIFY;
procedure WMDEVICECHANGE(var Msg: TMessage); message WM_DEVICECHANGE;
procedure CreateWnd; override;
procedure DestroyWindowHandle; override;
end;
procedure TVisualPlugin.CreateWnd;
begin
inherited;
if HandleAllocated then
NofitifyHandle := RegisterNotification(Self.Handle);
end;
procedure TVisualPlugin.DestroyWindowHandle;
begin
if Assigned(NofitifyHandle) then begin
UnregisterDeviceNotification(NofitifyHandle);
NofitifyHandle := nil;
end;
inherited;
end;
procedure TVisualPlugin.WMDEVICECHANGE(var Msg: TMessage);
begin
ShowMessage('USB Changed');
end;

How can I handle a keyboard shortcut when my program isn't active?

Is it ok if i use it like this..for multiple events?
unit Unit4;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Clipbrd;
type
TForm4 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure WMHotkey(var Message: TWMHotKey); message WM_HOTKEY;
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form4: TForm4;
implementation
const
MY_ID = 123;
MY_ID1 = 123;
MY_ID2 = 123;
{$R *.dfm}
procedure TForm4.FormCreate(Sender: TObject);
begin
RegisterHotKey(Handle, MY_ID, MOD_CONTROL, ord('1'));
RegisterHotKey(Handle, MY_ID1, MOD_CONTROL, ord('2'));
RegisterHotKey(Handle, MY_ID2, MOD_CONTROL, ord('3'));
end;
procedure TForm4.FormDestroy(Sender: TObject);
begin
UnregisterHotKey(Handle, MY_ID);
UnregisterHotKey(Handle, MY_ID1);
UnregisterHotKey(Handle, MY_ID2);
end;
procedure TForm4.WMHotkey(var Message: TWMHotKey);
begin
if Message.HotKey = MY_ID then
begin
if not AttachThreadInput(GetCurrentThreadId, GetWindowThreadProcessId(GetForegroundWindow), true) then
RaiseLastOSError;
try
Clipboard.AsText := 'text1';
SendMessage(GetFocus, WM_PASTE, 0, 0);
finally
AttachThreadInput(GetCurrentThreadId, GetWindowThreadProcessId(GetForegroundWindow), false);
end;
if Message.HotKey = MY_ID1 then
begin
if not AttachThreadInput(GetCurrentThreadId, GetWindowThreadProcessId(GetForegroundWindow), true) then
RaiseLastOSError;
try
Clipboard.AsText := 'text2';
SendMessage(GetFocus, WM_PASTE, 0, 0);
finally
AttachThreadInput(GetCurrentThreadId, GetWindowThreadProcessId(GetForegroundWindow), false);
end;
if Message.HotKey = MY_ID2 then
begin
if not AttachThreadInput(GetCurrentThreadId, GetWindowThreadProcessId(GetForegroundWindow), true) then
RaiseLastOSError;
try
Clipboard.AsText := 'text3';
SendMessage(GetFocus, WM_PASTE, 0, 0);
finally
AttachThreadInput(GetCurrentThreadId, GetWindowThreadProcessId(GetForegroundWindow), false);
end;
end;
end;
end;
end;
end.
Use the RegisterHotKey function. If you want the application to be invisible, you might want all the details in my answer to a similar question.
Try this:
unit Unit4;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Clipbrd;
type
TForm4 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure WMHotkey(var Message: TWMHotKey); message WM_HOTKEY;
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form4: TForm4;
implementation
const
MY_ID = 123;
{$R *.dfm}
procedure TForm4.FormCreate(Sender: TObject);
begin
RegisterHotKey(Handle, MY_ID, MOD_CONTROL, ord('1'));
end;
procedure TForm4.FormDestroy(Sender: TObject);
begin
UnregisterHotKey(Handle, MY_ID);
end;
procedure TForm4.WMHotkey(var Message: TWMHotKey);
begin
if Message.HotKey = MY_ID then
begin
if not AttachThreadInput(GetCurrentThreadId, GetWindowThreadProcessId(GetForegroundWindow), true) then
RaiseLastOSError;
try
Clipboard.AsText := 'This is my own text!';
SendMessage(GetFocus, WM_PASTE, 0, 0);
finally
AttachThreadInput(GetCurrentThreadId, GetWindowThreadProcessId(GetForegroundWindow), false);
end;
end;
end;
end.
Of course, you will need to use this approach and modify it so it suits your particular case. (That is, you probably want something more than an application that prints "This is my own text!" on Ctrl+1, but nothing else.)
to complement the Andreas answer you can use the RegisterHotKey function in combination with the WM_HOTKEY windows message.
try this code
type
TForm17 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
HotKey1 : Integer;
HotKey2 : Integer;
procedure WMHotKey(var Msg: TWMHotKey); message WM_HOTKEY;
public
{ Public declarations }
end;
var
Form17: TForm17;
implementation
{$R *.dfm}
{ TForm17 }
procedure TForm17.FormCreate(Sender: TObject);
const
MOD_CONTROL = $0002;//0x0002
begin
// Register Ctrl + 1 hotkey
HotKey1 := GlobalAddAtom('Hotkey1');
RegisterHotKey(Handle, HotKey1, MOD_CONTROL, Ord('1'));
// Register Ctrl + 2 hotkey
HotKey2 := GlobalAddAtom('Hotkey2');
RegisterHotKey(Handle, HotKey2, MOD_CONTROL, Ord('2'));
end;
procedure TForm17.FormDestroy(Sender: TObject);
begin
//unregister the hotkeys
UnRegisterHotKey(Handle, HotKey1);
GlobalDeleteAtom(HotKey1);
UnRegisterHotKey(Handle, HotKey2);
GlobalDeleteAtom(HotKey2);
end;
procedure TForm17.WMHotKey(var Msg: TWMHotKey);
begin
if Msg.HotKey = HotKey1 then
begin
ShowMessage('Ctrl + 1 was pressed');
//do your stuff
end
else
if Msg.HotKey = HotKey2 then
begin
ShowMessage('Ctrl + 2 was pressed');
//do your stuff
end;
end;
As others suggested, it's RegisterHotKey function. However, proper implementation of the application that you want to design requires keyboard hook and DLL injection into the application.
I would recommend that you take a look at TypePilot application. It lets you type or copy/paste any text with certain shortcuts that you type. Eg. you can type "thnk " and this will be replaced with "thank you" by the application.

How to detect when the laptop is running on batteries?

How to detect (from Delphi) when the laptop is running on batteries (or AC)?
To be notified when the status changes on Vista and Windows 7 you can use RegisterPowerSettingNotification.
For Windows 2000 and later, look at GetSystemPowerStatus, or go to MSDN and read about Power Management.
(Someone always posts while I am typing :-( )
function GetBattery : Boolean;
var
SysPowerStatus: TSystemPowerStatus;
begin
Win32Check(GetSystemPowerStatus(SysPowerStatus));
case SysPowerStatus.ACLineStatus of
0: Result := False;
1: begin
Result := True;
// You can return life with
// String := Format('Battery power left: %u percent.', SysPowerStatus.BatteryLifePercent]);
end;
else
raise Exception.Create('Unknown battery status');
end;
end;
There's a WINAPI function that I believe does this, GetSystemPowerStatus, which I believe you can execute from Delphi.
Here part of code that detect when laptop is running on batteries (if not it triggers some event):
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
WTSSessionNotification, StdCtrls, MediaPlayer, Buttons, ShellAPI, Settings,
ExtCtrls;
const
WM_ICONTRAY = WM_USER + 1;
type
TSettingsForm = class(TForm)
OpenDialog: TOpenDialog;
pnl1: TPanel;
InfoLabel: TLabel;
grp1: TGroupBox;
AlarmSoundLabel: TLabel;
lbl1: TLabel;
checkIfLocked: TCheckBox;
Filename: TEdit;
Browse: TBitBtn;
TestSound: TBitBtn;
btn1: TBitBtn;
lbl2: TLabel;
procedure Minimize(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure TestSoundClick(Sender: TObject);
procedure BrowseClick(Sender: TObject);
procedure checkIfLockedClick(Sender: TObject);
procedure OpenHomepage(Sender: TObject);
procedure btn1Click(Sender: TObject);
private
TrayIconData: TNotifyIconData;
procedure CheckForAC;
protected
procedure WndProc(var Message: TMessage); override;
public
{ Public declarations }
Function SecuredLockWorkStation : Boolean ;
end;
var
SettingsForm: TSettingsForm;
implementation
{$R *.DFM}
{$R WindowsXP.RES}
var
MPlayer: TMPlayer;
mySettings: TSettings;
isLocked: boolean = false;
// true if A/C is connected, false if not
function ACConnected: boolean;
var PowerStatus: TSystemPowerStatus;
begin
GetSystemPowerStatus(PowerStatus);
result := (PowerStatus.ACLineStatus = 1);
end;
// handles application.minimize; do not really
// minimize, but hide settings window
procedure TSettingsForm.Minimize(Sender: TObject);
begin
Application.Restore;
self.Hide;
end;
// processes window messages (notification about
// power status changes, locking of workstation and
// tray icon activity)
procedure TSettingsForm.WndProc(var Message: TMessage);
begin
case Message.Msg of
WM_WTSSESSION_CHANGE:
begin
if Message.wParam = WTS_SESSION_LOCK then
isLocked := true;
if Message.wParam = WTS_SESSION_UNLOCK then
begin
isLocked := false;
if MPlayer.isPlaying then
MPlayer.Close;
end;
end;
WM_POWERBROADCAST:
begin
if (isLocked) or (checkIfLocked.checked=false) then
CheckForAC;
end;
WM_ICONTRAY:
begin
case Message.lParam of
WM_LBUTTONDOWN:
begin
if SettingsForm.visible then
SettingsForm.Hide
else
SettingsForm.Show;
end;
WM_RBUTTONUP:
begin
if SettingsForm.visible then
SettingsForm.Hide
else
SettingsForm.Close;
end;
end;
end;
end;
inherited;
end;

Resources