dllCall to call getkeyboardlayout

Started by archimede, June 10, 2022, 04:07:06 PM

Previous topic - Next topic

archimede

Hallo.
Sorry for my ignorance.
I would to use the API function getkeyboardlayout to detect the keyboard active name, but I am no able to define the right dllCall(...): can you tell me anything about?
Thank you very much.

stanl

I am sure Tony or Chuck will get to the root of the request. Not sure how WB would do it, but in Powershell, simple one-liners




(Get-Culture).keyboardLayoutID
(Get-Culture).Name



give results. But if you have multiple physical keyboards I'm not sure they become active until key pressed which makes any code hindsight.  You really have to explain more about your layout and goals,

archimede

Hallo.
I think WinBatch can do it because I saw many API functions call ( for example the getasynckeystate API function call ); I am no able to make it... :-(
I have many keyborads connected to the computer; I need to distinguish when a key is pressed to assign that key to a different opened window.
I saw the getasynckeystate API function call to detect thhe key pressed; I need to identify the keyboard that generates the character: it is possible by getkeyboardlayout API function call, but I am no able to define that WinBatch API call...
Does anyone have a good idea to write that API call?
Thank you very much.

stanl


you might want to check out


https://docs.microsoft.com/en-us/dotnet/api/system.globalization.cultureinfo?view=netframework-4.7.2


Tony helped me with a CLR script in 2019. If you can detect  a key, WB's CLR can detect the keyboard. There is also a built in assumption that you already have code to activate a specific window to send the key to [as you wrote: I need to distinguish when a key is pressed to assign that key to a different opened window]


So, help me/us out here:



       
  • 3 keyboards: kb1,kb2,kb3
  • 5 open windows win1...win5
You press alt-{F7} on kb2 and you want your WB code to

       
  • recognize kb2 as the keyboard
  • assign the key [whatever that means] to win4


or;


Your code wants to SendKeys() alt-{F7} but make it appear to be generated by kb2 going to win4.




Either way, sounds like a bit of a challenge. Good luck.

ChuckC


archimede

It returns the active keyboard id.
When received a character is possible to test the active keyboard id to identify what keyboard generates that character.
Then if is possible to generate a WinBatch call to that API function I think is possible to identify the right keyboard.
Do you know how define that API function call in WinBatch?

archimede

I think the API function
GetKeyboardLayoutName(...)
can be used too (or better) but my problem is the same: I am not able to define that API function call in WinBatch.
:-(

archimede

I think the API function
GetKeyboardLayoutList(...)
could be very useful but the problem is always the same...
:-(

ChuckC

As I said, I don't see GetKeyboardLayout() or *any* of the related GetKeyboard*() functions are going to do what you are trying to achieve.

GetKeyboardLayout function (winuser.h)
Article
10/13/2021
2 minutes to read


Retrieves the active input locale identifier (formerly called the keyboard layout).


The "active input local identifier" does not identify the specific piece of keyboard hardware that is generating input.


The documented return value for the function:

Return value
Type: HKL

The return value is the input locale identifier for the thread. The low word contains a Language Identifier for the input language and the high word contains a device handle to the physical layout of the keyboard.


Pay careful attention to what the documentation for the function says that the function returns.


You will likely need to investigate these particular functions, as they pertain to identifying raw input devices and allow you distinguish between keyboard and non-keyboard devices.

https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getrawinputdevicelist

https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-rawinputdevicelist

https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getregisteredrawinputdevices

https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerrawinputdevices

https://docs.microsoft.com/en-us/windows/win32/inputdev/raw-input

https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-input


Even after reading thru the raw input related functions, there is still the issue of whether a WinBatch can receive & process the raw input events and make them available with your script for subsequent use.


archimede

Thank you very much.
I red all; I begin to understand (I hope).
I red the API function
GetKeyboardState(...)
copies the state of all keys of one keyboard: is it possible to copy the state of all keys of all keyboards and then see what key is pressed, then identify the keyboard?
I think an API call is quick... or not?
About the dll call in WinBatch:
the bigger problem it seem to convert the API data format (for example HKL, PBYTR) to WinBatch dll call data type: can you give me a detailed table about it?
Thank you for al

archimede

The device handle to the physical layout of the keyboard I think can be used to identify the keyboard that generates the last character... or not?

stanl

Quote from: archimede on June 12, 2022, 04:29:27 PM
The device handle to the physical layout of the keyboard I think can be used to identify the keyboard that generates the last character... or not?


see https://docs.microsoft.com/en-us/dotnet/api/system.globalization.cultureinfo.keyboardlayoutid?view=netframework-4.7.2


I believe the CLR can retrieve this.

ChuckC

Stop focusing on keyboard layouts, period.  They have nothing to do with identifying how many keyboards are attached and which keyboard is responsible for generating a keystroke.  That conclusion is 100% obvious from simply reading the Win32 API function documentation.  Wishing that a keyboard layout would somehow automagically achieve the desired goal is going to be about as effective as trying paint a brick house with a running shoe and a bucket of axle grease.



stanl

Quote from: ChuckC on June 13, 2022, 06:48:15 AM
Stop focusing on keyboard layouts, period. 


You are correct, sir. I'm getting a little key bored.

archimede

Ok.
And what is the right way? The keyboard name? Or what?

ChuckC

Refer back to the links I posted regarding "raw input".  That is one starting point.  Those Win32 API functions deal directly with interacting with input devices at a lower level than just receiving a high-level Windows message indicating that a keypress occurred.  The provide capabilities such as the following:

1)  Enumerating the HID [Human Interface Device] capable input devices that exist, with each device identified as a being a keyboard, a mouse or "other non-keyboard / non-mouse" device.

2)  Registering to receive raw input messages from one or more devices.  Refer to step #1 to identify all keyboard devices that are attached.

3)  Receive raw input messages from the registered raw in put devices.

4)  Process/decode/translate the raw input messages into a form that identifies the key pressed and the device on which the key press occurred.

Please note that even if you connect all the "plumbing" correctly in a C/C++ program, there is no guarantee that a WinBatch script will be able to successfully do the same.  That will all depend on whether the message processing loop that WinBatch operates will intercept the raw input messages of it it will allow your script code to receive them.  That aspect of WinBatch is something that Tony will be able to address in a more concise and informative manner.

Provided all of that works, then you still have to code your script to send simulated keystrokes to other applications.


Another approach is to take a look at the Win32 API functions and online documentation that I posted links for in your original thread named "Keyboard(s) detect".  Those links included some C/C++ code examples that appear to perform part of what you are trying to achieve.

What you're trying to do isn't impossible, but it's non-trivial in terms of the type of code you need to write and the particular programming language that most readily lends itself to such a task, e.g. C/C++.

td

If it were me, I'd download the WIL extender SDK, fire up my favorite C++ development environment, and use the latest WIL Extender template example to try to implement a solution. However, that is probably not a task easily performed by anyone without the appropriate background in C++, Wn32 SDK, OOP, and generic programming using templates. 
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

archimede

Thank you very much ChuckC.
I no know  C++; I was able to write C program, but I have no a Windows background, then I am no able to write a program for system control.
For that (not only for that) I prefer WinBatch.
It seem (for me) some API function allows to do what you told: is it right?
:-)

archimede

I try this API test:
hBuffer = BinaryAlloc ( 257 )
sTest = DllCall ( DirWindows ( 1 ) : "user32.dll", long:"GetKeyboardLayoutList", long:257, lpbinary:hBuffer )
BinaryFree ( hBuffer )
It seem works well ( the return value is sTest == 3 ) but If I try to see hBuffer I see only 000000000 and no other: do you know why? I'm sure it is a my error...
:-|

archimede

I'm sorry...
The problem is I no see anything into the hBuffer... why?
The return value is 3 then it seem the API call is correct and the value seem correct...
:-(

archimede

I undertood the layout name is no the keyboard name; I am only trying to use API functions.
Is there an API function that give the keyboard name (instead the layout name)?

ChuckC

The binary buffer allocation and DllCall() usage don't match what GetKeyboardLayoutList() is expecting for input parameter values.  Unless you feel compelled to keep gnawing on the keyboard layout bone, I'd suggest moving on to a more pertinent & applicable API functions as previously noted.

Getting the name of a device is, again, a non-trivial exercise.  If performing that exercise via API functions is what you're aiming for, you'll find yourself working with a rather convoluted & torturous set of functions related to device installation & setup.

An alternative to the device installation & setup API functions would be to read the registry.  Use REGEDIT.EXE to browse to the key "Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\HID" and then look under each child key and go one level deeper to a subkey and then look for a string value named "Service".  If the value for "Service" is "kbdhid", then you've identified a keyboard device, and the value named "DeviceDesc" will help you get a human-readable description.

Outside of that, you are simply going to have to "level up" your skills and knowledge and learn some C/C++ programming and the Win32 API to the point where you develop sufficient experience to utilize the Win32 API functions needed to achieve your goal.


ChuckC

If you need a more productive & suitable function to work with for learning how to utilize DllCall() in WinBatch, let's go back to the following:

https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getrawinputdevicelist

GetRawInputDeviceList() has a very straight forward [for Win32 API functions] set of parameters and return value that lend themselves nicely to the usage of the binary buffer functions and DllCall() function in WinBatch.


archimede

Hallo.
I followed you and I red into the register: I found all like you told, but if I unconnect a keyboard and re-read the register, I read the OLD keyboards list with the old unconnected keyboard name... :-(.
The other idea is interesting ( GetRawInputDeviceList (...)) but the problem is, like everytime, how define data types and how read data generated: can you tell me anything about?
After that, when I read the list of all keyboard name, I need to identify what keyboard generates a character...

archimede

It seem that the API function
GetRawInputBuffer (...)
can be what I search; the problem, no very small, is how to define data type and how to read data generated...
:-|

ChuckC

This is the documentation for GetRawInputDeviceList():

https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getrawinputdevicelist

On that page, the signature of the function is given along with the input/output parameters and the return value.

The very first named parameter, pRawInputDeviceList is defined as having the type "PRAWINPUTDEVICELIST" and is noted that it is an array of RAWINPUTDEVICELIST structures.  The address of the zeroth element of an array is what the value of pRawInputEviceList effectively is.  In Win32 API parlance, placing a "p" in front of a variable name is Hungarian Notation for "this thing is a pointer to something".  Likewise, placing a "P" in front of a type name indicates that the type starting with "P" is a "pointer to type".

***  Ensure that you understand how arrays work in C/C++.  Then ensure that you understand how structures work, followed by understanding how an array of structures works.  I'm not going to explain that part to you as it's a fundamental thing you've simply got to achieve an understanding of on your own and there's more "introduction to programming" resources on the 'Net than and I can name, number or point out to you.  Google is your friend... the kind of friend you are going to rely on quite heavily.

Note that the text in the parameter description that starts with "An array of RAWINPUTDEVICELIST structures..." has "RAWINPUTDEVICELIST" highlighted as a hyperlink.  Click on the link.  It takes you to a page that describes the definition of that structure type.  Refer back to the previous note about ensuring that you understand how arrays of structures work.

Note the 2nd & 3rd parameters types of PUINT and UINT.  Anytime you see a pointer passed in to a Win32 API function, especially when it's a pointer to an integer type, it typically means that the function *MAY* modify the value of the integer who's address was passed in as a pointer.  Again, brush up on usage of pointers as function parameters in C/C++.


The docs for GetRawInputDeviceList() provide a nice little example of C/C++ code showing how to setup & perform the function call.  In my opinion, that code readily converts to WinBatch script code once you have sufficient understanding of both C/C++ and WinBatch's binary buffer & DllCall() functions.

td

Per as-usual, you have gone above and beyond, Chuck. Nice explanations.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

archimede

Hallo, ChuckC.
Thank you for your explanation.
I am an hardware devoleper, not a software developer, then I have many limits on sw understanding.
I know what is a pointer (I programmed on C language), but I don't know what is a pointer in WinBatch (I never used pointer on WinBatch because I think is no very useful in this language and there are no many functions based on pointer): the first argument is a pointer to a structure; my questions are:
- what is a pointer on WinBatch? What operations must I build to access a data by a pointer on WinBatch?
- how is defined a structure, at data level? what operations must must I build to access to every single data on a structure? And if I have (only) a pointer to that structure? How can I distiguish between every data into a pointed structure?
- I think it is a very interesting argument but I am very ignorant about it, and for me is the first time (on many year) I must face it: if you like, I am very happy to learn by you that things, otherwise I think is much simple if you write some instructions to convert data from C++ standard to WinBatch standard.
- How I can understand how a structure is organized and how I can distinguish between different data on a pointed structure?
- To read a pointed value on WinBatch can I use a similar program line:
sData = *pPointedData
or must I make different operations?
Thank you very much

archimede

I think you have reason about the API function GetRawInputDeviceList() example, but my problem is how to convert that example on a WinBatch example...
:-(

ChuckC

Most of your questions about "How do I do <fill-in-the-blank> in WinBatch?" are things you can answer for yourself by reading the docs for WinBatch and WIL [Windows Interface Language], as well as spending some time learning how to utilize the "Tech Database" to search for knowledgebase articles about a *huge* number of things you can do with WinBatch.  This includes finding WinBatch code examples for DllCall(), binary buffer management, making Win32 API function calls, using pointers in WinBatch, etc.  There are articles in there demonstrating how to make API calls that have a pointer to a structure as a parameter, too.  When you do find an article that demonstrates usage of Win32 API functions via DllCall(), go to Microsoft's online Win32 API documentation and compare that function's reference docs with the docs for the function(s) that you're interested in using.  Look for similarities in the types of parameters that are passed to the function and whether they are input-only, output-only or combination input-output parameters.  When you find similarities in things like the data type of the parameter, whether it's an integral type or structure type, whether it's single instance or an array, etc., learn how to take what you see in one example and adapt it to what you are trying to do with a different function that has a signature that is similar.

In most cases, before you post a "how to" question here, you should search the tech support database to see if the question you have has been previously asked & answered.  Over time, the tech support database has grown and been populated with the results of what's been asked & answered in the support forums.  In some cases, it may be a complete working script you find that has some functionality in it that you'd like to borrow.  In other cases, it may simply be demonstrating usage of a specific API function or something more general like how to utilize WMI or .NET hosting.

Again, it doesn't make a difference if you primarily work with hardware, or have limited experience with writing software, or have spent a lifetime working in some other career and have picked up using WinBatch in a post-retirement hobby, you're still going to do some learning.  Don't balk at that prospect and shy away from it.  Begin tackling it piece by piece.  In order to learn what you need to know to be successful in what you directly want to achieve, you may well have to take a detour engaging in learning exercises until you're able to go forward with your primary scripting project.  If you can't/won't learn what you need to learn, then your chances of success are zero.

Taking the approach of posting "I want to do this thing... Somebody please just write a script to do it and give it to me." won't get you anywhere.  All but one of the people who answer questions here are doing so for free in their spare time outside of their daily work as software developers/engineers or WinBatch hobbyists.  AFAIK, only one person on here is paid to be here, and they happen to be the owner of the company that produces WinBatch & related products.

Take the approach of "Here's some WinBatch script code that I wrote and attempted to execute.  It's not working, and I'm getting the following error...  Does anybody have any suggestions about what I might be doing wrong at this point in my script?"  And be sure to post the code with applicable comments related to where it's failing.  Generic complaints of "I tried using DllCall() with function XYZ() and it didn't work.  Why doesn't it work?" most likely won't garner any meaningful attention and likely won't get a helpful answer.


archimede

:-)
It seem the API function
GetRawInputBuffer (...)
can be much useful for me.
I red (and normally I continue to read) WinBatch manual, and about DllCall(...) but I no found anything about how to access to a structure generated by an API call: can you give me some link about it? I would be very happy to understand it.
I red many WinBatch manuals, but I no found anything about how to access a pointed data generates by an API call: can you give me some link about?
I searched many informations on the WinBatch forum by search tool (searching the API functions to understanding how other person implemented that) but I no found anything about.
I searched by Google the API function name called by WinBatch but I no found anything about.
You tell "There are articles in there demonstrating how to make API calls that have a pointer to a structure as a parameter, too": nice... I no found anything about; can you give me any link about?
Thank you very much

ChuckC

Did you go to the following:

https://techsupt.winbatch.com/webcgi/webbatch.exe?techsupt/nftechsrch.web+~+TechHome

And did you try searching for some applicable on-topic keyword such as:

win32 api dllcall

???

That alone brings up all kinds of useful results.

The article named "Using DllStructAlloc": https://techsupt.winbatch.com/webcgi/webbatch.exe?techsupt/nftechsupt.web+WinBatch/DllCall~Information+DllStructAlloc~Example.txt would be a good place to start.

Or, how about this one?

"How To Call Windows API Functions":  https://techsupt.winbatch.com/webcgi/webbatch.exe?techsupt/nftechsupt.web+WinBatch/How~To+How~To~Call~Windows~API~Functions.txt

Or this one?

"Account Name Associated with a Process": https://techsupt.winbatch.com/webcgi/webbatch.exe?techsupt/nftechsupt.web+WinBatch/Samples~from~Users+Account~Name~Associated~with~a~Process.txt


At this point, you have homework to do.  A lot of it.


archimede

Thank you very much.
I will read all.
I tried this, not work: can you tell me why?
hBuffer = BinaryAlloc ( 0 )
pcbSize = 1024
sTest = DllCall ( DirWindows ( 1 ) : "user32.dll", long:"GetRawInputBuffer", lpbinary:hBuffer, long:pcbSize, long:256 )
BinaryFree ( hBuffer )
I know there is a coarse error... :-(

archimede

:-)
I looked (no red) the first link: it seem very interesting.
I will read all...
:-)

ChuckC

https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getrawinputbuffer

What do you think "hBuffer = BinaryAlloc ( 0 )" was supposed to do?  Hint... read the documentation [see link above] for GetRawInputBuffer() and ask yourself if passing "lpbinary:hBuffer" result in passing a valid value to the function.

Likewise, "pcbSize = 1024" is wrong.

And, what exactly is the reason for choosing GetRawInputBuffer() to begin with?  If you haven't already successfully made the function calls required to to register for raw input from a specific set of devices, and the application hasn't received a very specific WM_INPUT message flagged to indicate that raw input messages need to be processed, then GetRawInputBuffer() ***IS NEVER GOING TO WORK, PERIOD***  If that much isn't obvious from reading the documentation for the function, then you have some kind of fundamental disconnect occurring with your analysis & problem solving approach that needs to be resolved.


You really need to step back from your original goal and learn to do some Win32 programming.  Without that knowledge and experience, you simply are not going to have any chance of success at getting a WinBatch script to do what you want it to do.  The most simple basic C/C++ examples of what you would need to do, which is a non-trivial task to begin with, are going to translate to WinBatch scripting code that is larger, more complex and less understandable than the original C/C++ code.

There is a free community edition of Visual Studio, which includes the Visual C/C++ compiler.  Get it.  Learn to use it.  Install the Windows SDK.  Search Google for "Introduction to Windows Programming with C++".  Find sample projects.  Download them, build them and run them.  Spend some time stepping thru them with the debugger.  Learn how the message processing loop works and how messages get processed by a program.