Subversion Repositories delphiutils

Rev

Rev 58 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
41 daniel-mar 1
unit QuerySystemMenu;
2
 
3
(*
4
 
5
QuerySystemMenu.pas
58 daniel-mar 6
(C) 2010 - 2011 Daniel Marschall
41 daniel-mar 7
 
8
*)
9
 
58 daniel-mar 10
// WM_UNINITMENUPOPUP is not implemented in Win 95.
11
{.$DEFINE USE_WM_UNINITMENUPOPUP}
12
 
41 daniel-mar 13
interface
14
 
42 daniel-mar 15
// TODO: DefWindowProc() verwenden?
16
 
41 daniel-mar 17
uses
42 daniel-mar 18
  Windows, WindowsCompat, Classes, SysUtils;
41 daniel-mar 19
 
20
type
42 daniel-mar 21
  TWndProcIntercept = class(TObject)
41 daniel-mar 22
  private
23
    FHandle: HWnd;
24
    FPrevWndProc: LONG_PTR;
25
    MsgProcPointer: Pointer;
42 daniel-mar 26
    FIsRegistered: boolean;
27
    function MsgProcVirtualCall(Handle: HWnd; Msg: UInt; WParam: Windows.WParam; LParam: Windows.LParam): LResult; stdcall;
28
    procedure RegisterCB;
29
    procedure UnregisterCB;
30
  protected
31
    function MsgProc(Handle: HWnd; Msg: UInt; WParam: Windows.WParam; LParam: Windows.LParam): LResult; virtual; stdcall;
41 daniel-mar 32
  public
33
    constructor Create(AHandle: Hwnd);
34
    destructor Destroy; override;
42 daniel-mar 35
  end;
36
 
37
  TQueryMenu = class(TWndProcIntercept)
38
  private
39
    FOnMenuOpen: TNotifyEvent;
40
    FOnMenuClose: TNotifyEvent;
41
    FMenuOpened: boolean;
42
  protected
43
    function MsgProc(Handle: HWnd; Msg: UInt; WParam: Windows.WParam; LParam: Windows.LParam): LResult; override; stdcall;
41 daniel-mar 44
  published
42 daniel-mar 45
    property IsMenuOpened: boolean read FMenuOpened;
46
    property OnMenuOpen: TNotifyEvent read FOnMenuOpen write FOnMenuOpen;
47
    property OnMenuClose: TNotifyEvent read FOnMenuClose write FOnMenuClose;
48
  end;
49
 
50
  TQuerySystemMenu = class(TWndProcIntercept)
51
  private
52
    FOnSystemMenuOpen: TNotifyEvent;
53
    FOnSystemMenuClose: TNotifyEvent;
54
    FSystemMenuOpened: boolean;
55
  protected
56
    function MsgProc(Handle: HWnd; Msg: UInt; WParam: Windows.WParam; LParam: Windows.LParam): LResult; override; stdcall;
57
  published
41 daniel-mar 58
    property IsSystemMenuOpened: boolean read FSystemMenuOpened;
59
    property OnSystemMenuOpen: TNotifyEvent read FOnSystemMenuOpen write FOnSystemMenuOpen;
60
    property OnSystemMenuClose: TNotifyEvent read FOnSystemMenuClose write FOnSystemMenuClose;
61
  end;
62
 
63
implementation
64
 
65
uses
66
  Messages, MethodPtr;
67
 
42 daniel-mar 68
{ TWndProcIntercept }
69
 
70
constructor TWndProcIntercept.Create(AHandle: Hwnd);
41 daniel-mar 71
begin
42 daniel-mar 72
  FHandle := AHandle;
73
 
74
  RegisterCB;
75
end;
76
 
77
destructor TWndProcIntercept.Destroy;
78
begin
79
  UnregisterCB;
80
 
81
  inherited;
82
end;
83
 
84
function TWndProcIntercept.MsgProc(Handle: HWnd; Msg: UInt;
85
  WParam: Windows.WParam; LParam: Windows.LParam): LResult;
86
begin
46 daniel-mar 87
  result := Windows.CallWindowProc(WindowsCompat.WNDPROC(FPrevWndProc), Handle, Msg, WParam, LParam)
42 daniel-mar 88
end;
89
 
90
function TWndProcIntercept.MsgProcVirtualCall(Handle: HWnd; Msg: UInt;
91
  WParam: Windows.WParam; LParam: Windows.LParam): LResult;
92
begin
93
  // Virtual call
94
  result := MsgProc(Handle, Msg, WParam, LParam);
95
end;
96
 
97
procedure TWndProcIntercept.RegisterCB;
98
var
99
  f: TMethod;
100
begin
101
  if FIsRegistered then exit;
102
  FIsRegistered := true;
103
 
104
  FPrevWndProc := GetWindowLongPtr(FHandle, GWLP_WNDPROC);
105
 
106
  f.Code := @TWndProcIntercept.MsgProcVirtualCall;
107
  f.Data := Self;
46 daniel-mar 108
  MsgProcPointer := MethodPtr.MakeProcInstance(f);
42 daniel-mar 109
 
110
  // Problem: Kann es zu Komplikationen mit mehreren msg handlern kommen?
111
  // (Beim vermischten register+unregister !)
43 daniel-mar 112
 
113
  SetWindowLongPtr(FHandle, GWLP_WNDPROC, LONG_PTR(MsgProcPointer));
42 daniel-mar 114
end;
115
 
116
procedure TWndProcIntercept.UnregisterCB;
117
begin
118
  if not FIsRegistered then exit;
119
  FIsRegistered := false;
120
 
121
  SetWindowLongPtr(FHandle, GWLP_WNDPROC, FPrevWndProc);
122
 
123
  FreeProcInstance(MsgProcPointer);
124
end;
125
 
126
{ TQueryMenu }
127
 
128
function TQueryMenu.MsgProc(Handle: HWnd; Msg: UInt; WParam: Windows.WParam;
129
  LParam: Windows.LParam): LResult;
130
begin
41 daniel-mar 131
  if Msg = WM_INITMENUPOPUP then
132
  begin
42 daniel-mar 133
    FMenuOpened := true;
134
    if Assigned(FOnMenuOpen) then
135
    begin
136
      FOnMenuOpen(Self);
137
    end;
138
  end;
139
  if Msg = WM_UNINITMENUPOPUP then
140
  begin
141
    FMenuOpened := false;
142
    if Assigned(FOnMenuClose) then
143
    begin
144
      FOnMenuClose(Self);
145
    end;
146
  end;
147
 
148
  result := inherited MsgProc(Handle, Msg, WParam, LParam);
149
end;
150
 
151
{ TQuerySystemMenu }
152
 
153
function TQuerySystemMenu.MsgProc(Handle: HWnd; Msg: UInt; WParam: Windows.WParam; LParam: Windows.LParam): LResult;
154
begin
58 daniel-mar 155
  {$IFDEF USE_WM_UNINITMENUPOPUP}
41 daniel-mar 156
  if Msg = WM_UNINITMENUPOPUP then
58 daniel-mar 157
  {$ELSE}
59 daniel-mar 158
  // WM_INITMENUPOPUP wird benötigt, falls man z.B. direkt in das
159
  // MainMenu mit einem Klick wechselt.
160
  // TODO: Problem, wenn das System-Menu Untermenü-Punkte besitzt?
161
  if FSystemMenuOpened and ((Msg = WM_EXITMENULOOP)
162
    or (Msg = WM_INITMENUPOPUP)) then
58 daniel-mar 163
  {$ENDIF}
41 daniel-mar 164
  begin
58 daniel-mar 165
    {$IFDEF USE_WM_UNINITMENUPOPUP}
41 daniel-mar 166
    // if Cardinal(WParam) = GetSystemMenu(FHandle, False) then
167
    if HiWord(lParam) = MF_SYSMENU then
168
    begin
58 daniel-mar 169
    {$ENDIF}
41 daniel-mar 170
      FSystemMenuOpened := false;
171
      if Assigned(FOnSystemMenuClose) then
172
      begin
173
        FOnSystemMenuClose(Self);
174
      end;
58 daniel-mar 175
    {$IFDEF USE_WM_UNINITMENUPOPUP}
41 daniel-mar 176
    end;
58 daniel-mar 177
    {$ENDIF}
41 daniel-mar 178
  end;
179
 
59 daniel-mar 180
  // TODO bug: löst bei evtl vorhandenen submenus öfters aus
181
  if Msg = WM_INITMENUPOPUP then
182
  begin
183
    // if Cardinal(WParam) = GetSystemMenu(FHandle, False) then
184
    if LongBool(HiWord(lParam)) then
185
    begin
186
      FSystemMenuOpened := true;
187
      if Assigned(FOnSystemMenuOpen) then
188
      begin
189
        FOnSystemMenuOpen(Self);
190
      end;
191
    end;
192
  end;
193
 
42 daniel-mar 194
  result := inherited MsgProc(Handle, Msg, WParam, LParam);
41 daniel-mar 195
end;
196
 
197
end.