Community Documents

cancel
Showing results for 
Search instead for 
Did you mean: 

Simulated Keyboard Entries Using User32 DLL Functions

Overview

Demonstrates the use of VkKeyScanExA() to convert a string to virtual keys and use of SendInput() to synthesise that string being typed.

Description

This project shows how to use some of the functionality offered in the User32.dll that comes with Windows to simulate keyboard usage.  I extended the use of a couple of already existing forum posts to show how to convert a string to an array of virtual keys that can then be used to simulate keyboard usage.

Steps to Implement or Execute Code

  1. Run the VI
  2. Type a string into the String Input control.
  3. Click the "Recreate String" Boolean control.

Requirements

Software: Developed on Windows 7

               LabVIEW 2013 SP1 or later.

Hardware: N/A

Language: English

Last update: Nov 8, 2016

Potential issues:

  - DLL Path is wrong.  It is located here: C:\Windows\System32\user32.dll

Resources:

1) Interfacting with SendInput(): http://forums.ni.com/t5/LabVIEW/Does-LabView-have-a-function-similar-to-SendInput-from-C/td-p/791463 had a post with a link to a library that showed how to interface with the SendInput() function: http://forums.ni.com/ni/attachments/ni/170/362104/1/Util.WinAPI_Tool.SendString.EXAMPLE.llb

2) Getting the Keyboard Layout: https://decibel.ni.com/content/docs/DOC-14903

Comments
Member dadreamer
Member

This is not working properly in LabVIEW 64-bit because of aligning issues. As you maybe know, on Windows 64-bit memory data is aligned naturally up to 8-byte boundaries. So if you pass a byte array to SendInput, you have to insert the padding bytes to the cluster in order to get the function working. Based on that the INPUT structure should look like this:

 

typedef struct tagINPUT {
  DWORD type;
  DWORD Padding;
  union {
    MOUSEINPUT    mi;
    KEYBDINPUT    ki;
    HARDWAREINPUT hi;
  };
} INPUT, *PINPUT;

The KEYBDINPUT structure should be like this:

typedef struct tagKEYBDINPUT {
  WORD      wVk;
  WORD      wScan;
  DWORD     dwFlags;
  DWORD     time;
  DWORD     Padding; ULONG_PTR dwExtraInfo; } KEYBDINPUT, *PKEYBDINPUT;

The MOUSEINPUT structure:

typedef struct tagMOUSEINPUT {
  LONG      dx;
  LONG      dy;
  DWORD     mouseData;
  DWORD     dwFlags;
  DWORD     time;
  DWORD     Padding;
  ULONG_PTR dwExtraInfo;
} MOUSEINPUT, *PMOUSEINPUT;

And HARDWAREINPUT structure remains unchanged. Also no padding is needed for the INPUT structure in this case.

Now you may count an overall size of the INPUT struct for all three variants:

- using MOUSEINPUT: 40 bytes

- using KEYBDINPUT: 24 bytes

- using HARDWAREINPUT: 12 bytes

Well, if you do these changes to your code, you get it working. But as to me it's easier to use native LabVIEW clusters as structures. When passing some cluster to the external library, LabVIEW takes the alignment into the account and adds padding bytes automatically. So, you might get something like this:

 

2018-03-06_18-17-41.jpg

In this example I move the mouse to the specific coords and do a single left-button click. The cluster array is passed to SendInput as "Adapt To Type" -> "Array Data Pointer". Works fine in LabVIEW 2017 64-bit. Cat Happy

 

Member dadreamer
Member

@dadreamer wrote:
- using KEYBDINPUT: 24 bytes

 My mistake, here should be 32 bytes: 4+4+24. The rest is okay. By the way, my INPUT cluster is like this:

 

2018-03-07_12-53-35.jpg

Member dadreamer
Member

I have found one purely LabVIEW-specific bug in this example. When I enter some string in "Input control" with line breaks (done by main Enter key), the program reenters only the symbols before the very first line break, truncating the final string. Say, I entered this text:

One

Two

Three

In "Update Here" control I get just

One

I see that String to Virtual Key Array VI works fine and outputs Enter as 525. Then in Generate SendInput Data VI it splits into 2 (Control keycode) and 13 (Enter keycode). The VI passes Control into next VIs, not taking any actions here, but it's not always correct when using LabVIEW string controls. There's a special option in LabVIEW settings, which governs the behavior of the string control, when Enter key is pressed. It's called End text entry with Enter key and located at Environment page of Options window. When this checkbox is unchecked, a single Enter press moves the cursor to the next line, but Ctrl+Enter press ends the input. When the checkbox is checked, it works all the way around. So, this use case should be considered in the code, when using LV string control as a keys receiver for SendInput function. Well, I know that the option described corresponds to returnKeyAction string in LabVIEW.ini file (checked - True, unchecked - not present or False). So, I suggest using this private property to obtain the token from LabVIEW.ini:

Config_Get_Settings.png

Maybe there exists another way to do this. Of course, you may limit the string input to a single line, but we want an universal solution, don't we? Smiley Happy

So, when the keycode contains both Control and Enter keycodes, we check returnKeyAction parameter. If it's not found or False, we output zero instead of Control code. If it's present, we output Control code as usual.

2018-03-16_15-39-27.jpg

Member jpshaw
Member

I'm using Labview 2016.  I can't find the config.get settings method using the invoke node.  Can someone tell me how to get to the config.get settings method?

 

Thanks.

Member dadreamer
Member

@jpshaw wrote:
I can't find the config.get settings method using the invoke node.  Can someone tell me how to get to the config.get settings method?

I have posted a snippet with this property above, so you may save it and drag'n'drop to your block diagram. Or you may get access to a number of private properties by adding SuperSecretPrivateSpecialStuff=True to your LabVIEW.ini file. But be careful with it, because the most of them are neither stable, nor documented. You should play with it at your own risk.

Contributors