Search code examples
windowsdelphidelphi-10.4-sydneymouse-hook

How to prevent mouse from turning on monitor?


The following code is able to lock mouse events successfully. This was my attempt to prevent the mouse from turning on the monitor when it is off, but the monitor always turns on when executing any action with the mouse, ex: left button click.

Is there some solution to this?

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  MouseHook: HHOOK;

implementation

{$R *.dfm}

function LowLevelMouseProc(nCode: Integer; WParam: WParam; LParam: LParam): LRESULT; stdcall;
begin
  Result := 1{CallNextHookEx(MouseHook, nCode, WParam, LParam)};
  case WParam of
    WM_LBUTTONDOWN:
      Form1.Memo1.Lines.Add('Mouse Left Button Down');
    WM_LBUTTONUP:
      Form1.Memo1.Lines.Add('Mouse Left Button Up');
    WM_LBUTTONDBLCLK:
      Form1.Memo1.Lines.Add('Mouse Left Button Double Click');
    WM_RBUTTONDOWN:
      Form1.Memo1.Lines.Add('Mouse Right Button Down');
    WM_RBUTTONUP:
      Form1.Memo1.Lines.Add('Mouse Right Button Up');
    WM_RBUTTONDBLCLK:
      Form1.Memo1.Lines.Add('Mouse Right Button Double Click');
    WM_MBUTTONDOWN:
      Form1.Memo1.Lines.Add('Mouse Middle Button Down');
    WM_MBUTTONUP:
      Form1.Memo1.Lines.Add('Mouse Middle Button Up');
    WM_MBUTTONDBLCLK:
      Form1.Memo1.Lines.Add('Mouse Middle Button Double Click');
    WM_MOUSEMOVE:
      Form1.Memo1.Lines.Add('Mouse Move');
    WM_MOUSEWHEEL:
      Form1.Memo1.Lines.Add('Mouse Wheel');
  else
    Form1.Memo1.Lines.Add('Unknown Event');
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
const
  MONITOR_ON = -1;
  MONITOR_OFF = 2;
  MONITOR_STANDBY = 1;
begin
  MouseHook := SetWindowsHookEx(WH_MOUSE_LL, @LowLevelMouseProc, HInstance, 0);
  SendMessage(Application.Handle, WM_SYSCOMMAND, SC_MONITORPOWER, MONITOR_OFF);
  // SendMessage(Application.Handle, WM_SYSCOMMAND, SC_MONITORPOWER, MONITOR_ON);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  UnhookWindowsHookEx(MouseHook);
end;

Edit:

I need only turn off monitor without turn off system (hibernate or suspend). And prevent mouse actions revert it.


Solution

  • After analyze and test @AmigoJack's suggestion, seems that disable the device (like is made using Windows Device Manager) is a possible solution to me, even if some device cannot be disabled. I'm leaving the code to help future readers with this same doubt.

    DeviceCtrl

    unit DeviceCtrl;
    
    interface
    
    uses
      Classes, SysUtils, Windows;
    
    const
      GUID_DEVCLASS_1394: TGUID = '{6BDD1FC1-810F-11D0-BEC7-08002BE2092F}';
      GUID_DEVCLASS_ADAPTER: TGUID = '{4D36E964-E325-11CE-BFC1-08002BE10318}';
      GUID_DEVCLASS_APMSUPPORT: TGUID = '{D45B1C18-C8FA-11D1-9F77-0000F805F530}';
      GUID_DEVCLASS_BATTERY: TGUID = '{72631E54-78A4-11D0-BCF7-00AA00B7B32A}';
      GUID_DEVCLASS_CDROM: TGUID = '{4D36E965-E325-11CE-BFC1-08002BE10318}';
      GUID_DEVCLASS_COMPUTER: TGUID = '{4D36E966-E325-11CE-BFC1-08002BE10318}';
      GUID_DEVCLASS_DECODER: TGUID = '{6BDD1FC2-810F-11D0-BEC7-08002BE2092F}';
      GUID_DEVCLASS_DISKDRIVE: TGUID = '{4D36E967-E325-11CE-BFC1-08002BE10318}';
      GUID_DEVCLASS_DISPLAY: TGUID = '{4D36E968-E325-11CE-BFC1-08002BE10318}';
      GUID_DEVCLASS_FDC: TGUID = '{4D36E969-E325-11CE-BFC1-08002BE10318}';
      GUID_DEVCLASS_FLOPPYDISK: TGUID = '{4D36E980-E325-11CE-BFC1-08002BE10318}';
      GUID_DEVCLASS_GPS: TGUID = '{6BDD1FC3-810F-11D0-BEC7-08002BE2092F}';
      GUID_DEVCLASS_HDC: TGUID = '{4D36E96A-E325-11CE-BFC1-08002BE10318}';
      GUID_DEVCLASS_HIDCLASS: TGUID = '{745A17A0-74D3-11D0-B6FE-00A0C90F57DA}';
      GUID_DEVCLASS_IMAGE: TGUID = '{6BDD1FC6-810F-11D0-BEC7-08002BE2092F}';
      GUID_DEVCLASS_INFRARED: TGUID = '{6BDD1FC5-810F-11D0-BEC7-08002BE2092F}';
      GUID_DEVCLASS_KEYBOARD: TGUID = '{4D36E96B-E325-11CE-BFC1-08002BE10318}';
      GUID_DEVCLASS_LEGACYDRIVER: TGUID = '{8ECC055D-047F-11D1-A537-0000F8753ED1}';
      GUID_DEVCLASS_MEDIA: TGUID = '{4D36E96C-E325-11CE-BFC1-08002BE10318}';
      GUID_DEVCLASS_MEDIUM_CHANGER: TGUID = '{CE5939AE-EBDE-11D0-B181-0000F8753EC4}';
      GUID_DEVCLASS_MODEM: TGUID = '{4D36E96D-E325-11CE-BFC1-08002BE10318}';
      GUID_DEVCLASS_MONITOR: TGUID = '{4D36E96E-E325-11CE-BFC1-08002BE10318}';
      GUID_DEVCLASS_MOUSE: TGUID = '{4D36E96F-E325-11CE-BFC1-08002BE10318}';
      GUID_DEVCLASS_MTD: TGUID = '{4D36E970-E325-11CE-BFC1-08002BE10318}';
      GUID_DEVCLASS_MULTIFUNCTION: TGUID = '{4D36E971-E325-11CE-BFC1-08002BE10318}';
      GUID_DEVCLASS_MULTIPORTSERIAL: TGUID = '{50906CB8-BA12-11D1-BF5D-0000F805F530}';
      GUID_DEVCLASS_NET: TGUID = '{4D36E972-E325-11CE-BFC1-08002BE10318}';
      GUID_DEVCLASS_NETCLIENT: TGUID = '{4D36E973-E325-11CE-BFC1-08002BE10318}';
      GUID_DEVCLASS_NETSERVICE: TGUID = '{4D36E974-E325-11CE-BFC1-08002BE10318}';
      GUID_DEVCLASS_NETTRANS: TGUID = '{4D36E975-E325-11CE-BFC1-08002BE10318}';
      GUID_DEVCLASS_NODRIVER: TGUID = '{4D36E976-E325-11CE-BFC1-08002BE10318}';
      GUID_DEVCLASS_PCMCIA: TGUID = '{4D36E977-E325-11CE-BFC1-08002BE10318}';
      GUID_DEVCLASS_PORTS: TGUID = '{4D36E978-E325-11CE-BFC1-08002BE10318}';
      GUID_DEVCLASS_PRINTER: TGUID = '{4D36E979-E325-11CE-BFC1-08002BE10318}';
      GUID_DEVCLASS_PRINTERUPGRADE: TGUID = '{4D36E97A-E325-11CE-BFC1-08002BE10318}';
      GUID_DEVCLASS_SCSIADAPTER: TGUID = '{4D36E97B-E325-11CE-BFC1-08002BE10318}';
      GUID_DEVCLASS_SMARTCARDREADER: TGUID = '{50DD5230-BA8A-11D1-BF5D-0000F805F530}';
      GUID_DEVCLASS_SOUND: TGUID = '{4D36E97C-E325-11CE-BFC1-08002BE10318}';
      GUID_DEVCLASS_SYSTEM: TGUID = '{4D36E97D-E325-11CE-BFC1-08002BE10318}';
      GUID_DEVCLASS_TAPEDRIVE: TGUID = '{6D807884-7D21-11CF-801C-08002BE10318}';
      GUID_DEVCLASS_UNKNOWN: TGUID = '{4D36E97E-E325-11CE-BFC1-08002BE10318}';
      GUID_DEVCLASS_USB: TGUID = '{36FC9E60-C465-11CF-8056-444553540000}';
      GUID_DEVCLASS_VOLUME: TGUID = '{71A27CDD-812A-11D0-BEC7-08002BE2092F}';
    
    type
      TDeviceControlResult = (DCROK, DCRErrEnumDevIceInfo, DCRErrSetClassInstallParams, DCRErrDIF_PROPERTYCHANGE);
    
    function LoadDevices(GUID_DevClass: TGUID): TStringList;
    
    function EnableDevice(SelectedItem: DWORD): TDeviceControlResult;
    
    function DisableDevice(SelectedItem: DWORD): TDeviceControlResult;
    
    implementation
    
    const
      DIF_PROPERTYCHANGE = $00000012;
      DICS_ENABLE = $00000001;
      DICS_DISABLE = $00000002;
      DICS_FLAG_GLOBAL = $00000001;
      DIGCF_PRESENT = $00000002;
      SPDRP_DEVICEDESC = $00000000;
      SPDRP_CLASS = $00000007;
      SPDRP_CLASSGUID = $00000008;
      SPDRP_FRIENDLYNAME = $0000000C;
    
    type
      HDEVINFO = Pointer;
    
      DI_FUNCTION = LongWord;
    
      PSPClassInstallHeader = ^TSPClassInstallHeader;
    
      SP_CLASSINSTALL_HEADER = packed record
        cbSize: DWORD;
        InstallFunction: DI_FUNCTION;
      end;
    
      TSPClassInstallHeader = SP_CLASSINSTALL_HEADER;
    
      PSPPropChangeParams = ^TSPPropChangeParams;
    
      SP_PROPCHANGE_PARAMS = packed record
        ClassInstallHeader: TSPClassInstallHeader;
        StateChange: DWORD;
        Scope: DWORD;
        HwProfile: DWORD;
      end;
    
      TSPPropChangeParams = SP_PROPCHANGE_PARAMS;
    
      PSPDevInfoData = ^TSPDevInfoData;
    
      SP_DEVINFO_DATA = packed record
        cbSize: DWORD;
        ClassGuid: TGUID;
        DevInst: DWORD;
        Reserved: ULONG_PTR;
      end;
    
      TSPDevInfoData = SP_DEVINFO_DATA;
    
      TSetupDiEnumDeviceInfo = function(DeviceInfoSet: HDEVINFO; MemberIndex: DWORD; var DeviceInfoData: TSPDevInfoData): LongBool; stdcall;
    
      TSetupDiSetClassInstallParamsA = function(DeviceInfoSet: HDEVINFO; DeviceInfoData: PSPDevInfoData; ClassInstallParams: PSPClassInstallHeader; ClassInstallParamsSize: DWORD): LongBool; stdcall;
    
      TSetupDiSetClassInstallParamsW = function(DeviceInfoSet: HDEVINFO; DeviceInfoData: PSPDevInfoData; ClassInstallParams: PSPClassInstallHeader; ClassInstallParamsSize: DWORD): LongBool; stdcall;
    
      TSetupDiSetClassInstallParams = TSetupDiSetClassInstallParamsA;
    
      TSetupDiCallClassInstaller = function(InstallFunction: DI_FUNCTION; DeviceInfoSet: HDEVINFO; DeviceInfoData: PSPDevInfoData): LongBool; stdcall;
    
      TSetupDiGetClassDevs = function(ClassGuid: PGUID; const Enumerator: PAnsiChar; hwndParent: HWND; Flags: DWORD): HDEVINFO; stdcall;
    
      TSetupDiGetDeviceRegistryPropertyA = function(DeviceInfoSet: HDEVINFO; const DeviceInfoData: TSPDevInfoData; Property_: DWORD; var PropertyRegDataType: DWORD; PropertyBuffer: PBYTE; PropertyBufferSIze: DWORD; var RequiredSize: DWORD): BOOL; stdcall;
    
      TSetupDiGetDeviceRegistryPropertyW = function(DeviceInfoSet: HDEVINFO; const DeviceInfoData: TSPDevInfoData; Property_: DWORD; var PropertyRegDataType: DWORD; PropertyBuffer: PBYTE; PropertyBufferSIze: DWORD; var RequiredSize: DWORD): BOOL; stdcall;
    
      TSetupDiGetDeviceRegistryProperty = TSetupDiGetDeviceRegistryPropertyA;
    
    var
      DevInfo: hDevInfo;
      SetupDiEnumDeviceInfo: TSetupDiEnumDeviceInfo;
      SetupDiSetClassInstallParams: TSetupDiSetClassInstallParams;
      SetupDiCallClassInstaller: TSetupDiCallClassInstaller;
      SetupDiGetClassDevs: TSetupDiGetClassDevs;
      SetupDiGetDeviceRegistryProperty: TSetupDiGetDeviceRegistryProperty;
    
    var
      SetupApiLoadCount: Integer = 0;
    
    function LoadSetupApi: Boolean;
    var
      SetupApiLib: System.THandle;
    begin
      Result := True;
      Inc(SetupApiLoadCount);
      if SetupApiLoadCount > 1 then
        Exit;
      SetupApiLib := LoadLibrary('SetupApi.dll');
      Result := SetupApiLib <> 0;
      if Result then
      begin
        SetupDiEnumDeviceInfo := Windows.GetProcAddress(SetupApiLib, 'SetupDiEnumDeviceInfo');
        SetupDiSetClassInstallParams := Windows.GetProcAddress(SetupApiLib, 'SetupDiSetClassInstallParamsA');
        SetupDiCallClassInstaller := Windows.GetProcAddress(SetupApiLib, 'SetupDiCallClassInstaller');
        SetupDiGetClassDevs := Windows.GetProcAddress(SetupApiLib, 'SetupDiGetClassDevsA');
        SetupDiGetDeviceRegistryProperty := Windows.GetProcAddress(SetupApiLib, 'SetupDiGetDeviceRegistryPropertyA');
      end;
    end;
    
    function StateChange(NewState, SelectedItem: DWORD; hDevInfo: hDevInfo): TDeviceControlResult;
    var
      PropChangeParams: TSPPropChangeParams;
      DeviceInfoData: TSPDevInfoData;
    begin
      PropChangeParams.ClassInstallHeader.cbSize := SizeOf(TSPClassInstallHeader);
      DeviceInfoData.cbSize := SizeOf(TSPDevInfoData);
    
      if (not SetupDiEnumDeviceInfo(hDevInfo, SelectedItem, DeviceInfoData)) then
      begin
        Result := DCRErrEnumDevIceInfo;
        Exit;
      end;
    
      PropChangeParams.ClassInstallHeader.InstallFunction := DIF_PROPERTYCHANGE;
      PropChangeParams.Scope := DICS_FLAG_GLOBAL;
      PropChangeParams.StateChange := NewState;
      if (not SetupDiSetClassInstallParams(hDevInfo, @DeviceInfoData, PSPClassInstallHeader(@PropChangeParams), SizeOf(PropChangeParams))) then
      begin
        Result := DCRErrSetClassInstallParams;
        Exit;
      end;
    
      if (not SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, hDevInfo, @DeviceInfoData)) then
      begin
        Result := DCRErrDIF_PROPERTYCHANGE;
        Exit;
      end;
      Result := DCROK;
    end;
    
    function GetRegistryProperty(PnPHandle: hDevInfo; DevData: TSPDevInfoData; Prop: DWORD; Buffer: PChar; dwLength: DWORD): Boolean;
    var
      aBuffer: array[0..256] of Char;
    begin
      dwLength := 0;
      aBuffer[0] := #0;
      SetupDiGetDeviceRegistryProperty(PnPHandle, DevData, Prop, Prop, PBYTE(@aBuffer[0]), SizeOf(aBuffer), dwLength);
      StrCopy(Buffer, aBuffer);
      Result := Buffer^ <> #0;
    end;
    
    function ConstructDeviceName(DeviceInfoSet: hDevInfo; DeviceInfoData: TSPDevInfoData; Buffer: PChar; dwLength: DWORD): Boolean;
    const
      UnknownDevice = '<Unknown DevIce>';
    begin
      if (not GetRegistryProperty(DeviceInfoSet, DeviceInfoData, SPDRP_FRIENDLYNAME, Buffer, dwLength)) then
      begin
        if (not GetRegistryProperty(DeviceInfoSet, DeviceInfoData, SPDRP_DEVICEDESC, Buffer, dwLength)) then
        begin
          if (not GetRegistryProperty(DeviceInfoSet, DeviceInfoData, SPDRP_CLASS, Buffer, dwLength)) then
          begin
            if (not GetRegistryProperty(DeviceInfoSet, DeviceInfoData, SPDRP_CLASSGUID, Buffer, dwLength)) then
            begin
              dwLength := DWORD(SizeOf(UnknownDevice));
              Buffer := Pointer(LocalAlloc(LPTR, Cardinal(dwLength)));
              StrCopy(Buffer, UnknownDevice);
            end;
          end;
        end;
      end;
      Result := True;
    end;
    
    function LoadDevices(GUID_DevClass: TGUID): TStringList;
    var
      DeviceInfoData: TSPDevInfoData;
      I: DWORD;
      pszText: PChar;
    begin
      if (not LoadSetupApi) then
      begin
        Result := nil;
        Exit;
      end;
      DevInfo := nil;
    
      DevInfo := SetupDiGetClassDevs(@GUID_DevClass, nil, 0, DIGCF_PRESENT);
      if (DevInfo = Pointer(INVALID_HANDLE_VALUE)) then
      begin
        Result := nil;
        Exit;
      end;
      Result := TStringList.Create;
      DeviceInfoData.cbSize := SizeOf(TSPDevInfoData);
      I := 0;
    
      while SetupDiEnumDeviceInfo(DevInfo, I, DeviceInfoData) do
      begin
        GetMem(pszText, 256);
        try
          ConstructDeviceName(DevInfo, DeviceInfoData, pszText, DWORD(nil));
          Result.AddObject(string(PAnsiChar(pszText)), Tobject(I));
        finally
          FreeMem(pszText);
        end;
        Inc(I);
      end;
    end;
    
    function EnableDevice(SelectedItem: DWORD): TDeviceControlResult;
    begin
      Result := StateChange(DICS_ENABLE, SelectedItem, DevInfo);
    end;
    
    function DisableDevice(SelectedItem: DWORD): TDeviceControlResult;
    begin
      Result := StateChange(DICS_DISABLE, SelectedItem, DevInfo);
    end;
    
    end.
    

    DeviceTest

    program DeviceTest;
    
    {$APPTYPE CONSOLE}
    {$R *.res}
    
    uses
      Classes,
      DeviceCtrl;
    
    var
      sl: TStringList;
      I: Integer;
    
    begin
      sl := LoadDevices(GUID_DEVCLASS_MOUSE);
      try
        for I := 0 to sl.count - 1 do
        begin
          Writeln(I, ' : ', sl[I]);
          if DisableDevice(I) = DCROK then
            Writeln(sl[I], ' disabled');
          if EnableDevice(I) = DCROK then
            Writeln(sl[I], ' enabled');
        end;
      finally
        sl.Free;
      end;
      Readln;
    end.