LRESULT CALLBACK HookCallback(int nCode, WPARAM wParam, LPARAM lParam)
+;
void processKey();
void register_hook();
void unregister_hook();
void MsgLoop();
send_string.h
typedef struct delayed {
INPUT data;
struct delayed * next;
} delayed;
typedef unsigned char byte;
void send_string (const wchar_t * str);
void send_cmd(int time, byte vkcode);
void sendDelayedKeys();
void paste_from_clpb(int dk)
hook.c
#include <windows.h>
#include <WinAble.h>
#include "stdio.h"
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
#include "hook.h"
#include "send_string.h"
HHOOK hook;
int delayedSize = 0;
delayed * start = NULL;
delayed * last = NULL;
LRESULT CALLBACK HookCallback( int nCode, WPARAM wParam, LPARAM lParam
+ ) {
KBDLLHOOKSTRUCT * p = ( KBDLLHOOKSTRUCT * ) lParam;
int kup = p->flags & LLKHF_UP;
int alt = p->flags & LLKHF_ALTDOWN;
int ext = p->flags & LLKHF_EXTENDED;
processKey( kup, p->vkCode, alt, ext );
return CallNextHookEx( hook, nCode, wParam, lParam );
}
void processKey( int kup, int vkCode, int alt, int ext ) {
//printf ("processKey in C\n");
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP);
EXTEND( SP, 4 );
PUSHs( sv_2mortal( newSViv(kup) ) );
PUSHs( sv_2mortal( newSViv(vkCode) ) );
PUSHs( sv_2mortal( newSViv(alt) ) );
PUSHs( sv_2mortal( newSViv(ext) ) );
PUTBACK;
//count = call_pv( "Adder", G_SCALAR );
int count = call_pv( "Win32::Shortkeys::Kbh::process_key", G_DISCA
+RD );
PUTBACK;
FREETMPS;
LEAVE;
if ( count != 0 ) croak("Big trouble\n");
}
void MsgLoop() {
MSG message;
while ( GetMessage( &message, NULL, 0, 0 ) ) {
TranslateMessage(&message);
DispatchMessage(&message);
}
}
void register_hook() {
HMODULE hMod = (HMODULE) GetModuleHandle(NULL);
hook = SetWindowsHookEx( WH_KEYBOARD_LL, HookCallback, hMod, 0 );
}
void unregister_hook() {
UnhookWindowsHookEx(hook);
DWORD hookThreadId = GetCurrentThreadId();
PostThreadMessage( hookThreadId, WM_QUIT, 0, 0L );
}
void send_string( const wchar_t * str ) {
printf( "send_string : %s L:%i\n", str, wcslen(str) );
INPUT inp [2];
memset( inp, 0, sizeof(INPUT) );
inp [0] . type = INPUT_KEYBOARD;
/**
* KEYEVENTF_UNICODE flag send the string as Unicode characters.
* Unicode makes life easy because you don't have
* to synthesize capital letters using the Shift key. Without KEYEV
+ENTF_ UNICODE,
* you'd have to send capital E as <Shift>
* followed by e, with down/up events for each, for a total of fou
+r keystrokes.
*/
// to avoid shift, and so on
inp [0] . ki . dwFlags = KEYEVENTF_UNICODE;
inp [1] = inp [0];
inp [1] . ki . dwFlags |= KEYEVENTF_KEYUP;
int count = 0;
const wchar_t * p;
for ( p = str; *p; p++ ) {
count++;
if (delayedSize > 0 && count ==1 ){
inp[0].ki.wVk = inp[1].ki.wVk = LOBYTE(VkKeyScan(*p));
//inp[0].ki.wScan = inp[1].ki.wScan = *p;
} else {
inp[0].ki.wVk = inp[1].ki.wVk = 0;
//inp[0].ki.wScan = inp[1].ki.wScan = *p;
}
inp[0].ki.wScan = inp[1].ki.wScan = *p;
printf("wScan has %i\n", *p);
SendInput(2, inp, sizeof(INPUT));
if (count ==1){
sendDelayedKeys();
}
}//for
}
void send_cmd(int time, byte vkcode){
time*=2; //doubler le nombre de touche delete à envoyer
int size = time;
INPUT i[size];
//printf ("sendCmd : %d \n", cont++);
ZeroMemory(&i,sizeof(i));
int sendKey = 0;
int j;
for (j=0;j<time;j++){
if (vkcode == VK_SHIFT || vkcode == VK_CONTROL || vkcode == VK
+_MENU){
if (j%2==0) {
i[j].type = INPUT_KEYBOARD;
i[j].ki.wVk=vkcode;
i[j].ki.dwFlags =0;
sendKey++;
} else {
delayed *tmp;
tmp = (delayed*) malloc(sizeof(struct delayed));
tmp->data.type = INPUT_KEYBOARD;
tmp->data.ki.wVk = vkcode;
tmp->data.ki.dwFlags = KEYEVENTF_KEYUP;
tmp->next = NULL;
delayedSize++;
if (start == NULL){
start = tmp;
} else {
last->next = tmp;
}
last = tmp;
}
} else {
i[sendKey].type = INPUT_KEYBOARD;
i[sendKey].ki.wVk=vkcode;
i[sendKey].ki.dwFlags = (j%2==0?0:KEYEVENTF_KEYUP);
sendKey++;
}
}
SendInput(sendKey,i,sizeof(INPUT));
}
void sendDelayedKeys(){
delayed *current;
current = start;
INPUT s[delayedSize];
int pos =0;
while (current != NULL) {
delayed *temp;
s[pos++]= current->data;
temp = current;
free(current);
current = temp->next;
}
start = NULL;
last = NULL;
if (delayedSize > 0){
SendInput(delayedSize, s, sizeof(INPUT) );
delayedSize=0;
}
}
void paste_from_clpb(int dk) {
dk*=2; //doubler le nombre de touche delete à envoyer
int size = 4+dk;
INPUT i[size];
ZeroMemory(&i,sizeof(i));
int j;
for (j=0;j<dk;j++){
i[j].type = INPUT_KEYBOARD;
i[j].ki.wVk=VK_BACK;
i[j].ki.dwFlags = (j%2==0?0:KEYEVENTF_KEYUP);
}
i[0+dk].type = INPUT_KEYBOARD;
i[0+dk].ki.wVk =VK_CONTROL;
i[1+dk].type = INPUT_KEYBOARD;
i[1+dk].ki.wVk = LOBYTE(VkKeyScan('v'));
i[2+dk].type = INPUT_KEYBOARD;
i[2+dk].ki.dwFlags = KEYEVENTF_KEYUP;
i[2+dk].ki.wVk =LOBYTE(VkKeyScan('v'));
i[3+dk].type = INPUT_KEYBOARD;
i[3+dk].ki.dwFlags = KEYEVENTF_KEYUP;
i[3+dk].ki.wVk = VK_CONTROL;
SendInput(size,i,sizeof(INPUT));
}
typemap
const wchar_t * T_WCHAR
byte T_U_CHAR
INPUT
T_WCHAR
// $var = ($type)SvPV_nolen($arg)
{
Newz(0, $var, SvLEN($arg), wchar_t);
U8* src = (U8*) SvPV_nolen($arg);
wchar_t* dst = (wchar_t*) $var;
if (SvUTF8($arg)) {
STRLEN len;
while (*src) {
*dst++ = utf8_to_uvuni((U8*) src, &len);
//*dst++ = uvchr_to_urf8()
src += len;
}
} else {
while (*src) {
*dst++ = (wchar_t) *src++;
}
}
*dst = 0;
SAVEFREEPV($var);
}
T_U_CHAR
$var = (unsigned char)SvUV($arg)
OUTPUT
T_WCHAR
//sv_setpv((SV*)$arg, $var);
{
wchar_t* src = (wchar_t*) $var;
U8* dst;
U8* d;
Newz(0, dst, 3 * wcslen(src), U8);
d = dst;
while (*src) {
d = uvuni_to_utf8(d, *src++);
}
*d = 0;
sv_setpv((SV*)$arg, (char*) dst);
sv_utf8_decode($arg);
Safefree(dst);
}
T_U_CHAR
sv_setuv($arg, (UV)$var);
Kbh.xs
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
#include "hook.h"
#include "send_string.h"
MODULE = Win32::Shortkeys::Kbh PACKAGE = Win32::Shortkey
+s::Kbh
PROTOTYPES: DISABLE
void
MsgLoop()
void
register_hook()
void
unregister_hook()
void
send_string(s)
const wchar_t * s
void
send_cmd(howmutch, vkcode)
int howmutch
byte vkcode
void
paste_from_clpb(dk)
int dk
Makefile.pl
use ExtUtils::MakeMaker;
# See lib/ExtUtils/MakeMaker.pm for details of how to influence
# the contents of the Makefile that is written.
WriteMakefile(
NAME => 'Win32::Shortkeys::Kbh',
VERSION_FROM => 'lib/Win32/Shortkeys/Kbh.pm', # finds $VERSIO
+N, requires EU::MM from perl >= 5.5
PREREQ_PM => {}, # e.g., Module::Name => 1.1
ABSTRACT_FROM => 'lib/Win32/Shortkeys/Kbh.pm', # retrieve abst
+ract from module
OBJECT => 'hook.o Kbh.o',
AUTHOR => 'frazap',
#LICENSE => 'perl',
#Value must be from legacy list of licenses here
#http://search.cpan.org/perldoc?Module%3A%3ABuild%3A%3AAPI
LIBS => [''], # e.g., '-lm'
DEFINE => '', # e.g., '-DHAVE_SOMETHING'
INC => '-I.', # e.g., '-I. -I/usr/include/other'
# Un-comment this if you add C files to link with later:
# OBJECT => '$(O_FILES)', # link all the C files too
);
lib/Win32/Shortkeys/Kbh.pm
package Win32::Shortkeys::Kbh;
use strict;
use warnings;
use Carp;
require Exporter;
#use AutoLoader qw(AUTOLOAD);
our @ISA = qw(Exporter);
# Items to export into callers namespace by default. Note: do not expo
+rt
# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.
# This allows declaration use Win32::Shortkeys::Kbh ':all';
# If you do not need this, moving things directly into @EXPORT or @EXP
+ORT_OK
# will save memory.
our %EXPORT_TAGS = (
input => [ qw(send_string send_cmd paste_from_clpb) ],
hook => [qw(register_hook unregister_hook MsgLoop set_key_processor
+)],
vkcode => [qw (
VK_LBUTTON VK_RBUTTON VK_CANCEL VK_MBUTTON VK_XBUTTON1 VK_XBUTTON2 VK_
+BACK
VK_TAB VK_CLEAR VK_RETURN VK_SHIFT VK_CONTROL VK_MENU VK_PAUSE VK_CAPI
+TAL
VK_KANA VK_HANGEUL VK_HANGUL VK_JUNJA VK_FINAL VK_HANJA VK_KANJI VK_ES
+CAPE
VK_CONVERT VK_NONCONVERT VK_ACCEPT VK_MODECHANGE VK_SPACE VK_PRIOR VK_
+NEXT
VK_END VK_HOME VK_LEFT VK_UP VK_RIGHT VK_DOWN VK_SELECT VK_PRINT VK_EX
+ECUTE
VK_SNAPSHOT VK_INSERT VK_DELETE VK_HELP VK_LWIN VK_RWIN VK_APPS VK_SLE
+EP
VK_NUMPAD0 VK_NUMPAD1 VK_NUMPAD2 VK_NUMPAD3 VK_NUMPAD4 VK_NUMPAD5 VK_N
+UMPAD6
VK_NUMPAD7 VK_NUMPAD8 VK_NUMPAD9 VK_MULTIPLY VK_ADD VK_SEPARATOR VK_SU
+BTRACT
VK_DECIMAL VK_DIVIDE VK_F1 VK_F2 VK_F3 VK_F4 VK_F5 VK_F6 VK_F7 VK_F8 V
+K_F9
VK_F10 VK_F11 VK_F12 VK_F13 VK_F14 VK_F15 VK_F16 VK_F17 VK_F18 VK_F19
+VK_F20
VK_F21 VK_F22 VK_F23 VK_F24 VK_NUMLOCK VK_SCROLL VK_OEM_NEC_EQUAL
VK_OEM_FJ_JISHO VK_OEM_FJ_MASSHOU VK_OEM_FJ_TOUROKU VK_OEM_FJ_LOYA
VK_OEM_FJ_ROYA VK_LSHIFT VK_RSHIFT VK_LCONTROL VK_RCONTROL VK_LMENU
VK_RMENU VK_BROWSER_BACK VK_BROWSER_FORWARD VK_BROWSER_REFRESH
VK_BROWSER_STOP VK_BROWSER_SEARCH VK_BROWSER_FAVORITES VK_BROWSER_HOME
+
VK_VOLUME_MUTE VK_VOLUME_DOWN VK_VOLUME_UP VK_MEDIA_NEXT_TRACK
VK_MEDIA_PREV_TRACK VK_MEDIA_STOP VK_MEDIA_PLAY_PAUSE VK_LAUNCH_MAIL
VK_LAUNCH_MEDIA_SELECT VK_LAUNCH_APP1 VK_LAUNCH_APP2 VK_OEM_1 VK_OEM_P
+LUS
VK_OEM_COMMA VK_OEM_MINUS VK_OEM_PERIOD VK_OEM_2 VK_OEM_3 VK_OEM_4 VK_
+OEM_5
VK_OEM_6 VK_OEM_7 VK_OEM_8 VK_OEM_AX VK_OEM_102 VK_ICO_HELP VK_ICO_00
VK_PROCESSKEY VK_ICO_CLEAR VK_PACKET VK_OEM_RESET VK_OEM_JUMP VK_OEM_P
+A1
VK_OEM_PA2 VK_OEM_PA3 VK_OEM_WSCTRL VK_OEM_CUSEL VK_OEM_ATTN VK_OEM_FI
+NISH
VK_OEM_COPY VK_OEM_AUTO VK_OEM_ENLW VK_OEM_BACKTAB VK_ATTN VK_CRSEL
VK_EXSEL VK_EREOF VK_PLAY VK_ZOOM VK_NONAME VK_PA1 VK_OEM_CLEAR
)],
);
my %seen;
push @{$EXPORT_TAGS{all}},
grep {!$seen{$_}++} @{$EXPORT_TAGS{$_}} foreach keys %EXPORT_TAGS;
Exporter::export_tags('hook');
Exporter::export_ok_tags(qw(input vkcode));
our $VERSION = '0.01';
require XSLoader;
XSLoader::load('Win32::Shortkeys::Kbh', $VERSION);
my $key_processor;
sub set_key_processor {
$key_processor = shift;
confess ("set_key_processor must receive a sub ref") unless (ref $
+key_processor eq "CODE");
}
sub process_key {
# my ($cup, $code, $alt, $ext) = @_;
#print "process_key in perl cup: $cup code: $code alt: $alt ext: $
+ext \n";
$key_processor->(@_);
}
use constant {
VK_LBUTTON =>0x01,
VK_RBUTTON =>0x02,
VK_CANCEL =>0x03,
VK_MBUTTON =>0x04,
VK_XBUTTON1 =>0x05,
VK_XBUTTON2 =>0x06,
VK_BACK =>0x08,
VK_TAB =>0x09,
VK_CLEAR =>0x0C,
VK_RETURN =>0x0D,
VK_SHIFT =>0x10,
VK_CONTROL =>0x11,
VK_MENU =>0x12,
VK_PAUSE =>0x13,
VK_CAPITAL =>0x14,
VK_KANA =>0x15,
VK_HANGEUL =>0x15,
VK_HANGUL =>0x15,
VK_JUNJA =>0x17,
VK_FINAL =>0x18,
VK_HANJA =>0x19,
VK_KANJI =>0x19,
VK_ESCAPE =>0x1B,
VK_CONVERT =>0x1C,
VK_NONCONVERT =>0x1D,
VK_ACCEPT =>0x1E,
VK_MODECHANGE =>0x1F,
VK_SPACE =>0x20,
VK_PRIOR =>0x21,
VK_NEXT =>0x22,
VK_END =>0x23,
VK_HOME =>0x24,
VK_LEFT =>0x25,
VK_UP =>0x26,
VK_RIGHT =>0x27,
VK_DOWN =>0x28,
VK_SELECT =>0x29,
VK_PRINT =>0x2A,
VK_EXECUTE =>0x2B,
VK_SNAPSHOT =>0x2C,
VK_INSERT =>0x2D,
VK_DELETE =>0x2E,
VK_HELP =>0x2F,
VK_LWIN =>0x5B,
VK_RWIN =>0x5C,
VK_APPS =>0x5D,
VK_SLEEP =>0x5F,
VK_NUMPAD0 =>0x60,
VK_NUMPAD1 =>0x61,
VK_NUMPAD2 =>0x62,
VK_NUMPAD3 =>0x63,
VK_NUMPAD4 =>0x64,
VK_NUMPAD5 =>0x65,
VK_NUMPAD6 =>0x66,
VK_NUMPAD7 =>0x67,
VK_NUMPAD8 =>0x68,
VK_NUMPAD9 =>0x69,
VK_MULTIPLY =>0x6A,
VK_ADD =>0x6B,
VK_SEPARATOR =>0x6C,
VK_SUBTRACT =>0x6D,
VK_DECIMAL =>0x6E,
VK_DIVIDE =>0x6F,
VK_F1 =>0x70,
VK_F2 =>0x71,
VK_F3 =>0x72,
VK_F4 =>0x73,
VK_F5 =>0x74,
VK_F6 =>0x75,
VK_F7 =>0x76,
VK_F8 =>0x77,
VK_F9 =>0x78,
VK_F10 =>0x79,
VK_F11 =>0x7A,
VK_F12 =>0x7B,
VK_F13 =>0x7C,
VK_F14 =>0x7D,
VK_F15 =>0x7E,
VK_F16 =>0x7F,
VK_F17 =>0x80,
VK_F18 =>0x81,
VK_F19 =>0x82,
VK_F20 =>0x83,
VK_F21 =>0x84,
VK_F22 =>0x85,
VK_F23 =>0x86,
VK_F24 =>0x87,
VK_NUMLOCK =>0x90,
VK_SCROLL =>0x91,
VK_OEM_NEC_EQUAL =>0x92,
VK_OEM_FJ_JISHO =>0x92,
VK_OEM_FJ_MASSHOU =>0x93,
VK_OEM_FJ_TOUROKU =>0x94,
VK_OEM_FJ_LOYA =>0x95,
VK_OEM_FJ_ROYA =>0x96,
VK_LSHIFT =>0xA0,
VK_RSHIFT =>0xA1,
VK_LCONTROL =>0xA2,
VK_RCONTROL =>0xA3,
VK_LMENU =>0xA4,
VK_RMENU =>0xA5,
VK_BROWSER_BACK =>0xA6,
VK_BROWSER_FORWARD =>0xA7,
VK_BROWSER_REFRESH =>0xA8,
VK_BROWSER_STOP =>0xA9,
VK_BROWSER_SEARCH =>0xAA,
VK_BROWSER_FAVORITES =>0xAB,
VK_BROWSER_HOME =>0xAC,
VK_VOLUME_MUTE =>0xAD,
VK_VOLUME_DOWN =>0xAE,
VK_VOLUME_UP =>0xAF,
VK_MEDIA_NEXT_TRACK =>0xB0,
VK_MEDIA_PREV_TRACK =>0xB1,
VK_MEDIA_STOP =>0xB2,
VK_MEDIA_PLAY_PAUSE =>0xB3,
VK_LAUNCH_MAIL =>0xB4,
VK_LAUNCH_MEDIA_SELECT =>0xB5,
VK_LAUNCH_APP1 =>0xB6,
VK_LAUNCH_APP2 =>0xB7,
VK_OEM_1 =>0xBA,
VK_OEM_PLUS =>0xBB,
VK_OEM_COMMA =>0xBC,
VK_OEM_MINUS =>0xBD,
VK_OEM_PERIOD =>0xBE,
VK_OEM_2 =>0xBF,
VK_OEM_3 =>0xC0,
VK_OEM_4 =>0xDB,
VK_OEM_5 =>0xDC,
VK_OEM_6 =>0xDD,
VK_OEM_7 =>0xDE,
VK_OEM_8 =>0xDF,
VK_OEM_AX =>0xE1,
VK_OEM_102 =>0xE2,
VK_ICO_HELP =>0xE3,
VK_ICO_00 =>0xE4,
VK_PROCESSKEY =>0xE5,
VK_ICO_CLEAR =>0xE6,
VK_PACKET =>0xE7,
VK_OEM_RESET =>0xE9,
VK_OEM_JUMP =>0xEA,
VK_OEM_PA1 =>0xEB,
VK_OEM_PA2 =>0xEC,
VK_OEM_PA3 =>0xED,
VK_OEM_WSCTRL =>0xEE,
VK_OEM_CUSEL =>0xEF,
VK_OEM_ATTN =>0xF0,
VK_OEM_FINISH =>0xF1,
VK_OEM_COPY =>0xF2,
VK_OEM_AUTO =>0xF3,
VK_OEM_ENLW =>0xF4,
VK_OEM_BACKTAB =>0xF5,
VK_ATTN =>0xF6,
VK_CRSEL =>0xF7,
VK_EXSEL =>0xF8,
VK_EREOF =>0xF9,
VK_PLAY =>0xFA,
VK_ZOOM =>0xFB,
VK_NONAME =>0xFC,
VK_PA1 =>0xFD,
VK_OEM_CLEAR =>0xF
};
1;
__END__
=head1 NAME
Win32::Shortkeys::Kbh - Perl extension for blah blah blah
=head1 SYNOPSIS
use Win32::Shortkeys::Kbh;
....
=cut
example.pl in the module folder
use lib qw(./lib ./blib/arch/auto/Win32/Shortkeys/Kbh);
#use blib;
use strict;
use warnings;
use Time::HiRes qw(usleep);
use Win32::Shortkeys::Kbh qw(:hook :input VK_BACK VK_TAB);
set_key_processor(sub {
my ($cup, $code, $alt, $ext) = @_;
return unless $cup == 0;
print "process_key in perl cup: $cup code: $code alt: $alt ex
+t: $ext \n";
// F12 to leave the script
if ($code == 123) { unregister_hook(); }
// s: hitting s in notepad replace s with the string below
if ($code == 83) {
usleep(400_000);
send_cmd(1, VK_BACK);
send_string("You hit the s key !!!");
}
});
register_hook();
MsgLoop();
|