Skip to content

Instantly share code, notes, and snippets.

@Noitidart
Last active August 29, 2015 14:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Noitidart/0de3be2442a0295eb386 to your computer and use it in GitHub Desktop.
Save Noitidart/0de3be2442a0295eb386 to your computer and use it in GitHub Desktop.
_ff-addon-snippet-SendInput - js-ctypes to send keys winapi.
Cu.import('resource://gre/modules/ctypes.jsm');
var user32 = ctypes.open('user32');
if (ctypes.voidptr_t.size == 4 /* 32-bit */) {
var is64bit = false;
} else if (ctypes.voidptr_t.size == 8 /* 64-bit */) {
var is64bit = true;
} else {
throw new Error('huh??? not 32 or 64 bit?!?!');
}
var wantUnicode = true;
// types
// def long
var LONG = ctypes.long;
// def bool
var BOOL = ctypes.bool;
// def int
var INT = ctypes.int;
// def word
var WORD = ctypes.unsigned_short;
// def dword
var DWORD = ctypes.unsigned_long;
// def hwnd
var PVOID = ctypes.voidptr_t;
var HANDLE = PVOID;
var HWND = HANDLE;
// def uint
var UINT = ctypes.unsigned_int;
// def wparam
var UINT_PTR = is64bit ? ctypes.uint64_t : ctypes.unsigned_int;
var WPARAM = UINT_PTR;
// def lparam
var LONG_PTR = is64bit ? ctypes.int64_t : ctypes.long;
var LPARAM = LONG_PTR;
// def ulong_ptr
var ULONG_PTR = is64bit ? ctypes.uint64_t : ctypes.unsigned_long;
// structures
var MOUSEINPUT = ctypes.StructType('tagMOUSEINPUT', [
{ 'dx': LONG },
{ 'dy': LONG },
{ 'mouseData': DWORD },
{ 'dwFlags': DWORD },
{ 'time': ULONG_PTR },
{ 'dwExtraInfo': DWORD }
]);
var KEYBDINPUT = ctypes.StructType('tagKEYBDINPUT', [
{ 'wVk': WORD },
{ 'wScan': WORD },
{ 'dwFlags': DWORD },
{ 'time': DWORD },
{ 'dwExtraInfo': ULONG_PTR },
{ 'padding0': ctypes.uint8_t.array((MOUSEINPUT.size) - (WORD.size + WORD.size + DWORD.size + DWORD.size + ULONG_PTR.size) ) } // i need dum1 and dum2 due to: http://stackoverflow.com/questions/27972667/what-are-these-extra-parameters-when-calling-sendinput-via-win32api#comment44338874_27972900
]);
var INPUT = ctypes.StructType('tagINPUT', [
{ 'type': DWORD },
{ 'ki': KEYBDINPUT } // union, pick which one you want, we want keyboard input
]);
// constatns
var INPUT_KEYBOARD = 1;
var KEYEVENTF_EXTENDEDKEY = 0x0001;
var KEYEVENTF_KEYUP = 0x0002;
var KEYEVENTF_UNICODE = 0x0004;
var KEYEVENTF_SCANCODE = 0x0008;
var LPINPUT = INPUT.ptr; // pointer to first element of array of INPUTs
var SendInput = user32.declare('SendInput', ctypes.winapi_abi, UINT, UINT, LPINPUT, INT);
var keybdPadding = ctypes.uint8_t.array(KEYBDINPUT.fields[5].padding0.size)();
var js_pInputs = [
INPUT(INPUT_KEYBOARD, KEYBDINPUT(0x54, 0, 0, 0, 0, keybdPadding)), // vk codes: https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731%28v=vs.85%29.aspx
//INPUT(INPUT_KEYBOARD, KEYBDINPUT(0x54, 0, KEYEVENTF_KEYUP, 0, 0, keybdPadding)) // im not sure if we need to send a key up, it works though, this types a lower case t
];
var pInputs = INPUT.array()(js_pInputs);
console.info(pInputs.length, pInputs.toString(), INPUT.size)
var rez_SI = SendInput(pInputs.length, pInputst, INPUT.size);
console.log('rez_SI:', rez_SI.toString());
if (parseInt(rez_SI.toString()) != pInputs.length) {
console.error('not all key events were sent, win api error:', ctypes.winLastError);
}
user32.close();
@Noitidart
Copy link
Author

README

Rev1

  • Works, sends lower case "t", click run, if you hit ctrl+r from scarchpad, it will type t but ctrl is pressed so you wont see it type

  • Learned from here that union of different sized elements requires us to take the size of the biggest element, due to that stackoverflow linked topic

    20:54 noida   also arai a question about union pplease
    20:54 noida   http://stackoverflow.com/questions/27972667/what-are-these-extra-parameters-when-calling-sendinput-via-win32api#comment44338874_27972900
    20:55 noida   is it true that if a union between two structures, one with size 32 and other with size 20
    20:55 noida   if im having to use size of 20 struct, i have to add 12 padding?
    20:55 noida   i actually had to do it cuz it wouldnt work otherwise
    20:55 noida   but just wondeirng if thats true
    21:01 arai    if you're going to pass 2 or more INPUTs, you should allocate in proper size
    21:01 arai    so 2nd element's position will be correct
    21:01 arai    if the UNION is 32bytes, you'll need to add padding
    21:03 arai    it might be better to declare those 3 structs, and use largest struct for allocating, and cast each element for assignment
    21:04 noida   ahhh
    21:04 noida   super idea!!
    21:04 noida   when you cast it, it makes the rest of it 0's? i didnt know that, super cool
    21:05 arai    I don't remember about initializing
    21:05 arai    but those area shouldn't be accessed while reading
    21:06 arai    https://pastebin.mozilla.org/8838334
    21:07 arai    seems to be zero-ed
    21:07 noida   woweee
    21:07 noida   thx man so cool!
    
    js> S = ctypes.StructType('S', [ { 'X': ctypes.int32_t }, { 'Y': ctypes.int32_t } ]);
    js> S.array(10)()
    S.array(10)([{"X": 0, "Y": 0}, {"X": 0, "Y": 0}, {"X": 0, "Y": 0}, {"X": 0, "Y": 0}, {"X": 0, "Y": 0}, {"X": 0, "Y": 0}, {"X": 0, "Y": 0}, {"X": 0, "Y": 0}, {"X": 0, "Y": 0}, {"X": 0, "Y": 0}])
    
  • And passing array means first element of array, see chat with @arai 😄

    20:24 noida   hey arai man question plz
    20:24 noida   https://msdn.microsoft.com/en-us/library/windows/desktop/ms646310%28v=vs.85%29.aspx
    20:24 noida   is LPINPUT (2nd arg) a pointer to array of INPUT?
    20:25 noida   var SendInput = user32.declare('SendInput', ctypes.winapi_abi, UINT, UINT, INPUT.array(), INT);
    20:25 noida   or should i make it INPUT.array().ptr?
    20:29 arai    if the documentation says an array for pointer, it's a pointer to the first element
    20:30 noida   ahhh
    20:30 noida   cuz i got it working
    20:30 noida   but i dont know if its per docs or hacky
    20:30 noida   what i did was
    20:30 noida   var SendInput = user32.declare('SendInput', ctypes.winapi_abi, UINT, UINT, INPUT.array(), INT);
    20:30 noida   then i make array like this:
    20:31 noida   var js_pInputs = [     INPUT(INPUT_KEYBOARD, KEYBDINPUT(0x54, 0, 0, 0, 0, keybdPadding))   ]
    20:31 noida   var pInputs = INPUT.array()(js_pInputs);
    20:31 arai    so, the function receives the number of the array elements, and the pointer to the first element. those 2 arguments describe the array
    20:31 noida   var rez_SI = SendInput(pInputs.length, pInputs, INPUT.size);
    20:31 noida   and it works
    20:31 noida   but im not sure if its right 
    20:31 arai    it's correct
    20:31 noida   ah why though
    20:32 arai    "why"?
    20:32 noida   cuz in this cpp he passess address to input
    20:32 noida   https://github.com/sangww/eyecan/blob/4e5a7d49d1f5859003157eeb105c766b3bcf2ba9/src/windows/WinAction.cpp#L145
    20:32 arai    ah
    20:32 arai    so, if you're going to pass single element array, you don't need to declare array
    20:34 arai    allocating a INPUT equals to allocating an array with 1 INPUT
    20:34 arai    then, the pointer to the former equals to the latter array
    20:35 arai    https://pastebin.mozilla.org/8838330
    20:36 noida   im going to be passing more then 1
    20:36 arai    okay, so you'll need to allocate array
    20:36 noida   ah so pointer to first element is defined as INPUT.ptr
    20:36 noida   right?
    20:36 noida   so func declare should be
    20:36 arai    yes
    20:37 noida   var SendInput = user32.declare('SendInput', ctypes.winapi_abi, UINT, UINT, INPUT.ptr, INT);
    20:37 noida   and not
    20:37 noida   var SendInput = user32.declare('SendInput', ctypes.winapi_abi, UINT, UINT, INPUT.array(), INT);
    20:37 arai    they're same
    20:37 arai    .array is converted to .ptr
    20:37 noida   oh yea implicitly huh?
    20:37 arai    yes
    20:39 arai    https://dxr.mozilla.org/mozilla-central/source/js/src/ctypes/CTypes.cpp#6132
    20:42 noida   yayyy
    20:42 noida   thanks!!
    20:42 noida   now im true to the docs and have understanding!! 
    20:42 noida   var rez_SI = SendInput(pInputs.length, pInputs[0].address(), INPUT.size);
    20:42 noida   so i sent pointer to first element of aray 
    20:42 arai    oh
    20:43 arai    I guess you can pass pInputs there
    20:43 noida   is that due to implicit stuff?
    20:44 arai    it's a convention, I guess
    20:44 noida   o no way intersting it works!
    20:44 arai    "pInputs" means "&(pInputs[0])"
    20:44 arai    for array
    20:45 noida   ahh
    20:45 arai    in C, so js-ctypes follows it, I think
    20:46 noida   ah so thats the reason for them implicitly doing it (under the hood and not telling us in public)
    20:46 arai    yeah
    20:47 arai    not a "special" thing about js-ctypes
    
      INPUT Input; // allocating a INPUT
      &Input       // a pointer to the Input
    
      INPUT InputArray[1]: // allocating an array with single INPUT element
      InputArray   // a pointer to the first element of the InputArray 
    

    Rev2

  • Made gist a .js

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment