Subversion Repositories delphiutils

Rev

Rev 59 | Blame | Compare with Previous | Last modification | View Log | RSS feed

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