Subversion Repositories delphiutils

Rev

Rev 58 | Go to most recent revision | 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. // WM_UNINITMENUPOPUP is not implemented in Win 95.
  11. {.$DEFINE USE_WM_UNINITMENUPOPUP}
  12.  
  13. interface
  14.  
  15. // TODO: DefWindowProc() verwenden?
  16.  
  17. uses
  18.   Windows, WindowsCompat, Classes, SysUtils;
  19.  
  20. type
  21.   TWndProcIntercept = class(TObject)
  22.   private
  23.     FHandle: HWnd;
  24.     FPrevWndProc: LONG_PTR;
  25.     MsgProcPointer: Pointer;
  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;
  32.   public
  33.     constructor Create(AHandle: Hwnd);
  34.     destructor Destroy; override;
  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;
  44.   published
  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
  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.  
  68. { TWndProcIntercept }
  69.  
  70. constructor TWndProcIntercept.Create(AHandle: Hwnd);
  71. begin
  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
  87.   result := Windows.CallWindowProc(WindowsCompat.WNDPROC(FPrevWndProc), Handle, Msg, WParam, LParam)
  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;
  108.   MsgProcPointer := MethodPtr.MakeProcInstance(f);
  109.  
  110.   // Problem: Kann es zu Komplikationen mit mehreren msg handlern kommen?
  111.   // (Beim vermischten register+unregister !)
  112.  
  113.   SetWindowLongPtr(FHandle, GWLP_WNDPROC, LONG_PTR(MsgProcPointer));
  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
  131.   if Msg = WM_INITMENUPOPUP then
  132.   begin
  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
  155.   {$IFDEF USE_WM_UNINITMENUPOPUP}
  156.   if Msg = WM_UNINITMENUPOPUP then
  157.   {$ELSE}
  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
  163.   {$ENDIF}
  164.   begin
  165.     {$IFDEF USE_WM_UNINITMENUPOPUP}
  166.     // if Cardinal(WParam) = GetSystemMenu(FHandle, False) then
  167.     if HiWord(lParam) = MF_SYSMENU then
  168.     begin
  169.     {$ENDIF}
  170.       FSystemMenuOpened := false;
  171.       if Assigned(FOnSystemMenuClose) then
  172.       begin
  173.         FOnSystemMenuClose(Self);
  174.       end;
  175.     {$IFDEF USE_WM_UNINITMENUPOPUP}
  176.     end;
  177.     {$ENDIF}
  178.   end;
  179.  
  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.  
  194.   result := inherited MsgProc(Handle, Msg, WParam, LParam);
  195. end;
  196.  
  197. end.
  198.