How to detect modifier key change in a control which doesn't have focus?

后端 未结 4 1435
故里飘歌
故里飘歌 2020-12-31 07:18

Background:

I\'m working on a control derived from TCustomControl class which can get focus and which has some inner elements inside. Those inner elem

4条回答
  •  一整个雨季
    2020-12-31 08:00

    I can't tell if it would be less overkill than using a hook, but one option would be to use "raw input". If you register your control accordingly, it will receive input also when it's not active. Sample implementation to decide..:

    type
      TMyCustomControl = class(TCustomControl)
        ..
      protected
        ..
        procedure CreateWindowHandle(const Params: TCreateParams); override;
        procedure WMInput(var Message: TMessage); message WM_INPUT;
      ..
      end;
    
    uses
      types;
    
    type
      tagRAWINPUTDEVICE = record
        usUsagePage: USHORT;
        usUsage: USHORT;
        dwFlags: DWORD;
        hwndTarget: HWND;
      end;
      RAWINPUTDEVICE = tagRAWINPUTDEVICE;
      TRawInputDevice = RAWINPUTDEVICE;
      PRawInputDevice = ^TRawInputDevice;
      LPRAWINPUTDEVICE = PRawInputDevice;
      PCRAWINPUTDEVICE = PRawInputDevice;
    
    function RegisterRawInputDevices(
      pRawInputDevices: PCRAWINPUTDEVICE;
      uiNumDevices: UINT;
      cbSize: UINT): BOOL; stdcall; external user32;
    
    const
      GenericDesktopControls: USHORT = 01;
      Keyboard: USHORT = 06;
      RIDEV_INPUTSINK = $00000100;
    
    procedure TMyCustomControl.CreateWindowHandle(const Params: TCreateParams);
    var
      RID: TRawInputDevice;
    begin
      inherited;
    
      RID.usUsagePage := GenericDesktopControls;
      RID.usUsage := Keyboard;
      RID.dwFlags := RIDEV_INPUTSINK;
      RID.hwndTarget := Handle;
      Win32Check(RegisterRawInputDevices(@RID, 1, SizeOf(RID)));
    end;
    
    type
      HRAWINPUT = THandle;
    
    function GetRawInputData(
      hRawInput: HRAWINPUT;
      uiCommand: UINT;
      pData: LPVOID;
      var pcbSize: UINT;
      cbSizeHeader: UINT): UINT; stdcall; external user32;
    
    type
      tagRAWINPUTHEADER = record
        dwType: DWORD;
        dwSize: DWORD;
        hDevice: THandle;
        wParam: WPARAM;
      end;
      RAWINPUTHEADER = tagRAWINPUTHEADER;
      TRawInputHeader = RAWINPUTHEADER;
      PRawInputHeader = ^TRawInputHeader;
    
      tagRAWKEYBOARD = record
        MakeCode: USHORT;
        Flags: USHORT;
        Reserved: USHORT;
        VKey: USHORT;
        Message: UINT;
        ExtraInformation: ULONG;
      end;
      RAWKEYBOARD = tagRAWKEYBOARD;
      TRawKeyboard = RAWKEYBOARD;
      PRawKeyboard = ^TRawKeyboard;
      LPRAWKEYBOARD = PRawKeyboard;
    
    //- !!! bogus declaration below, see winuser.h for the correct one
      tagRAWINPUT = record
        header: TRawInputHeader;
        keyboard: TRawKeyboard;
      end;
    //-
      RAWINPUT = tagRAWINPUT;
      TRawInput = RAWINPUT;
      PRawInput = ^TRawInput;
      LPRAWINPUT = PRawInput;
    
    const
      RIM_INPUT = 0;
      RIM_INPUTSINK = 1;
      RID_INPUT = $10000003;
      RIM_TYPEKEYBOARD = 1;
      RI_KEY_MAKE = 0;
      RI_KEY_BREAK = 1;
    
    procedure TMyCustomControl.WMInput(var Message: TMessage);
    var
      Size: UINT;
      Data: array of Byte;
      RawKeyboard: TRawKeyboard;
    begin
      if (Message.WParam and $FF) in [RIM_INPUT, RIM_INPUTSINK] then
        inherited;
    
      if not Focused and
          (WindowFromPoint(SmallPointToPoint(SmallPoint(GetMessagePos))) = Handle) and
          (GetRawInputData(Message.LParam, RID_INPUT, nil, Size,
          SizeOf(TRawInputHeader)) = 0) then begin
        SetLength(Data, Size);
        if (GetRawInputData(Message.LParam, RID_INPUT, Data, Size,
            SizeOf(TRawInputHeader)) <> UINT(-1)) and
            (PRawInput(Data)^.header.dwType = RIM_TYPEKEYBOARD) then begin
          RawKeyboard := PRawInput(Data)^.keyboard;
    
          if (RawKeyboard.VKey = VK_CONTROL) then begin
            if RawKeyboard.Flags and RI_KEY_BREAK = RI_KEY_BREAK then
              Cursor := crDefault
            else
              Cursor := crSizeAll; // will call continously until key is released
          end;
          // might opt to reset the cursor regardless of pointer position...
    
    
          if (RawKeyboard.VKey = VK_MENU) then begin
            ....
          end;
    
        end;
    
      end;
    end;
    

提交回复
热议问题