Ahoj, snažím se udělat aplikaci, která bude na pozadí snímat stisknuté klávesy. Zkoušel sem několik způsobů, ale žádný nefungoval, když byla aplikace na pozadí. Díky za pomoc.
Fórum › Delphi
Snímání kláves
![](img/forum_vlakno_help_vyreseno.png)
A jaké způsoby jsi zkoušel? Na 100% to jde to udělat pomocí globálního keyboard hooku (https://msdn.microsoft.com/en-us/library/windows/desktop/ms632589%28v=vs.85%29.aspx), případně pomocí raw input (https://msdn.microsoft.com/en-us/library/windows/desktop/ms645536%28v=vs.85%29.aspx) při nastavení RIDEV_INPUTSINK.
Hooky jsou lokální a globální, pokud jsi použil lokální tak to nepude. Nahoď sem co máš, ať se pohnem. A než hooky (kde se musí řešit DLL a tím pádem může nastat problém s kompatibilitou 32 vs 64bit) bych šel cestou raw input, je to překvapivě jednoduché když se do toho dostaneš.
Tu máš drobnou ukázku:
unit MainForm;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, StdCtrls,
WinRawInput;
type
TfMainForm = class(TForm)
meKeys: TMemo;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
procedure OnRawInput(var Msg: TMessage); message WM_INPUT;
procedure ProcessKeyboardInput(Input: TRawKeyboard);
procedure WriteInterceptedKey(VirtualKey: Word; Released: Boolean);
end;
var
fMainForm: TfMainForm;
implementation
{$R *.dfm}
const
MAPVK_VK_TO_VSC = 0;
MAPVK_VSC_TO_VK_EX = 3;
//------------------------------------------------------------------------------
Function GetVirtualKeyName(VirtualKey: Word; NumberForUnknown: Boolean = False): String;
var
Flag_E0: Boolean;
ScanCode: Integer;
begin
case VirtualKey of
VK_NUMLOCK,VK_RCONTROL,VK_RMENU,VK_LWIN,VK_RWIN,VK_INSERT,VK_DELETE,VK_HOME,
VK_END,VK_PRIOR,VK_NEXT,VK_LEFT,VK_RIGHT,VK_UP,VK_DOWN,VK_DIVIDE,VK_APPS,
VK_SNAPSHOT,VK_CLEAR: Flag_E0 := True;
else
Flag_E0 := False;
end;
// MapVirtualKey(Ex) is unable to map following VK to SC, have to do it manually
case VirtualKey of
VK_PAUSE: ScanCode := $45;
VK_SNAPSHOT: ScanCode := $37;
else
ScanCode := MapVirtualKey(VirtualKey,MAPVK_VK_TO_VSC);
end;
If Flag_E0 then ScanCode := ScanCode or $100;
SetLength(Result,32);
SetLength(Result,GetKeyNameText(ScanCode shl 16,PChar(Result),Length(Result)));
If (Length(Result) <= 0) and NumberForUnknown then
Result := '0x' + IntToHex(VirtualKey,2);
end;
//------------------------------------------------------------------------------
procedure TfMainForm.OnRawInput(var Msg: TMessage);
var
RawInputSize: LongWord;
RawInput: PRawInput;
begin
GetRawInputData(HRAWINPUT(Msg.lParam),RID_INPUT,nil,@RawInputSize,SizeOf(TRawInputHeader));
If RawInputSize > 0 then
begin
RawInput := AllocMem(RawInputSize);
try
If GetRawInputData(HRAWINPUT(Msg.lParam),RID_INPUT,RawInput,@RawInputSize,SizeOf(TRawInputHeader)) = RawInputSize then
case RawInput^.header.dwType of
RIM_TYPEMOUSE:;
RIM_TYPEKEYBOARD: ProcessKeyboardInput(RawInput^.keyboard);
RIM_TYPEHID:;
end;
finally
FreeMem(RawInput,RawInputSize);
end;
end;
end;
//------------------------------------------------------------------------------
procedure TfMainForm.ProcessKeyboardInput(Input: TRawKeyboard);
var
Flag_E0: Boolean;
Flag_E1: Boolean;
begin
// Repair input (because raw input in Windows is bugged and generally weird)
Flag_E0 := (Input.Flags and RI_KEY_E0) <> 0;
Flag_E1 := (Input.Flags and RI_KEY_E1) <> 0;
case Input.VKey of
VK_SHIFT: Input.VKey := MapVirtualKey(Input.MakeCode,MAPVK_VSC_TO_VK_EX);
VK_CONTROL: If Flag_E0 then Input.VKey := VK_RCONTROL
else Input.VKey := VK_LCONTROL;
VK_MENU: If Flag_E0 then Input.VKey := VK_RMENU
else Input.VKey := VK_LMENU;
//VK_RETURN: If Flag_E0 then Input.VKey := VK_NUMPADENTER; -> Sadly, there is no VK for numpad enter.
VK_INSERT: If not Flag_E0 then Input.VKey := VK_NUMPAD0;
VK_DELETE: If not Flag_E0 then Input.VKey := VK_DECIMAL;
VK_HOME: If not Flag_E0 then Input.VKey := VK_NUMPAD7;
VK_END: If not Flag_E0 then Input.VKey := VK_NUMPAD1;
VK_PRIOR: If not Flag_E0 then Input.VKey := VK_NUMPAD9;
VK_NEXT: If not Flag_E0 then Input.VKey := VK_NUMPAD3;
VK_CLEAR: If not Flag_E0 then Input.VKey := VK_NUMPAD5;
VK_LEFT: If not Flag_E0 then Input.VKey := VK_NUMPAD4;
VK_RIGHT: If not Flag_E0 then Input.VKey := VK_NUMPAD6;
VK_UP: If not Flag_E0 then Input.VKey := VK_NUMPAD8;
VK_DOWN: If not Flag_E0 then Input.VKey := VK_NUMPAD2;
VK_NUMLOCK: Input.MakeCode := MapVirtualKey(Input.VKey,MAPVK_VK_TO_VSC) or $100;
$FF: Exit;
end;
If Flag_E1 then
begin
If Input.VKey = VK_PAUSE then
Input.MakeCode := $45
else
Input.MakeCode := MapVirtualKey(Input.VKey,MAPVK_VK_TO_VSC);
end;
WriteInterceptedKey(Input.VKey,(Input.Flags and RI_KEY_BREAK) <> 0);
end;
//------------------------------------------------------------------------------
procedure TfMainForm.WriteInterceptedKey(VirtualKey: Word; Released: Boolean);
begin
If Released then
meKeys.Lines.Add('Released key 0x' + IntToHex(VirtualKey,2) + ' - ' + GetVirtualKeyName(VirtualKey))
else
meKeys.Lines.Add('Pressed key 0x' + IntToHex(VirtualKey,2) + ' - ' + GetVirtualKeyName(VirtualKey));
end;
//------------------------------------------------------------------------------
procedure TfMainForm.FormCreate(Sender: TObject);
var
RawInputDevice: PRawInputDevice;
begin
New(RawInputDevice);
try
RawInputDevice^.usUsagePage := $01;
RawInputDevice^.usUsage := $06; //keyboards
RawInputDevice^.dwFlags := RIDEV_INPUTSINK;
RawInputDevice^.hwndTarget := Handle;
If not RegisterRawInputDevices(RawInputDevice,1,SizeOf(TRawInputDevice)) then
raise Exception.Create('Raw input registration failed.');
finally
Dispose(RawInputDevice);
end;
end;
end.
Jednotka WinRawInput je moje, protože jsem to psal v D7 a tam není podpora raw input. Jestli máš novější delphi tak to tam snad už je (nahraď patřičnou jednotkou z nich), pokud ne, dej vědět, hodím ji sem.
No problemo:
unit WinRawInput;
interface
uses
Windows;
type
USHORT = Word;
LONG = LongInt;
INT = Integer;
HANDLE = THandle;
QWORD = UInt64;
const
WM_INPUT = $00FF;
WM_INPUT_DEVICE_CHANGE = $00FE;
//WM_INPUT / wParam
RIM_INPUT = 0;
RIM_INPUTSINK = 1;
//WM_INPUT_DEVICE_CHANGE / wParam
GIDC_ARRIVAL = 1;
GIDC_REMOVAL = 2;
//RAWINPUTDEVICE.dwFlags
RIDEV_APPKEYS = $00000400;
RIDEV_CAPTUREMOUSE = $00000200;
RIDEV_DEVNOTIFY = $00002000;
RIDEV_EXCLUDE = $00000010;
RIDEV_EXINPUTSINK = $00001000;
RIDEV_INPUTSINK = $00000100;
RIDEV_NOHOTKEYS = $00000200;
RIDEV_NOLEGACY = $00000030;
RIDEV_PAGEONLY = $00000020;
RIDEV_REMOVE = $00000001;
//RAWINPUTDEVICELIST.dwType, RAWINPUTHEADER.dwType, RID_DEVICE_INFO.dwType
RIM_TYPEHID = 2;
RIM_TYPEKEYBOARD = 1;
RIM_TYPEMOUSE = 0;
//RAWMOUSE.usFlags
MOUSE_ATTRIBUTES_CHANGED = $04;
MOUSE_MOVE_RELATIVE = $00;
MOUSE_MOVE_ABSOLUTE = $01;
MOUSE_VIRTUAL_DESKTOP = $02;
//RAWMOUSE.usButtonFlags
RI_MOUSE_LEFT_BUTTON_DOWN = $0001;
RI_MOUSE_LEFT_BUTTON_UP = $0002;
RI_MOUSE_MIDDLE_BUTTON_DOWN = $0010;
RI_MOUSE_MIDDLE_BUTTON_UP = $0020;
RI_MOUSE_RIGHT_BUTTON_DOWN = $0004;
RI_MOUSE_RIGHT_BUTTON_UP = $0008;
RI_MOUSE_BUTTON_1_DOWN = RI_MOUSE_LEFT_BUTTON_DOWN;
RI_MOUSE_BUTTON_1_UP = RI_MOUSE_LEFT_BUTTON_UP;
RI_MOUSE_BUTTON_2_DOWN = RI_MOUSE_RIGHT_BUTTON_DOWN;
RI_MOUSE_BUTTON_2_UP = RI_MOUSE_RIGHT_BUTTON_UP;
RI_MOUSE_BUTTON_3_DOWN = RI_MOUSE_MIDDLE_BUTTON_DOWN;
RI_MOUSE_BUTTON_3_UP = RI_MOUSE_MIDDLE_BUTTON_UP;
RI_MOUSE_BUTTON_4_DOWN = $0040;
RI_MOUSE_BUTTON_4_UP = $0080;
RI_MOUSE_BUTTON_5_DOWN = $0100;
RI_MOUSE_BUTTON_5_UP = $0200;
RI_MOUSE_WHEEL = $0400;
//RAWKEYBOARD.Flags
RI_KEY_BREAK = 1;
RI_KEY_E0 = 2;
RI_KEY_E1 = 4;
RI_KEY_MAKE = 0;
//GetRawInputData / uiCommand
RID_HEADER = $10000005;
RID_INPUT = $10000003;
//GetRawInputDeviceInfo / uiCommand
RIDI_DEVICENAME = $20000007;
RIDI_DEVICEINFO = $2000000b;
RIDI_PREPARSEDDATA = $20000005;
//==============================================================================
type
HRAWINPUT = THandle;
//------------------------------------------------------------------------------
tagRAWINPUTDEVICE = record
usUsagePage: USHORT;
usUsage: USHORT;
dwFlags: DWORD;
hwndTarget: HWND;
end;
RAWINPUTDEVICE = tagRAWINPUTDEVICE;
TRAWINPUTDEVICE = tagRAWINPUTDEVICE;
PRAWINPUTDEVICE = ^TRAWINPUTDEVICE;
LPRAWINPUTDEVICE = ^TRAWINPUTDEVICE;
TRAWINPUTDEVICEARRAY = Array[0..High(Word)] of TRAWINPUTDEVICE;
PRAWINPUTDEVICEARRAY = ^TRAWINPUTDEVICEARRAY;
//------------------------------------------------------------------------------
tagRAWINPUTDEVICELIST = record
hDevice: HANDLE;
dwType: DWORD;
end;
RAWINPUTDEVICELIST = tagRAWINPUTDEVICELIST;
TRAWINPUTDEVICELIST = tagRAWINPUTDEVICELIST;
PRAWINPUTDEVICELIST = ^TRAWINPUTDEVICELIST;
//------------------------------------------------------------------------------
tagRAWINPUTHEADER = record
dwType: DWORD;
dwSize: DWORD;
hDevice: HANDLE;
wParam: WPARAM;
end;
RAWINPUTHEADER = tagRAWINPUTHEADER;
TRAWINPUTHEADER = tagRAWINPUTHEADER;
PRAWINPUTHEADER = ^TRAWINPUTHEADER;
//------------------------------------------------------------------------------
tagRAWMOUSE = record
usFlags: USHORT;
case Integer of
0: (ulButtons: ULONG);
1: (usButtonFlags: USHORT;
usButtonsData: USHORT;
ulRawButtons: ULONG;
lLastX: LONG;
lLastY: LONG;
ulExtraInformation: ULONG);
end;
RAWMOUSE = tagRAWMOUSE;
TRAWMOUSE = tagRAWMOUSE;
PRAWMOUSE = ^TRAWMOUSE;
LPRAWMOUSE = ^TRAWMOUSE;
//------------------------------------------------------------------------------
tagRAWKEYBOARD = record
MakeCode: USHORT;
Flags: USHORT;
Reserved: USHORT;
VKey: USHORT;
Message: UINT;
ExtraInformation: ULONG;
end;
RAWKEYBOARD = tagRAWKEYBOARD;
TRAWKEYBOARD = tagRAWKEYBOARD;
PRAWKEYBOARD = ^TRAWKEYBOARD;
LPRAWKEYBOARD = ^TRAWKEYBOARD;
//------------------------------------------------------------------------------
tagRAWHID = record
dwSizeHid: DWORD;
dwCount: DWORD;
bRawData: Byte;
end;
RAWHID = tagRAWHID;
TRAWHID = tagRAWHID;
PRAWHID = ^TRAWHID;
LPRAWHID = ^TRAWHID;
//------------------------------------------------------------------------------
tagRAWINPUT = record
header: RAWINPUTHEADER;
case Integer of
RIM_TYPEMOUSE: (mouse: RAWMOUSE);
RIM_TYPEKEYBOARD:(keyboard: RAWKEYBOARD);
RIM_TYPEHID: (hid: RAWHID);
end;
RAWINPUT = tagRAWINPUT;
TRAWINPUT = tagRAWINPUT;
PRAWINPUT = ^TRAWINPUT;
LPRAWINPUT = ^TRAWINPUT;
PPRAWINPUT = ^PRAWINPUT;
//------------------------------------------------------------------------------
tagRID_DEVICE_INFO_MOUSE = record
dwId: DWORD;
dwNumberOfButtons: DWORD;
dwSampleRate: DWORD;
fHasHorizontalWheel: BOOL;
end;
RID_DEVICE_INFO_MOUSE = tagRID_DEVICE_INFO_MOUSE;
TRID_DEVICE_INFO_MOUSE = tagRID_DEVICE_INFO_MOUSE;
PRID_DEVICE_INFO_MOUSE = ^TRID_DEVICE_INFO_MOUSE;
//------------------------------------------------------------------------------
tagRID_DEVICE_INFO_KEYBOARD = record
dwType: DWORD;
dwSubType: DWORD;
dwKeyboardMode: DWORD;
dwNumberOfFunctionKeys: DWORD;
dwNumberOfIndicators: DWORD;
dwNumberOfKeysTotal: DWORD;
end;
RID_DEVICE_INFO_KEYBOARD = tagRID_DEVICE_INFO_KEYBOARD;
TRID_DEVICE_INFO_KEYBOARD = tagRID_DEVICE_INFO_KEYBOARD;
PRID_DEVICE_INFO_KEYBOARD = ^TRID_DEVICE_INFO_KEYBOARD;
//------------------------------------------------------------------------------
tagRID_DEVICE_INFO_HID = record
dwVendorId: DWORD;
dwProductId: DWORD;
dwVersionNumber: DWORD;
usUsagePage: USHORT;
usUsage: USHORT;
end;
RID_DEVICE_INFO_HID = tagRID_DEVICE_INFO_HID;
TRID_DEVICE_INFO_HID = tagRID_DEVICE_INFO_HID;
PRID_DEVICE_INFO_HID = ^TRID_DEVICE_INFO_HID;
//------------------------------------------------------------------------------
tagRID_DEVICE_INFO = record
cbSize: DWORD;
case dwType: DWORD of
RIM_TYPEMOUSE: (mouse: RID_DEVICE_INFO_MOUSE);
RIM_TYPEKEYBOARD:(keyboard: RID_DEVICE_INFO_KEYBOARD);
RIM_TYPEHID: (hid: RID_DEVICE_INFO_HID);
end;
RID_DEVICE_INFO = tagRID_DEVICE_INFO;
TRID_DEVICE_INFO = tagRID_DEVICE_INFO;
PRID_DEVICE_INFO = ^TRID_DEVICE_INFO;
LPRID_DEVICE_INFO = ^TRID_DEVICE_INFO;
//==============================================================================
Function DefRawInputProc(
paRawInput: PPRAWINPUT;
nInput: INT;
cbSizeHeader: UINT): LRESULT; stdcall; external user32;
//------------------------------------------------------------------------------
Function GetRawInputBuffer(
pData: PRAWINPUT;
pcbSize: PUINT;
cbSizeHeader: UINT): UINT; stdcall; external user32;
//------------------------------------------------------------------------------
Function GetRawInputData(
hRawInput: HRAWINPUT;
uiCommand: UINT;
pData: Pointer;
pcbSize: PUINT;
cbSizeHeader: UINT): UINT; stdcall; external user32;
//------------------------------------------------------------------------------
Function GetRawInputDeviceInfo(
hDevice: THandle;
uiCommand: UINT;
pData: Pointer;
pcbSize: PUINT): UINT; stdcall; external user32 name{$IFDEF UNICODE}'GetRawInputDeviceInfoW'{$ELSE}'GetRawInputDeviceInfoA'{$ENDIF};
Function GetRawInputDeviceInfoA(
hDevice: THandle;
uiCommand: UINT;
pData: Pointer;
pcbSize: PUINT): UINT; stdcall; external user32 name 'GetRawInputDeviceInfoA';
Function GetRawInputDeviceInfoW(
hDevice: THandle;
uiCommand: UINT;
pData: Pointer;
pcbSize: PUINT): UINT; stdcall; external user32 name 'GetRawInputDeviceInfoW';
//------------------------------------------------------------------------------
Function GetRawInputDeviceList(
pRawInputDeviceLis: PRAWINPUTDEVICELIST;
puiNumDevices: PUINT;
cbSize: UINT): UINT; stdcall; external user32;
//------------------------------------------------------------------------------
Function GetRegisteredRawInputDevices(
pRawInputDevices: PRAWINPUTDEVICE;
puiNumDevices: PUINT;
cbSize: UINT): UINT; stdcall; external user32;
//------------------------------------------------------------------------------
Function RegisterRawInputDevices(
pRawInputDevices: PRAWINPUTDEVICE;
uiNumDevices: UINT;
cbSize: UINT): BOOL; stdcall; external user32;
//==============================================================================
Function GET_RAWINPUT_CODE_WPARAM(wParam: WPARAM): WPARAM;
Function NEXTRAWINPUTBLOCK(ptr: PRAWINPUT): PRAWINPUT;
implementation
Function GET_RAWINPUT_CODE_WPARAM(wParam: WPARAM): WPARAM;
begin
Result := wParam and $FF;
end;
Function RAWINPUT_ALIGN(x: Pointer): Pointer;
begin
{$IFDEF x64}
{%H-}Result := Pointer((NativeInt(x) + SizeOf(QWORD) - 1) and not (SizeOf(QWORD) - 1));
{$ELSE}
{%H-}Result := Pointer((NativeInt(x) + SizeOf(DWORD) - 1) and not (SizeOf(DWORD) - 1));
{$ENDIF}
end;
Function NEXTRAWINPUTBLOCK(ptr: PRAWINPUT): PRAWINPUT;
begin
{%H-}Result := PRAWINPUT(RAWINPUT_ALIGN(Pointer(NativeInt(ptr) + ptr^.header.dwSize)));
end;
end.
No to záleží, jak to hodláš pojmout. Každopádně pokud to chceš provozovat ve vlákně, tak si musíš zařídit vlastní cyklus na zpracování zpráv a pochopitelně alokovat okno co bude ty zprávy přijímat. Pokud ta aplikace nebude dělat nic jinýho než zpracovávat vstupy z klávesnice, notabene na pozadí, tak nemá cenu to cpát do vláken.
Proč to tedy chceš skrývat ze seznamu běžících procesů, to ti nestačí spustit to na pozadí? To je skoro ukázkovej případ malwaru. Přesně kvůli takovejmhle pokusům, co nakonec skončily jako viry, byly svýho času kompletně všechny programy psaný v delphi označovaný antiviry za malware.
Tak jestli ti stačí to skrýt, tak do DPR (před Application.Run) dej Application.ShowMainForm := False. Na ovládání si můžeš zařídit třeba tray ikonu (https://msdn.microsoft.com/en-us/library/windows/desktop/bb762159%28v=vs.85%29.aspx).
![](img/forum_nove_psp_new.png)
Přidej příspěvek
Ano, opravdu chci reagovat → zobrazí formulář pro přidání příspěvku
×Vložení zdrojáku
×Vložení obrázku
×Vložení videa
![](/img/smile/1.gif)
![](/img/smile/2.gif)
![](/img/smile/3.gif)
![](/img/smile/4.gif)
![](/img/smile/5.gif)
![](/img/smile/6.gif)
![](/img/smile/7.gif)
![](/img/smile/8.gif)
![](/img/smile/9.gif)
![](/img/smile/10.gif)
![](/img/smile/11.gif)
![](/img/smile/12.gif)
![](/img/smile/13.gif)
![](/img/smile/14.gif)
![](/img/smile/15.gif)
![](/img/smile/16.gif)
![](/img/smile/17.gif)
![](/img/smile/18.gif)
![](/img/smile/19.gif)
![](/img/smile/20.gif)
![](/img/smile/21.gif)
![](/img/smile/22.gif)
![](/img/smile/23.gif)
![](/img/smile/24.gif)
![](/img/smile/25.gif)
![](/img/smile/26.gif)
![](/img/smile/27.gif)
![](/img/smile/28.gif)
![](/img/smile/29.gif)
![](/img/smile/30.gif)
![](/img/smile/31.gif)
![](/img/smile/32.gif)
Uživatelé prohlížející si toto vlákno
Podobná vlákna
Snímání kláves (linux) — založil Atheo
Snimani klaves. (neco jako BIND) — založil Pepa Rohlik
Snímání a ošetření stisku/přidržení dvou kláves naráz — založil návštěvník
C - Snímání pixelu XY — založil Jan
OpenGL - Snímání obrazovky — založil richard.zavodny