program ToTray;
{
Copyright (c) 1996 by Charlie Calvert
Modifications by Florian Klaempfl
Standard Windows API application written in Object Pascal.
No VCL code included. This is all done on the Windows API
level.
Last modifications by Torbins.
}
uses
Messages, Windows;
const
ClassName = 'ToTrayWindClass';
WindowName = 'ToTray';
NIM_ADD = $00000000;
NIM_MODIFY = $00000001;
NIM_DELETE = $00000002;
NIF_MESSAGE = $00000001;
NIF_ICON = $00000002;
NIF_TIP = $00000004;
type
TNotifyIconData = record
cbSize: DWORD;
Wnd: HWND;
uID: UINT;
uFlags: UINT;
uCallbackMessage: UINT;
hIcon: HICON;
szTip: array [0..63] of Char;
end;
PNotifyIconData = ^TNotifyIconData;
function Shell_NotifyIcon(dwMessage: DWORD; lpData: PNotifyIconData): BOOL;
stdcall; external 'shell32.dll' name 'Shell_NotifyIconA';
var
IconCapt: PChar;
hWindow, hFWind: HWnd;
hIcon: Windows.HICON;
i: Integer;
WndThread: Cardinal;
TrayMes, TaskbarCreated: Cardinal;
ThreadWindows: array of HWnd;
AMessage: Msg;
Param: String;
{ From SysUtils }
function StrComp(const Str1, Str2: PChar): Integer; assembler;
asm
PUSH EDI
PUSH ESI
MOV EDI,EDX
MOV ESI,EAX
MOV ECX,0FFFFFFFFH
XOR EAX,EAX
REPNE SCASB
NOT ECX
MOV EDI,EDX
XOR EDX,EDX
REPE CMPSB
MOV AL,[ESI-1]
MOV DL,[EDI-1]
SUB EAX,EDX
POP ESI
POP EDI
end;
function StrLCopy(Dest: PChar; const Source: PChar; MaxLen: Cardinal): PChar; assembler;
asm
PUSH EDI
PUSH ESI
PUSH EBX
MOV ESI,EAX
MOV EDI,EDX
MOV EBX,ECX
XOR AL,AL
TEST ECX,ECX
JZ @@1
REPNE SCASB
JNE @@1
INC ECX
@@1: SUB EBX,ECX
MOV EDI,ESI
MOV ESI,EDX
MOV EDX,EDI
MOV ECX,EBX
SHR ECX,2
REP MOVSD
MOV ECX,EBX
AND ECX,3
REP MOVSB
STOSB
MOV EAX,EDX
POP EBX
POP ESI
POP EDI
end;
{ From JclSysInfo }
function GetWindowIcon(Wnd: HWND): Windows.HICON;
begin
Result := GetClassLong(Wnd, GCL_HICON);
if Result = 0 then
Result := SendMessage(Wnd, WM_GETICON, ICON_BIG, 0);
if Result = 0 then
Result := LoadIcon(0, MakeIntResource(32512));
end;
{ Add Icon to Tray }
procedure AddIcon(h: HWND; m: Cardinal; hi: Windows.HICON; t: PChar);
var NIData: TNotifyIconData;
begin
FillChar(NIData, SizeOf(TNotifyIconData), 0);
with NIData do
begin
cbSize := SizeOf(TNotifyIconData);
Wnd := h;
uID := 1;
uFlags := NIF_ICON or NIF_MESSAGE or NIF_TIP;
uCallBackMessage := m;
hIcon := hi;
CopyMemory(@szTip, t, 64);
end;
Shell_NotifyIcon(NIM_ADD, @nidata);
end;
procedure ModifyIcon(h: HWND; f: Cardinal; hi: Windows.HICON; t: PChar);
var NIData: TNotifyIconData;
begin
FillChar(NIData, SizeOf(TNotifyIconData), 0);
with NIData do
begin
cbSize := SizeOf(TNotifyIconData);
Wnd := h;
uID := 1;
uFlags := f;
hIcon := hi;
if f and NIF_TIP = NIF_TIP then
CopyMemory(@szTip, t, 64);
end;
Shell_NotifyIcon(NIM_MODIFY, @nidata);
end;
{ Remove Icon from Tray }
procedure DelIcon(h: HWND);
var NIData: TNotifyIconData;
begin
FillChar(NIData, SizeOf(TNotifyIconData), 0);
with NIData do
begin
cbSize := SizeOf(TNotifyIconData);
Wnd := h;
uID := 1;
end;
Shell_NotifyIcon(NIM_DELETE, @nidata);
end;
{ Process Window and Tray Massages }
function WindowProc(Window: HWnd; AMessage: UINT; WParam : WPARAM;
LParam: LPARAM): LRESULT; stdcall; export;
var
twl, j: Integer;
tmpIcon: Windows.HICON;
tmpCapt: PChar;
f: Cardinal;
begin
WindowProc := 0;
case AMessage of
wm_Destroy:
begin
DelIcon(hWindow);
twl := Length(ThreadWindows);
if twl > 0 then
for j := 0 to twl-1 do
ShowWindow(ThreadWindows[j], SW_SHOW)
else
ShowWindow(hFWind, SW_SHOW);
PostQuitMessage(0);
Exit;
end;
wm_Timer:
begin
GetMem(tmpCapt, 64);
try
FillChar(tmpCapt^, 63, 0);
GetWindowText(hFWind, tmpCapt, 63);
tmpIcon := GetWindowIcon(hFWind);
f := 0;
if tmpIcon <> hIcon then
begin
f := NIF_ICON;
hIcon := tmpIcon;
end;
if StrComp(tmpCapt, IconCapt) <> 0 then
begin
f := f or NIF_TIP;
StrLCopy(IconCapt, tmpCapt, 63);
end;
if f <> 0 then
ModifyIcon(hWindow, f, hIcon, IconCapt);
finally
FreeMem(tmpCapt);
end;
end;
else
if AMessage = TrayMes then
begin
if (lParam = WM_LBUTTONDOWN) or (lParam = WM_RBUTTONDOWN) or
(lParam = WM_MBUTTONDOWN) then
begin
PostMessage(hWindow, WM_DESTROY, 0, 0);
end;
end
else
if AMessage = TaskbarCreated then
begin
DelIcon(hWindow);
FillChar(IconCapt^, 63, 0);
GetWindowText(hFWind, IconCapt, 63);
AddIcon(hWindow, TrayMes, GetWindowIcon(hFWind), IconCapt);
end;
end;
WindowProc := DefWindowProc(Window, AMessage, WParam, LParam);
end;
{ Register the Window Class }
function WinRegister: Boolean;
var
WindowClass: WndClass;
begin
WindowClass.Style := 0;
WindowClass.lpfnWndProc := @WindowProc;
WindowClass.cbClsExtra := 0;
WindowClass.cbWndExtra := 0;
WindowClass.hInstance := system.MainInstance;
WindowClass.hIcon := LoadIcon(0, idi_Application);
WindowClass.hCursor := LoadCursor(0, idc_Arrow);
WindowClass.hbrBackground := COLOR_WINDOW;
WindowClass.lpszMenuName := nil;
WindowClass.lpszClassName := ClassName;
Result := RegisterClass(WindowClass) <> 0;
end;
{ Create the Window Class }
function WinCreate: HWnd;
begin
Result := CreateWindow(ClassName, WindowName, ws_Overlapped, -100, -100, 50,
30, 0, 0, system.MainInstance, nil);
{if Result <> 0 then begin
ShowWindow(Result, SW_SHOWNA);
ShowWindow(Result, SW_HIDE);
end;}
end;
function GetErrorStr(code:Cardinal): string;
var
MsgBuf: PChar;
begin
Result:='';
if FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER or
FORMAT_MESSAGE_FROM_SYSTEM, Nil, code, 0, @MsgBuf, 0, Nil)>0 then
begin
Result:=MsgBuf;
LocalFree(Cardinal(MsgBuf));
end;
end;
function OnTaskBar(Window: HWnd): Boolean;
begin
Result := False;
if (GetParent(Window) = 0) and (GetWindowLong(Window, GWL_EXSTYLE) and
WS_EX_TOOLWINDOW = 0) and (GetWindowLong(Window, GWL_EXSTYLE) and
WS_EX_NOACTIVATE = 0) then
Result := True;
end;
function EnumThreadWndProc(Window: HWnd; LParam: LPARAM): BOOL; stdcall;
var
twl: Integer;
CName: PAnsiChar;
begin
Result := True;
if IsWindowVisible(Window) then
begin
twl := Length(ThreadWindows);
SetLength(ThreadWindows, twl+1);
ThreadWindows[twl] := Window;
GetMem(CName, 256);
try
FillChar(CName^, 255, 0);
if GetClassName(Window, CName, 255) = 0 then
MessageBox(0, PChar('Can''t get Class Name'#13#10+
GetErrorStr(GetLastError)), nil, mb_Ok);
if (CName = 'TApplication') and OnTaskBar(Window) then
hFWind := Window;
finally
FreeMem(CName);
end;
end;
end;
begin
TrayMes := RegisterWindowMessage('WM_Tray');
TaskbarCreated := RegisterWindowMessage('TaskbarCreated');
hFWind := GetForegroundWindow;
//hFWind := FindWindow('TTimerForm', nil);
if (hFWind <> 0) and (hFWind <> GetDesktopWindow) and
(hFWind <> FindWindow('Shell_TrayWnd', nil)) and
(hFWind <> FindWindow('Progman', nil)) then
begin
if not WinRegister then
begin
MessageBox(0, PChar('Register failed'#13#10+GetErrorStr(GetLastError)),
nil, mb_Ok);
Exit;
end;
hWindow := WinCreate;
if longint(hWindow) = 0 then
begin
MessageBox(0, PChar('WinCreate failed'#13#10+GetErrorStr(GetLastError)),
nil, mb_Ok);
Exit;
end;
GetMem(IconCapt, 64);
try
Param := ParamStr(1);
if (Param = '/AllWnd') or (Param = '-AllWnd') then
begin
WndThread := GetWindowThreadProcessId(hFWind);
EnumThreadWindows(WndThread, @EnumThreadWndProc, 0);
FillChar(IconCapt^, 63, 0);
GetWindowText(hFWind, IconCapt, 63);
hIcon := GetWindowIcon(hFWind);
AddIcon(hWindow, TrayMes, hIcon, IconCapt);
if Length(ThreadWindows) > 0 then
for i := 0 to Length(ThreadWindows)-1 do
ShowWindow(ThreadWindows[i], SW_HIDE)
else
ShowWindow(hFWind, SW_HIDE);
end
else
begin
FillChar(IconCapt^, 63, 0);
GetWindowText(hFWind, IconCapt, 63);
hIcon := GetWindowIcon(hFWind);
AddIcon(hWindow, TrayMes, hIcon, IconCapt);
ShowWindow(hFWind, SW_HIDE);
end;
//Param := '';
if SetTimer(hWindow, 1, 1000, nil) = 0 then
MessageBox(0, PChar('SetTimer failed'#13#10+GetErrorStr(GetLastError)),
nil, mb_Ok);
while GetMessage(AMessage, 0, 0, 0) do
begin
TranslateMessage(AMessage);
DispatchMessage(AMessage);
end;
finally
FreeMem(IconCapt);
KillTimer(hWindow, 1);
end;
Halt(AMessage.wParam);
end;
end.