1

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.

FLASHCODER
  • 1
  • 7
  • 24
  • Have a look at [`DevicePowerSetDeviceState()`](https://learn.microsoft.com/en-us/windows/win32/api/powrprof/nf-powrprof-devicepowersetdevicestate): "*The DevicePowerSetDeviceState function enables or disables a specified device from waking the system from a sleep state.*" See [Device Power Management](https://learn.microsoft.com/en-us/windows/win32/power/device-power-management) for more details. – Remy Lebeau Jan 22 '22 at 19:11
  • 1
    @RemyLebeau The function you recommend is used to control which device can wake computer from sleep state but OP is interested only in powering down the monitor. – SilverWarior Jan 22 '22 at 20:00
  • The most likely reason why this doesn't work is because you are initializing the Mouse Hook at the application level. So before you Hook is able to react to the mouse messages windows has already done some processing and thus turn the monitor on. You will have to create a system-wide hook like being described in [this article](http://delphi-kb.blogspot.com/2008/09/how-to-create-system-wide-windows-hook.html). No the said article is quite old so I'm not sure its code will work on 64 bit Windows. – SilverWarior Jan 22 '22 at 20:24
  • Also if I remember correctly windows has specific settings that require you to press a key on keyboard in order to be able to leave screensaver or turn on the monitor after it has been turned off due to inactivity but I don't remember where this setting is located. – SilverWarior Jan 22 '22 at 20:26
  • 1
    Wouldn't it be (unbound to permissions) a better approach to [disable all mouse _devices_](https://stackoverflow.com/a/1610140/4299358) (both attached and built-in, like touchpads), to later enable them again? – AmigoJack Jan 23 '22 at 01:27
  • @AmigoJack I would not recommend the path of disabling the devices. Why? Let us say that you do disable all mouse devices. In order to allow user later use computer normally you need to enable all of those devices again. But what happens if your application crashes before it enables all of disabled devices or if there is a power outage? You have just created a scenario where all those devices stay disabled even when your application isn't running. I bet one of the users would be happy encounter that and would also be pretty quick in uninstalling your application from the computer. – SilverWarior Jan 23 '22 at 19:08
  • ...or they would thank me by realizing how unusable their OS has become if it can't be controlled by keyboard to reach the device manager and enable devices. (Of course that's a joke: those few people being aware/able will probably never need the software described here.) – AmigoJack Jan 23 '22 at 21:10

1 Answers1

1

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.
FLASHCODER
  • 1
  • 7
  • 24