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
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
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:
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.
@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:
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:
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?
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.
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.
@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.