home *** CD-ROM | disk | FTP | other *** search
- {
- BUSINESS CONSULTING
- s a i n t - p e t e r s b u r g
-
- Components Library for Borland Delphi 4.x, 5.x
- Copyright (c) 1998-2000 Alex'EM
-
- }
- unit DCTray;
- {$I DCConst.inc}
-
- interface
-
- uses Windows, Messages,
- Classes, Graphics, SysUtils, Forms, Controls, Menus, ShellAPI, DCConst;
-
- const
- NIF_INFO = $00000010;
-
- NIIF_NONE = $00000000;
- NIIF_INFO = $00000001;
- NIIF_WARNING = $00000002;
- NIIF_ERROR = $00000003;
-
- NOTIFYICONDATA_V1_SIZE = 88;
-
- type
-
- PNotifyIconDataEx = ^TNotifyIconDataEx;
- TNotifyIconDataEx = record
- cbSize: DWORD;
- Wnd: HWND;
- uID: UINT;
- uFlags: UINT;
- uCallbackMessage: UINT;
- hIcon: HICON;
- szTip: array [0..MAXCHAR] of AnsiChar;
- {Windows 5.x support}
- dwState: DWORD;
- dwStateMask: DWORD;
- szInfo: array[0..MAXBYTE] of AnsiChar;
- uTimeout: UINT;
- szInfoTitle: array [0..63] of AnsiChar;
- dwInfoFlags: DWORD;
- end;
-
- TBaloonTimeout = 10..30;
- TBaloonInfoType = (biNone, biInfo, biWarning, biError);
- TMouseButtons = set of TMouseButton;
-
- TDCTrayIcon = class(TComponent)
- private
- FHandle: HWnd;
- FActive: Boolean;
- FAdded: Boolean;
- FClicked: TMouseButtons;
- FIconData: TNotifyIconDataEx;
- FIcon: TIcon;
- FDestroying: Boolean;
- FHint: string;
- FShowDesign: Boolean;
- FPopupMenu: TPopupMenu;
- FOnClick: TMouseEvent;
- FOnDblClick: TNotifyEvent;
- FOnMouseMove: TMouseMoveEvent;
- FOnMouseDown: TMouseEvent;
- FOnMouseUp: TMouseEvent;
- FStartMinimized: boolean;
- procedure ChangeIcon;
- procedure SendCancelMode;
- function CheckMenuPopup(X, Y: Integer): Boolean;
- function CheckDefaultMenuItem: Boolean;
- procedure SetHint(const Value: string);
- procedure SetIcon(Value: TIcon);
- procedure SetPopupMenu(Value: TPopupMenu);
- procedure Activate;
- procedure Deactivate;
- procedure SetActive(Value: Boolean);
- procedure SetShowDesign(Value: Boolean);
- procedure IconChanged(Sender: TObject);
- procedure WndProc(var Message: TMessage);
- function GetActiveIcon: TIcon;
- procedure LoadDefaultIcon;
- function Win2k: boolean;
- protected
- procedure DblClick; dynamic;
- procedure DoClick(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); dynamic;
- procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); dynamic;
- procedure MouseMove(Shift: TShiftState; X, Y: Integer); dynamic;
- procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); dynamic;
- procedure Loaded; override;
- procedure Notification(AComponent: TComponent; Operation: TOperation); override;
- procedure UpdateNotifyData; virtual;
- property Handle: HWnd read FHandle;
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- procedure Hide;
- procedure Show;
- procedure ShowBaloonToolTip(const Info, InfoTitle: string;
- const BaloonType: TBaloonInfoType; const Timeout: TBaloonTimeout);
- published
- property Active: Boolean read FActive write SetActive default True;
- property Hint: string read FHint write SetHint;
- property Icon: TIcon read FIcon write SetIcon;
- property PopupMenu: TPopupMenu read FPopupMenu write SetPopupMenu;
- property ShowDesign: Boolean read FShowDesign write SetShowDesign stored False;
- property OnClick: TMouseEvent read FOnClick write FOnClick;
- property OnDblClick: TNotifyEvent read FOnDblClick write FOnDblClick;
- property OnMouseMove: TMouseMoveEvent read FOnMouseMove write FOnMouseMove;
- property OnMouseDown: TMouseEvent read FOnMouseDown write FOnMouseDown;
- property OnMouseUp: TMouseEvent read FOnMouseUp write FOnMouseUp;
- property StartMinimized: boolean read FStartMinimized write FStartMinimized;
- end;
-
- type
- TExecState = (esNormal, esMinimized, esMaximized, esHidden);
-
- type
- TPreviousInstance = class(TObject)
- private
- FMessageID: DWORD;
- FMutexHandle: THandle;
- FhPrevInst: boolean;
- FNewWndProc: Pointer;
- FDefWndProc: Pointer;
- protected
- procedure NewWndProc(var Message: TMessage);
- public
- destructor Destroy; override;
- procedure SethPrevInst;
- property MutexHandle: THandle read FMutexHandle;
- property hPrevInst: boolean read FhPrevInst write FhPrevInst;
- property MessageID: DWORD read FMessageID;
- end;
-
- function CheckToMultyInstance: boolean;
- function FileExecute(const FileName, Params, StartDir: string;
- InitialState: TExecState): THandle;
- function FileExecuteWait(const FileName, Params, StartDir: string;
- InitialState: TExecState): Integer;
-
- var
- PreviousInstance: TPreviousInstance;
-
- implementation
-
- const
- ShowCommands: array[TExecState] of Integer =
- (SW_SHOWNORMAL, SW_MINIMIZE, SW_SHOWMAXIMIZED, SW_HIDE);
-
- function FileExecute(const FileName, Params, StartDir: string;
- InitialState: TExecState): THandle;
- begin
- Result := ShellExecute(Application.Handle, nil, PChar(FileName),
- PChar(Params), PChar(StartDir), ShowCommands[InitialState]);
- end;
-
- function FileExecuteWait(const FileName, Params, StartDir: string;
- InitialState: TExecState): Integer;
- var
- Info: TShellExecuteInfo;
- ExitCode: DWORD;
- begin
- FillChar(Info, SizeOf(Info), 0);
- Info.cbSize := SizeOf(TShellExecuteInfo);
- with Info do begin
- fMask := SEE_MASK_NOCLOSEPROCESS;
- Wnd := Application.Handle;
- lpFile := PChar(FileName);
- lpParameters := PChar(Params);
- lpDirectory := PChar(StartDir);
- nShow := ShowCommands[InitialState];
- end;
- if ShellExecuteEx(@Info) then begin
- repeat
- Application.ProcessMessages;
- GetExitCodeProcess(Info.hProcess, ExitCode);
- until (ExitCode <> STILL_ACTIVE) or Application.Terminated;
- Result := ExitCode;
- end
- else Result := -1;
- end;
-
- procedure SwitchToWindow(Wnd: HWnd; Restore: Boolean);
- begin
- if IsWindowEnabled(Wnd) then begin
- SetForegroundWindow(Wnd);
- if Restore and IsWindowVisible(Wnd) then begin
- if not IsZoomed(Wnd) then
- SendMessage(Wnd, WM_SYSCOMMAND, SC_RESTORE, 0);
- SetFocus(Wnd);
- end;
- end;
- end;
-
- function GetShiftState: TShiftState;
- begin
- Result := [];
- if GetKeyState(VK_SHIFT ) < 0 then Include(Result, ssShift);
- if GetKeyState(VK_CONTROL) < 0 then Include(Result, ssCtrl);
- if GetKeyState(Vk_MENU ) < 0 then Include(Result, ssAlt);
- end;
-
- constructor TDCTrayIcon.Create(AOwner: Tcomponent);
- begin
- inherited Create(AOwner);
- {$IFDEF DELPHI_V6}
- FHandle := Classes.AllocateHWnd(WndProc);
- {$ELSE}
- FHandle := AllocateHWnd(WndProc);
- {$ENDIF}
- FIcon := TIcon.Create;
- FIcon.OnChange := IconChanged;
- FActive := True;
- StartMinimized := False;
- LoadDefaultIcon;
- end;
-
- destructor TDCTrayIcon.Destroy;
- begin
- FDestroying := True;
- FIcon.OnChange := nil;
- Deactivate;
- {$IFDEF DELPHI_V6}
- Classes.DeallocateHWnd(FHandle);
- {$ELSE}
- DeallocateHWnd(FHandle);
- {$ENDIF}
- FIcon.Free;
- FIcon := nil;
- inherited Destroy;
- end;
-
- procedure TDCTrayIcon.Loaded;
- begin
- inherited Loaded;
- if FActive and not (csDesigning in ComponentState) then Activate;
-
- if FStartMinimized then
- begin
- Application.ShowMainForm := False;
- ShowWindow(Application.Handle, SW_HIDE);
- end;
-
- end;
-
- procedure TDCTrayIcon.Notification(AComponent: TComponent;
- Operation: TOperation);
- begin
- inherited Notification(AComponent, Operation);
- if (AComponent = PopupMenu) and (Operation = opRemove) then
- PopupMenu := nil;
- end;
-
- procedure TDCTrayIcon.SetPopupMenu(Value: TPopupMenu);
- begin
- FPopupMenu := Value;
- if Value <> nil then Value.FreeNotification(Self);
- end;
-
- procedure TDCTrayIcon.SendCancelMode;
- var
- F: TForm;
- begin
- if not ((csDestroying in ComponentState) or FDestroying) then begin
- F := Screen.ActiveForm;
- if F = nil then F := Application.MainForm;
- if F <> nil then F.SendCancelMode(nil);
- end;
- end;
-
- function TDCTrayIcon.CheckMenuPopup(X, Y: Integer): Boolean;
- begin
- Result := False;
- if not (csDesigning in ComponentState) and Active and
- (PopupMenu <> nil) and PopupMenu.AutoPopup then
- begin
- PopupMenu.PopupComponent := Self;
- SendCancelMode;
- SwitchToWindow(FHandle, False);
- Application.ProcessMessages;
- try
- PopupMenu.Popup(X, Y);
- finally
- SwitchToWindow(FHandle, False);
- end;
- Result := True;
- end;
- end;
-
- function TDCTrayIcon.CheckDefaultMenuItem: Boolean;
- var
- Item: TMenuItem;
- I: Integer;
- begin
- Result := False;
- if not (csDesigning in ComponentState) and Active and
- (PopupMenu <> nil) and (PopupMenu.Items <> nil) then
- begin
- I := 0;
- while (I < PopupMenu.Items.Count) do begin
- Item := PopupMenu.Items[I];
- if Item.Default and Item.Enabled then begin
- Item.Click;
- Result := True;
- Break;
- end;
- Inc(I);
- end;
- end;
- end;
-
- procedure TDCTrayIcon.SetIcon(Value: TIcon);
- begin
- FIcon.Assign(Value);
- end;
-
- function TDCTrayIcon.GetActiveIcon: TIcon;
- begin
- Result := FIcon;
- end;
-
- procedure TDCTrayIcon.SetActive(Value: Boolean);
- begin
- if (Value <> FActive) then begin
- FActive := Value;
- if not (csDesigning in ComponentState) then
- if Value then Activate else Deactivate;
- end;
- end;
-
- procedure TDCTrayIcon.Show;
- begin
- Active := True;
- end;
-
- procedure TDCTrayIcon.Hide;
- begin
- Active := False;
- end;
-
- procedure TDCTrayIcon.SetShowDesign(Value: Boolean);
- begin
- if (csDesigning in ComponentState) then begin
- if Value then Activate else Deactivate;
- FShowDesign := FAdded;
- end;
- end;
-
- procedure TDCTrayIcon.IconChanged(Sender: TObject);
- begin
- ChangeIcon;
- end;
-
- procedure TDCTrayIcon.SetHint(const Value: string);
- begin
- if FHint <> Value then begin
- FHint := Value;
- ChangeIcon;
- end;
- end;
-
- procedure TDCTrayIcon.UpdateNotifyData;
- var
- Ico: TIcon;
- begin
- with FIconData do
- begin
- if Win2k then
- cbSize := SizeOf(TNotifyIconDataEx)
- else
- cbSize := NOTIFYICONDATA_V1_SIZE;
- Wnd := FHandle;
- uFlags := NIF_MESSAGE or NIF_ICON or NIF_TIP;
- Ico := GetActiveIcon;
- if Ico <> nil then
- hIcon := Ico.Handle
- else
- hIcon := INVALID_HANDLE_VALUE;
- StrPCopy(szTip, GetShortHint(FHint));
- uCallbackMessage := CM_TRAYICON;
- uID := 0;
- end;
- end;
-
- procedure TDCTrayIcon.Activate;
- var
- Ico: TIcon;
- begin
- Deactivate;
- Ico := GetActiveIcon;
- if (Ico <> nil) and not Ico.Empty then
- begin
- FClicked := [];
- UpdateNotifyData;
- FAdded := Shell_NotifyIcon(NIM_ADD, @FIconData);
- if (GetShortHint(FHint) = '') and FAdded then
- Shell_NotifyIcon(NIM_MODIFY, @FIconData);
- end;
- end;
-
- procedure TDCTrayIcon.Deactivate;
- begin
- Shell_NotifyIcon(NIM_DELETE, @FIconData);
- FAdded := False;
- FClicked := [];
- end;
-
- procedure TDCTrayIcon.ChangeIcon;
- var
- Ico: TIcon;
- begin
- if FAdded then begin
- Ico := GetActiveIcon;
- if (Ico <> nil) and not Ico.Empty then begin
- UpdateNotifyData;
- Shell_NotifyIcon(NIM_MODIFY, @FIconData);
- end
- else Deactivate;
- end
- else begin
- if ((csDesigning in ComponentState) and FShowDesign) or
- (not (csDesigning in ComponentState) and FActive) then Activate;
- end;
- end;
-
- procedure TDCTrayIcon.MouseMove(Shift: TShiftState; X, Y: Integer);
- begin
- if Assigned(FOnMouseMove) then FOnMouseMove(Self, Shift, X, Y);
- end;
-
- procedure TDCTrayIcon.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
- begin
- if Assigned(FOnMouseDown) then FOnMouseDown(Self, Button, Shift, X, Y);
- end;
-
- procedure TDCTrayIcon.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
- begin
- if Assigned(FOnMouseUp) then FOnMouseUp(Self, Button, Shift, X, Y);
- end;
-
- procedure TDCTrayIcon.DblClick;
- begin
- if not CheckDefaultMenuItem and Assigned(FOnDblClick) then
- FOnDblClick(Self);
- end;
-
- procedure TDCTrayIcon.DoClick(Button: TMouseButton; Shift: TShiftState;
- X, Y: Integer);
- begin
- if (Button = mbRight) and CheckMenuPopup(X, Y) then Exit;
- if Assigned(FOnClick) then FOnClick(Self, Button, Shift, X, Y);
- end;
-
- procedure TDCTrayIcon.WndProc(var Message: TMessage);
- var
- P: TPoint;
- Shift: TShiftState;
- begin
- try
- with Message do
- begin
- if Msg = CM_TRAYICON then begin
- case lParam of
- WM_LBUTTONDBLCLK:
- begin
- GetCursorPos(P);
- MouseDown(mbLeft, GetShiftState + [ssDouble], P.X, P.Y);
- DblClick;
- end;
- WM_RBUTTONDBLCLK:
- begin
- GetCursorPos(P);
- MouseDown(mbRight, GetShiftState + [ssDouble], P.X, P.Y);
- end;
- WM_MBUTTONDBLCLK:
- begin
- GetCursorPos(P);
- MouseDown(mbMiddle, GetShiftState + [ssDouble], P.X, P.Y);
- end;
- WM_MOUSEMOVE:
- begin
- GetCursorPos(P);
- MouseMove(GetShiftState, P.X, P.Y);
- end;
- WM_LBUTTONDOWN:
- begin
- GetCursorPos(P);
- MouseDown(mbLeft, GetShiftState + [ssLeft], P.X, P.Y);
- Include(FClicked, mbLeft);
- end;
- WM_LBUTTONUP:
- begin
- Shift := GetShiftState + [ssLeft];
- GetCursorPos(P);
- if (mbLeft in FClicked) then begin
- Exclude(FClicked, mbLeft);
- DoClick(mbLeft, Shift, P.X, P.Y);
- end;
- MouseUp(mbLeft, Shift, P.X, P.Y);
- end;
- WM_RBUTTONDOWN:
- begin
- GetCursorPos(P);
- MouseDown(mbRight, GetShiftState + [ssRight], P.X, P.Y);
- Include(FClicked, mbRight);
- end;
- WM_RBUTTONUP:
- begin
- Shift := GetShiftState + [ssRight];
- GetCursorPos(P);
- if (mbRight in FClicked) then begin
- Exclude(FClicked, mbRight);
- DoClick(mbRight, Shift, P.X, P.Y);
- end;
- MouseUp(mbRight, Shift, P.X, P.Y);
- end;
- WM_MBUTTONDOWN:
- begin
- GetCursorPos(P);
- MouseDown(mbMiddle, GetShiftState + [ssMiddle], P.X, P.Y);
- end;
- WM_MBUTTONUP:
- begin
- GetCursorPos(P);
- MouseUp(mbMiddle, GetShiftState + [ssMiddle], P.X, P.Y);
- end;
- end;
- end
- else Result := DefWindowProc(FHandle, Msg, wParam, lParam);
- end
- except
- Application.HandleException(Self);
- end;
- end;
-
- destructor TPreviousInstance.Destroy;
- begin
- CloseHandle(PreviousInstance.MutexHandle);
- if FDefWndProc <> nil then
- SetWindowLong(Application.Handle, GWL_WNDPROC, LongInt(FDefWndProc));
- {$IFDEF DELPHI_V6}
- Classes.FreeObjectInstance(FNewWndProc);
- {$ELSE}
- FreeObjectInstance(FNewWndProc);
- {$ENDIF}
- inherited;
- end;
-
- procedure TPreviousInstance.NewWndProc(var Message: TMessage);
- begin
- with Message do
- begin
- if Msg = FMessageID then
- begin
- if IsIconic(Application.Handle) then
- begin
- Application.MainForm.WindowState := wsNormal;
- Application.Restore;
- end;
- SetForegroundWindow(Application.Handle);
- end
- else
- Result := CallWindowProc(FDefWndProc, Application.Handle, Msg, WParam, LParam);
- end;
- end;
-
- procedure TPreviousInstance.SethPrevInst;
- begin
- FMessageID := RegisterWindowMessage(PChar(Application.Title));
- FMutexHandle := CreateMutex(nil, TRUE, PChar(Application.Title));
- if MutexHandle <> 0 then
- begin
- if GetLastError = ERROR_ALREADY_EXISTS then
- hPrevInst := True
- else begin
- hPrevInst := False;
- {$IFDEF DELPHI_V6}
- FNewWndProc := Classes.MakeObjectInstance(NewWndProc);
- {$ELSE}
- FNewWndProc := MakeObjectInstance(NewWndProc);
- {$ENDIF}
- FDefWndProc := Pointer(SetWindowLong(Application.Handle, GWL_WNDPROC,
- LongInt(FNewWndProc)));
- end;
- end
- else
- hPrevInst := FALSE;
- end;
-
- function CheckToMultyInstance: boolean;
- type
- TBroadcastSystemMessage = function(Flags: DWORD; Recipients: PDWORD;
- uiMessage: UINT; wParam: WPARAM; lParam: LPARAM): Longint;
- var
- BSMReceptions: DWORD;
- User32Dll: THandle;
- BroadCastSystemMessageAW: TBroadcastSystemMessage;
- begin
- if PreviousInstance.hPrevInst then
- begin
- Application.ShowMainForm := False;
- BSMReceptions := BSM_APPLICATIONS;
- User32Dll := GetModuleHandle(user32);
- if User32Dll <> 0 then
- begin
- {Under Win95 fixed bug with BroadCastSystemMessage}
- if (Win32Platform <> VER_PLATFORM_WIN32_NT) and (Win32MajorVersion <= 4)
- or ((Win32MajorVersion = 4) and (Win32MinorVersion < 10)) then
- @BroadCastSystemMessageAW := GetProcAddress(User32Dll, 'BroadcastSystemMessageW')
- else
- @BroadCastSystemMessageAW := GetProcAddress(User32Dll, 'BroadcastSystemMessageA');
-
- if @BroadCastSystemMessageAW <> nil then
- BroadCastSystemMessageAW(BSF_IGNORECURRENTTASK or BSF_POSTMESSAGE,
- @BSMReceptions, PreviousInstance.MessageID, 0 ,0);
- end;
- Result := True
- end
- else
- Result := False;
- end;
-
- procedure TDCTrayIcon.LoadDefaultIcon;
- begin
- FIcon.Handle := LoadIcon(hInstance, 'MAINICONX16');
- if FIcon.Handle = 0 then
- FIcon.Handle := LoadIcon(0, IDI_WINLOGO);
- end;
-
- function TDCTrayIcon.Win2k: boolean;
- begin
- Result := (Win32MajorVersion > 4) and (Win32Platform = VER_PLATFORM_WIN32_NT);
- end;
-
- procedure TDCTrayIcon.ShowBaloonToolTip(const Info, InfoTitle: string;
- const BaloonType: TBaloonInfoType; const Timeout: TBaloonTimeout);
- const
- aBaloonInfoType: array[TBaloonInfoType] of DWORD =
- (NIIF_NONE, NIIF_INFO, NIIF_WARNING, NIIF_ERROR);
- var
- Ico: TIcon;
- begin
- with FIconData do
- begin
- if Win2k then
- cbSize := SizeOf(TNotifyIconDataEx)
- else
- cbSize := NOTIFYICONDATA_V1_SIZE;
- Wnd := FHandle;
- uFlags := NIF_INFO;
- Ico := GetActiveIcon;
- if Ico <> nil then
- hIcon := Ico.Handle
- else
- hIcon := INVALID_HANDLE_VALUE;
- uID := 0;
- uTimeout := 1000 * Timeout;
-
- {Hide previous tooltip}
- StrPCopy(szInfoTitle, '');
- StrPCopy(szInfo, '');
- Shell_NotifyIcon(NIM_MODIFY, @FIconData);
-
- StrPCopy(szInfoTitle, InfoTitle);
- StrPCopy(szInfo, Info);
- dwInfoFlags := aBaloonInfoType[BaloonType];
- Shell_NotifyIcon(NIM_MODIFY, @FIconData);
- end;
- end;
-
- initialization
- PreviousInstance := TPreviousInstance.Create;
- PreviousInstance.SethPrevInst;
-
- finalization
- PreviousInstance.Free;
-
- end.
-