WinBatch® Technical Support Forum

All Things WinBatch => WinBatch => Topic started by: archimede on June 10, 2022, 04:07:06 PM

Title: dllCall to call getkeyboardlayout
Post by: archimede on June 10, 2022, 04:07:06 PM
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.
Title: Re: dllCall to call getkeyboardlayout
Post by: stanl on June 11, 2022, 12:59:59 PM
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,
Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 12, 2022, 03:04:08 AM
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.
Title: Re: dllCall to call getkeyboardlayout
Post by: stanl on June 12, 2022, 05:26:17 AM

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:


You press alt-{F7} on kb2 and you want your WB code to


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.
Title: Re: dllCall to call getkeyboardlayout
Post by: ChuckC on June 12, 2022, 10:30:50 AM
https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getkeyboardlayout

I don't see how GetKeyboardLayout() would be applicable to what you are trying to achieve.

Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 12, 2022, 02:54:07 PM
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?
Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 12, 2022, 03:02:03 PM
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.
:-(
Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 12, 2022, 03:18:38 PM
I think the API function
GetKeyboardLayoutList(...)
could be very useful but the problem is always the same...
:-(
Title: Re: dllCall to call getkeyboardlayout
Post by: ChuckC on June 12, 2022, 03:48:00 PM
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.

Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 12, 2022, 04:21:56 PM
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
Title: Re: dllCall to call getkeyboardlayout
Post by: 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?
Title: Re: dllCall to call getkeyboardlayout
Post by: stanl on June 13, 2022, 02:38:07 AM
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.
Title: Re: dllCall to call getkeyboardlayout
Post by: ChuckC on June 13, 2022, 06:48:15 AM
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.


Title: Re: dllCall to call getkeyboardlayout
Post by: stanl on June 13, 2022, 01:17:10 PM
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.
Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 13, 2022, 03:55:47 PM
Ok.
And what is the right way? The keyboard name? Or what?
Title: Re: dllCall to call getkeyboardlayout
Post by: ChuckC on June 13, 2022, 04:40:53 PM
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++.
Title: Re: dllCall to call getkeyboardlayout
Post by: td on June 13, 2022, 11:30:26 PM
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. 
Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 14, 2022, 01:33:23 AM
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?
:-)
Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 14, 2022, 01:56:23 AM
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...
:-|
Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 14, 2022, 02:00:52 AM
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...
:-(
Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 14, 2022, 02:34:50 AM
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)?
Title: Re: dllCall to call getkeyboardlayout
Post by: ChuckC on June 14, 2022, 07:46:23 AM
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.

Title: Re: dllCall to call getkeyboardlayout
Post by: ChuckC on June 14, 2022, 06:37:56 PM
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.

Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 15, 2022, 01:51:29 AM
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...
Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 15, 2022, 02:12:03 AM
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...
:-|
Title: Re: dllCall to call getkeyboardlayout
Post by: ChuckC on June 15, 2022, 05:56:45 AM
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.
Title: Re: dllCall to call getkeyboardlayout
Post by: td on June 15, 2022, 06:49:54 AM
Per as-usual, you have gone above and beyond, Chuck. Nice explanations.
Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 15, 2022, 07:45:13 AM
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
Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 15, 2022, 07:50:57 AM
I think you have reason about the API function GetRawInputDeviceList() example, but my problem is how to convert that example on a WinBatch example...
:-(
Title: Re: dllCall to call getkeyboardlayout
Post by: ChuckC on June 15, 2022, 08:38:44 AM
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.

Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 15, 2022, 09:07:28 AM
:-)
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
Title: Re: dllCall to call getkeyboardlayout
Post by: ChuckC on June 15, 2022, 09:43:51 AM
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 (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 (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 (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.

Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 15, 2022, 10:05:54 AM
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... :-(
Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 15, 2022, 10:09:15 AM
:-)
I looked (no red) the first link: it seem very interesting.
I will read all...
:-)
Title: Re: dllCall to call getkeyboardlayout
Post by: ChuckC on June 15, 2022, 10:53:55 AM
https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getrawinputbuffer (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.


Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 15, 2022, 02:45:32 PM
I am learning links you wrote me.
About your first question:
I would like pass the first argument like NULL, then BinaryAlloc (0) I think can define a null value: how I must define a NULL value to pass like argument to an API function?
Title: Re: dllCall to call getkeyboardlayout
Post by: ChuckC on June 15, 2022, 05:36:23 PM
Read the help topic for the DllCall() function in the Windows Interface Language reference documentation.  You will find an explicit statement made there regarding how to pass a "pointer to null", sometimes referred to as "long pointer to null" or "lpnull".

And, again, the function you chose isn't a place to start learning how to use DllCall().  Think of it this way... Compare your effort to that required to build a house.  The function you are starting with is akin to putting plywood down on the substructure of the roofing trusses before putting on the shingles.  The problem is, you haven't built the portion of the house that the roof is supported by.  Until you build the foundation and the ground floor of the house, it is pointless to attempt to put on portions of the roof.

Despite the fact that I keep pointing you at a very specific set of functions to utilize, you insist on going off in random directions and completely disregarding the instruction you've been provided with.

Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 17, 2022, 02:14:41 AM
Hallo.
I am following your suggestion.
I am studying all links you posted me.
I decided to change the Dll function to call to try: now I use GetRawInputDeviceList(...) with the simplest call (with the NULL pointer for the first argument).
I made this test:
hBuffer = BinaryAlloc ( 2 )
sTest = DllCall ( DirWindows ( 1 ) : "user32.dll", long:"GetRawInputDeviceList", lpnull, long:hBuffer, word:0 )
BinaryFree ( hBuffer )
The first argument lpnull is necessary to obtain the number of connected keyboards on the pointed var in the second argument
The second argument must be a pointer to a UINT, 2 bytes; then I use BinaryAlloc ( 2 ) to allocate memory for the pointer t a UINT value; then I assign that value to the second argument, lpbinary type because it is allocated by BinaryAlloc(...)
The third argument is 0 because it define The size of a RAWINPUTDEVICELIST structure, in bytes; that dimension is 0 because the pointer to it (first argument) is lpnull.
At the end the function call returns -1, error: can you help me to find the error?
Thank you very much
Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 17, 2022, 02:19:33 AM
Sorry.
The second line is:
sTest = DllCall ( DirWindows ( 1 ) : "user32.dll", long:"GetRawInputDeviceList", lpnull, lpbinary:hBuffer, word:0 )
with lpbinary type for the second agument
Title: Re: dllCall to call getkeyboardlayout
Post by: ChuckC on June 17, 2022, 04:55:29 AM
Here's another link to multiple C++ examples making use of GetRawInputDeviceList():

https://cpp.hotexamples.com/examples/-/-/GetRawInputDeviceList/cpp-getrawinputdevicelist-function-examples.html (https://cpp.hotexamples.com/examples/-/-/GetRawInputDeviceList/cpp-getrawinputdevicelist-function-examples.html)


Passing lpnull for the 1st parameter, pRawInputDeviceList, is correct when calling the function to find out how many raw input devices exist and for the purpose of determining how large of a [binary] buffer to allocate for the second call to the function to actually retrieve the list of raw input devices.

Look at the function documentation again and ask yourself how many bytes are used to represent a UINT.

Look at the function documentation again and ask yourself how many bytes represent a single instance of the RAWINPUTDEVICELIST structure.  Look at the example code in the function documentation and figure out what the "sizeof()" operator does.  Find the definition of RAWINPUTDEVICELIST and add up how many bytes it takes to represent all the fields in the structure.
Title: Re: dllCall to call getkeyboardlayout
Post by: stanl on June 17, 2022, 04:57:18 AM
Chuck has been very patient but you seem to waffle between listing the keyboard devices and detecting keys and assigning to a window. I think WMI can list your devices. I posted a reference to an app [WMIGen] that might assist you with your list if you need it and then you can concentrate on the keys.
Title: Re: dllCall to call getkeyboardlayout
Post by: ChuckC on June 17, 2022, 05:46:06 AM
WMI certainly can assist with identifying device names.  One of the problems, though, is that the handling of raw input has no connection to the actual device names... it's all achieved via handles to devices.  Part of what I'm trying to get firmly understood by anybody interested in the raw input issue is that the following must happen:

1)  All raw input devices must be enumerated, and what is returned by the API function is device handles and other meta data associated with each device.

2)  The enumerated results must then be analyzed to identify which handles are associated with keyboard devices vs. mouse devices vs. other types of HID devices that are neither keyboard nor mouse.

3)  The keyboard devices must then have their handles passed to an API function that registers the process to receive raw input messages from those devices.

4)  The program's message loop then needs to specifically detect when a WM_INPUT message is received that contains a special flag indicating that raw input messages follow it in the message queue.

5)  Another API function must then be called to receive the raw in put messages from the message queue.

6)  Multiple API functions then need to be called to decode the raw input messages to identify the key that was pressed which device handle it is associated with based on the particular keyboard device that generated the raw input.

7)  Finally, the keystroke & source keyboard device handle can be used to determine which target application a simulated keystroke will be sent to.


That is all non-trivial but doable, at least in a C/C++ application.

Perhaps Tony could fill in some detail about step #4.  If WinBatch lacks a method for allowing a script to have a callback invoked for specific messages being received by GetMessage(), then this entire exercise is pointless and irrelevant in terms of a solution that is based 100% on WinBatch.
Title: Re: dllCall to call getkeyboardlayout
Post by: td on June 17, 2022, 08:00:13 AM
It is possible to subclass a WinBatch window or alternatively, set up a window procedure hook (not sure if Windows will still let you set a hook because of all the security "enhancements".) Both techniques would require using the DllCallback function. However, the chances of getting that done correctly and reliably are in the winning-the-lottery area of probability for the average WinBatch user. That is why I mentioned using an extender many posts ago.

Chuck has done all the heavy lifting by laying out the equivalent of a design and implementation specifications. It is just a matter of someone wanting this bit of functionality enough to wiring it all together.
Title: Re: dllCall to call getkeyboardlayout
Post by: ChuckC on June 17, 2022, 08:39:14 AM
I agree, an extender is likely to be the best option to do the really dirty work in terms of capturing the raw input provided that C/C++ code in the extender can inform WinBatch that it wants to participate in the message processing.  From there, the extender could simply buffer the parsed raw input and return that via extender function calls made by the script via polling or possibly via some kind of event trigger where the script waits [with a timeout] on the event looking for non-timeout results indicating that extender has data for it.

It looks like registration for receiving raw input may also allow a process to get those messages without being in the foreground and having a window displayed that has focus.  Of course that would need to be tested, but if it works that way, it would allow for some interesting flexibility.

The last that i'm going to post [or re-post] on the subject are the following links which detail the core concepts related to receiving & processing raw input along with various code examples demonstrating the techniques:

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

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

https://cpp.hotexamples.com/examples/-/-/GetRawInputDeviceList/cpp-getrawinputdevicelist-function-examples.html (https://cpp.hotexamples.com/examples/-/-/GetRawInputDeviceList/cpp-getrawinputdevicelist-function-examples.html)

It doesn't get any more clear than what those pages present in the way of technical reference information, tutorials and examples.  No amount of spoon-feeding will do more than what those links provide, and if anybody won't or can't successfully utilize that information, then the task of receiving & processing raw keyboard input is simply going to beyond their skill.

Title: Re: dllCall to call getkeyboardlayout
Post by: stanl on June 17, 2022, 08:39:44 AM
Quote from: td on June 17, 2022, 08:00:13 AM
It is just a matter of someone wanting this bit of functionality enough to wiring it all together.


Which begs the question of the practical application for the Op's request.
all of which I suppose can be done, but seem really outside the scope of wanting to control in WB. Or maybe the goal is just to discover a WB way to handle raw input on multiple keyboards... then configure practical application -  cart/horse
Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 17, 2022, 09:40:02 AM
My case is the first:
many keyboards everyone connected to a different window.
Every keyboard is a remote control visible like a keyboard in the system.
Every time I press a key on a remote I want to send the equivalent character to a specific window.
Another way (I think) is to convert windows 7 to concurrent mutiuser, but I think it is very hard...
Title: Re: dllCall to call getkeyboardlayout
Post by: ChuckC on June 17, 2022, 09:53:39 AM
Sending simulated keystrokes does not work in a "locked" or "disconnected" Terminal Services session.  That precludes the use of the SendKey*() / SendMenu*() / Mouse*() functions under those conditions.
Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 17, 2022, 09:54:58 AM
I tried this:
hBuffer = BinaryAlloc ( 2 )
sTest = DllCall ( DirWindows ( 1 ) : "user32.dll", long:"GetRawInputDeviceList", lpnull, lpbinary:hBuffer, word:8 )
iTest = BinaryPeek2 ( hBuffer, 0 )
BinaryFree ( hBuffer )
It seem work because the return value sTest is 0 (instead -1); the iTest value is 10; if I unconnect a keyboard and retry, the iTest value is 7, then it seem work
The last argument I calculate 8 because the RAWINPUTDEVICELIST structure contain 2 elements:
- HANDLE hDevice (4 bytes)
- DWORD  dwType ( 4 bytes)
Is it right?
Do you know if there are a simplest way to calculate it?
Title: Re: dllCall to call getkeyboardlayout
Post by: ChuckC on June 17, 2022, 10:23:45 AM
DWORD and UINT are both 4 byte values.

HANDLE varies based on the architecture of the process that is executing.  For a 32-bit process, it is 4 bytes, while for a 64-bit process it is 8 bytes.  Depending on whether you are running a 32-bit or 64-bit build of WinBatch, your script code will have to adjust accordingly.
Title: Re: dllCall to call getkeyboardlayout
Post by: td on June 17, 2022, 01:20:20 PM
From the help file: DllCall supports a "long_ptr" integer value guaranteed to be the bit size of a memory address. That is, a 32-bit integer value when used with 32-bit WinBatch and a 64-bit value when used with 64-bit WinBatch.

This is the best choice when passing a "HANDLE" and several other data types that vary with the process's bitness.

Title: Re: dllCall to call getkeyboardlayout
Post by: ChuckC on June 17, 2022, 02:39:55 PM
Good point.  For passing a handle value as an individual parameter, that would be preferred way to do it.

When it comes to computing the site of a structure that contains a field of type HANDLE, would it be best just to test the "bitness" of WinBatch and add 4 or 8 to the structure size that's being computed?  I wasn't sure where "long_ptr" modifier for DllCall() could be shoe-horned into the arithmetic for structure size calculation.


Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 18, 2022, 02:31:22 AM
I made this test program that seem work on WIL 32 bit and WIL 64 bit:
WILBITNESS = 2 ** ( WinMetrics ( -2 ) + 3 )                          ; Num. Bit architettura questo programma
POINTERINT = 2                                                       ; Num. Bytes INT pointer
POINTERUINT = 2                                                      ; Num. Bytes UNSIGNED INT pointer
HANDLE32 = 4                                                         ; Num. Bytes HANDLE in WinBatch 32 bit
HANDLE64 = 8                                                         ; Num. Bytes HANDLE in WinBatch 64 bit
DWORD32 = 4                                                          ; Num. Bytes DWORD in WinBatch 32 bit
DWORD64 = 8                                                          ; Num. Bytes DWORD in WinBatch 64 bit
If WILBITNESS == 32                                                  ; Se questo programma è in architettura 32 bit

   HANDLE = HANDLE32                                                ; La lunghezza di un HANDLE è quella definita da HANDLE32
   DWORD = DWORD32                                                  ; La lunghezza di DWORD è quella definita da DWORD32

Else                                                                 ; Altrimenti significa che l'architettura di questo programma è
                                                                     ; a 64 bit, quindi:
   HANDLE = HANDLE64                                                ; La lunghezza di un HANDLE è quella definita da HANDLE64
   DWORD = DWORD64                                                  ; La lunghezza di DWORD è quella definita da DWORD64

EndIf                                                                ; If WILBITNESS == 32
hpuiNumDevicesBuffer = BinaryAlloc ( POINTERUINT )
iStructLen = HANDLE + DWORD
sTest = DllCall ( DirWindows ( 1 ) : "user32.dll", long:"GetRawInputDeviceList", lpnull, lpbinary:hpuiNumDevicesBuffer, word:iStructLen )
iTest = BinaryPeek2 ( hpuiNumDevicesBuffer, 0 )
BinaryFree ( hpuiNumDevicesBuffer )
Message ( "Test architettura", "Numero tastiere: " : iTest : " Architettura " : WILBITNESS : " bit" : " Lunghezza handle: " : HANDLE )
Title: Re: dllCall to call getkeyboardlayout
Post by: ChuckC on June 18, 2022, 05:40:52 AM
The size of an integer doesn't change with the difference between 32-bit and 64-bit architecture.

The size of a pointer to *anything* changes with the difference between 32-bit and 64-bit architecture.  Pointers are 4 bytes on 32-bit and 8 bytes on 64-bit.

an INT, UINT and a DWORD are *always* 4 bytes.

a WORD is always 2 bytes.

Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 18, 2022, 05:57:01 AM
Hallo.
You tell DWORD is ALWAYS 4 bytes.
When I compile 64 bit, if I put in the program
DWORD = DWORD64
the program works
if I put
DWORD = DWORD32
the program no works... why?
Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 18, 2022, 06:08:57 AM
I tryed this calls:
hpuiNumDevice = BinaryAlloc ( POINTERUINT )
iStructLen = HANDLE + DWORD

sTest = DllCall ( DirWindows ( 1 ) : "user32.dll", long:"GetRawInputDeviceList", lpnull, lpbinary:hpuiNumDevice, word:iStructLen )
iStructWidth = iStructLen * iStructCount
hpRawInputDeviceList = BinaryAlloc ( iStructWidth )
sTest = DllCall ( DirWindows ( 1 ) : "user32.dll", long:"GetRawInputDeviceList", lpbinary:hpRawInputDeviceList, lpbinary:hpuiNumDevice, word:iStructWidth )

BinaryFree ( hpRawInputDeviceList )
BinaryFree ( hpuiNumDevice )

The first DllCall works, the second DllCall no works ( return -1 ): can you help me to understand why?
Thank you very much
Title: Re: dllCall to call getkeyboardlayout
Post by: ChuckC on June 18, 2022, 07:37:57 AM
Use the exact data types and their architecture-specific sizes *EXACTLY* as they are defined in the function's signature as it is declared in the documentation.  If you deviate from doing so in any way, the function will fail.

This code is wrong:

iStructWidth = iStructLen * iStructCount
hpRawInputDeviceList = BinaryAlloc ( iStructWidth )

"iStructCount" is undefined and any calculations based on it will produce wrong results.


You are still not correctly understanding what you are doing with allocating a buffer and then passing the pointer to the buffer as a parameter to the function.  The function is going to write some data into that buffer.  There are *2* parameters that you are going to pass that are pointers to buffers.  One is the buffer that's going to receive one or more RAWINPUTDEVICELIST structures.  At the start you don't know how many of those structures you are going to receive, so you have to call the function twice.  The first call where you pass "lpnull" for the 1st parameter tells the function that you want it to tell you how many structures it will return.  The number of structures gets written so a UINT that is passed by address [e.g. Pointer to UINT] in the 2nd parameter.  Since you can't pass arbitrary numeric variables by a pointer in WinBatch, you have to allocate a binary buffer that is the size of a UINT [e.g. 4 bytes, same as a DWORD] and pass the buffer with "lpbinary:".  After the first call, provided that the function's return value indicates success, you read the 4-byte unsigned integer [UINT] value *from* that buffer and use it to figure out how much memory to allocate for the binary buffer that you will pass as the 1st parameter the second time you call the function.

UINT GetRawInputDeviceList(
  [out, optional] PRAWINPUTDEVICELIST pRawInputDeviceList,  // lpnull on 1st call, lpbinary:<buffer-variable-name> on 2nd call
  [in, out]       PUINT               puiNumDevices,  // lpbinary:<buffer-for-uint-variable-name>
  [in]            UINT                cbSize  //  just a normal WinBatch numeric variable with an integer value in it.
);


Make note of the return value of the function as defined in the online docs:

Return value
Type: UINT

If the function is successful, the return value is the number of devices stored in the buffer pointed to by pRawInputDeviceList.

On any other error, the function returns (UINT) -1 and GetLastError returns the error indication.


The definition of RAWINPUTDEVICELIST from the online docs:

typedef struct tagRAWINPUTDEVICELIST {
  HANDLE hDevice;  //  4 bytes on 32-bit, 8 bytes on 64-bit
  DWORD  dwType;  //  always 4 bytes
} RAWINPUTDEVICELIST, *PRAWINPUTDEVICELIST;

sizeof(RAWINPUTDEVICELIST) is 8 on 32-bit and 12 on 64-bit.

"*RAWINPUTDEVICELIST" means "Pointer to RAWINPUTDEVICELIST", the size of which varies only with the 32-bit/64-bit architecture, not with the size of RAWINPUTDEVICELIST itself.



The example code from the online docs:

UINT nDevices;
PRAWINPUTDEVICELIST pRawInputDeviceList = NULL;
while (true) {
    if (GetRawInputDeviceList(NULL, &nDevices, sizeof(RAWINPUTDEVICELIST)) != 0) { Error();}
    if (nDevices == 0) { break; }
    if ((pRawInputDeviceList = malloc(sizeof(RAWINPUTDEVICELIST) * nDevices)) == NULL) {Error();}
    nDevices = GetRawInputDeviceList(pRawInputDeviceList, &nDevices, sizeof(RAWINPUTDEVICELIST));
    if (nDevices == (UINT)-1) {
        if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { Error(); }
        // Devices were added.
        free(pRawInputDeviceList);
        continue;
    }
    break;
}
// do the job...
// after the job, free the RAWINPUTDEVICELIST
free(pRawInputDeviceList);


For now, ignore the fact that the function calls are made in a loop.  The usage of a looping construct is there to deal with the possibility that a device is added or removed between the 1st & 2nd function calls and allows the code to re-try without blowing up or returning inaccurate information.

The first call to the function is:

GetRawInputDeviceList(NULL, &nDevices, sizeof(RAWINPUTDEVICELIST)

With DllCall(), you'll pass "lpnull" for the 1st parameter.

The 2nd parameter will be passed with "lpbinary:" and the buffer will be 4 bytes in size.

The 3rd parameter will be the integer value 8 or 12 depending on the architecture, and it can be a literal numeric value or a variable set to that value.

The return value of the function will be -1 if it fails on the 1st call; otherwise it is successful.

You will need to learn to use the BinaryPeek*() functions to read a 4 byte unsigned integer from the buffer that you passed in as the 2nd parameter.  That will get you the "nDevices" [e.g. "number of devices"] value that you multiply by the size of RAWINPUTDEVICELIST to determine what size binary buffer to allocate and pass as the 1st parameter the 2nd time you call the function.

The 2nd time the function is called, it looks like this [taken from example code above]:

nDevices = GetRawInputDeviceList(pRawInputDeviceList, &nDevices, sizeof(RAWINPUTDEVICELIST));

In this case, when you do the same with DllCall(), "pRawInputDeviceList" will be replaced with your 1st binary buffer passed via "lpbinary:<buffer-name>".  The 2nd & 3rd parameters remain the same.

Note that the return value of the function can have 2 meanings this time.  If it returns -1, then the function failed.  Otherwise, the return value is the # of devices returned in the buffer that was passed at the 1st parameter.

Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 19, 2022, 03:06:40 AM
Hallo.
First all thank you very much for your help.
Second thing: I no understand a thing: how I must calculate the struct dimension?
This struct
typedef struct tagRAWINPUTDEVICELIST {
  HANDLE hDevice;
  DWORD  dwType;
} RAWINPUTDEVICELIST, *PRAWINPUTDEVICELIST;
it seem to contain 2 data:
- HANDLE hDevice
- DWORD dwType
but reading what you tell me it seem no like this...
This is my limit to no know c++.

Another thing: if WinBatch is "near" to C++, why no generate a conversion WinBatch -> C++ to compilate it ( really, no semicompilation ): I understant it would be an hard work, but I think it would be very very useful...
:-)
Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 19, 2022, 03:20:15 AM
Sorry I was no enough clear.
The API function GetRawInputDeviceList ( ... )
requires, on the third argument, "The size of a RAWINPUTDEVICELIST structure, in bytes".
I think the struct dimension is the sum of all data dimension contents.
Then the struct
typedef struct tagRAWINPUTDEVICELIST {
  HANDLE hDevice;
  DWORD  dwType;
} RAWINPUTDEVICELIST, *PRAWINPUTDEVICELIST;
seem to contain 2 data:
- HANDLE hDevice
- DWORD dwType
total 12 bytes, like you tell.
with WinBatch 64 bitness,  if I put, on the third argument of GetRawInputDeviceList ( ... ), 12 it generates error; if I put 16 it works: why?
with WinBatch 32 bitness,  if I put, on the third argument of GetRawInputDeviceList ( ... ), 8 it works: why?
Title: Re: dllCall to call getkeyboardlayout
Post by: ChuckC on June 19, 2022, 06:07:11 AM
Yes, you finally figured out the process of computing the size of a structure.  It is nothing more than the sum of the memory sizes of all the fields in the structure.  Doing this in C++ is exactly the same as in C.

Regarding 16 bytes vs. 12 bytes for the size of the RAWINPUTDEVICELIST in a 64-bit program, you have likely encountered one of the "quirks" of how code is actually generated to run on 64-bit Windows vs. 32-bit Windows.  Specifically, note the types of the fields in the structure and the *order* in which they appear.  They are, respectively, a HANDLE field followed by a DWORD field.  On 64-bit architecture, the concept of "memory alignment" becomes more important since the "native word size" is 8 bytes rather than the 4 byte native word size for 32-bit Windows.  In practice, this means that the compiler will insert padding bytes into a structure to ensure that it has an overall size that is a multiple of 8 bytes.

The *best* way to figure this with 100% certainty is to actually install Visual Studio [Visual C/C++] and the Windows Platform SDK and then examine the header files where the functions and structures are defined.  There will very likely be some annotation in comments or usage of "#prgama" pre-processor directives that specify things like "packing size" or "alignment".  Once you learn to read that and understand it, you'll be doing less guess work.

Title: Re: dllCall to call getkeyboardlayout
Post by: td on June 19, 2022, 07:07:55 AM
Using the DllStructAlloc and related functions to allocate and access c-style structures takes care of memory alignment automagically. 
Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 19, 2022, 08:31:47 AM
Hallo.
For td:
I tried this:
hStructHandle = DllStructAlloc ( "handle:hDevice dword:dwType" )
iStructWidth = IntControl ( 98, hStructHandle, 1, 0, 0 )
Message ( "Struct dimension", iStructWidth )
DllStructFree ( hStructHandle )
Exit
if executed on WinBatch 32 bit it give the message "8"
if executed on WinBatch 64 bit it give the message "12"
then it seem no magically aligned...
Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 19, 2022, 09:38:37 AM
Hallo.
I found this function to calculate the right aligned struct dimension:
WILBYTENESS = ( 2 ** ( WinMetrics ( -2 ) + 3 )) / 8
DWORD = 4
If WILBYTENESS == 4

   HANDLE = HANDLE32

Else
   HANDLE = HANDLE64

EndIf
iStructWidth = HANDLE + DWORD
iStructWidth += ( WILBYTENESS - iStructWidth mod WILBYTENESS ) mod WILBYTENESS  ; Struct dimension with aligned data

It seem work.
I have some questions:
- in the 64 bitness WIL structure, every data must be aligned or only the structure must be aligned?
- in the 64 bitness WIL structure, the 2 bytes data must be aligned ( then every data is 8 bytes or multiple width )?
- in the 32 bitness WIL structure, the 2 bytes data must be aligned like 4 bytes data on the 64 bitness structure?
Title: Re: dllCall to call getkeyboardlayout
Post by: ChuckC on June 19, 2022, 10:11:52 AM
Where do you keep coming up with a data type that's 2 bytes in size?  There is no situation, ever, any where, where a HANDLE or a DWORD value would be 2 bytes in size.

"DWORD" stands for "Double WORD", where "WORD" is a 16-bit [2 byte] unsigned integer, and DWORD is twice as large as a WORD.


Again, regarding the actual size of RAWINPUTDEVICELIST that is required on 64-bit Windows, the absolute authoritative source would be to compile and execute some C++ code that has the following code:

auto x = sizeof(RAWINPUTDEVICELIST);

and then examine the value of "x" in the debugger or print it out to the console.

Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 19, 2022, 12:37:05 PM
"a WORD is always 2 bytes"
you told before...
I think some structures can contain it: in that case how is the alignment? in the 32 bitness WIL structure is necessary, too?
Thank you very much
Title: Re: dllCall to call getkeyboardlayout
Post by: ChuckC on June 19, 2022, 02:57:55 PM
On Windows, regardless of whether or operating system or program is 32-bit or 64-bit, the following always apply:

The type macro "WORD" is always defined as a 2-byte [16-bit] integer.

The type macro "DWORD" is always defined as a 4-byte [32-bit] integer.


In terms of memory alignment, that will be based on the native "word size" of the CPU architecture and the size of the data fields in the structure.  Yes, just as a single DWORD field at the end of the RAWINPUTDEVICELIST requires an additional 4 bytes of padding space on 64-bit architecture, a similar thing happens on a 32-bit architecture where a single WORD at the end of a structure that has an overall apparent size that does not end on a 4-byte boundary and would require an additional 2 bytes of padding space.


From one of the C/C++ header files in the Windows Platform SDK:

#undef FAR
#undef  NEAR
#define FAR                 far
#define NEAR                near
#ifndef CONST
#define CONST               const
#endif

typedef unsigned long       DWORD;
typedef int                 BOOL;
typedef unsigned char       BYTE;
typedef unsigned short      WORD;
typedef float               FLOAT;
typedef FLOAT               *PFLOAT;
typedef BOOL near           *PBOOL;
typedef BOOL far            *LPBOOL;
typedef BYTE near           *PBYTE;
typedef BYTE far            *LPBYTE;
typedef int near            *PINT;
typedef int far             *LPINT;
typedef WORD near           *PWORD;
typedef WORD far            *LPWORD;
typedef long far            *LPLONG;
typedef DWORD near          *PDWORD;
typedef DWORD far           *LPDWORD;
typedef void far            *LPVOID;
typedef CONST void far      *LPCVOID;

typedef int                 INT;
typedef unsigned int        UINT;
typedef unsigned int        *PUINT;


Ignore "near" and "far"... they are macros defined to be nothing and represent ancient terms that were applicable way back on 16-bit Windows 3.1.


Yes, structures may be defined to have fields of type WORD.  However, in this case, you don't have a structure to work with that contains a field of type WORD.  Stop conflating and confusing things by introducing garbage that isn't pertinent or applicable to the API function & associated parameter data types.


Again, do the following:

Download and install Visual Studio 2022 Community Edition.  It's *FREE* and won't cost you anything but the disk space required on your computer and the time it takes to install it.

Create a new C++ Console application from the corresponding built-in project template.

Start experimenting with C++ code examples related to your task and make them work enough that you understand them well enough to convert them to WinBatch code using DllCall() and related functions.

Visual Studio has a feature called "Intellisense" that allows you to right-click on keyword like "DWORD" and have it automatically navigate to whatever source code module or header file contains the declaration/definition for the keyword.  This capability is invaluable when you are learning to use a portion of the Win32 API that you are not familiar with.  It will show you all kinds of important details that you need to know.  It removes the guess work from the process, and from what I've observed, you are making a lot of bad assumptions and making a lot of bad guesses as to what things mean.  I'm trying to show you how to find FACTS that will be useful to you.

Title: Re: dllCall to call getkeyboardlayout
Post by: td on June 19, 2022, 03:34:51 PM
Quote from: ChuckC on June 17, 2022, 02:39:55 PM
Good point.  For passing a handle value as an individual parameter, that would be preferred way to do it.

When it comes to computing the site of a structure that contains a field of type HANDLE, would it be best just to test the "bitness" of WinBatch and add 4 or 8 to the structure size that's being computed?  I wasn't sure where "long_ptr" modifier for DllCall() could be shoe-horned into the arithmetic for structure size calculation.

The DllStructAlloc function and friends are "bitness aware" so you can use types like HANDLE and long_ptr in structures without having to worry about the bitness of the process running the script.
Title: Re: dllCall to call getkeyboardlayout
Post by: ChuckC on June 19, 2022, 03:59:59 PM
Quote from: td on June 19, 2022, 03:34:51 PM
Quote from: ChuckC on June 17, 2022, 02:39:55 PM
Good point.  For passing a handle value as an individual parameter, that would be preferred way to do it.

When it comes to computing the site of a structure that contains a field of type HANDLE, would it be best just to test the "bitness" of WinBatch and add 4 or 8 to the structure size that's being computed?  I wasn't sure where "long_ptr" modifier for DllCall() could be shoe-horned into the arithmetic for structure size calculation.

The DllStructAlloc function and friends are "bitness aware" so you can use types like HANDLE and long_ptr in structures without having to worry about the bitness of the process running the script.


Nifty!  Thanks!
Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 20, 2022, 03:50:24 AM
Hallo ChuckC.
Thank you very much for your help.
Sorry for my bad English.
I am an hardware developer about domotic.
I build a new PC, starting the box ( I build the box by hand, cutting the plate... ), modifying the power supply, studying the air way, and may other things ); I made a simil-driver to interface the PC with an OLD 56K modem to automatically make phone number by only a selection on Outlook: when I open the phone communication the modem make the phone number and the call begins; I made a radio-alarm with week scheduling and auto-regulation of the volume ( from 0% to 100% in many minuts ) and many other ( really, many other, for example interfacing my stereo with PC ).
I use WinBatch ( and AutoHotkey ) because is very versatile: it allows to build a simil-driver ( low level ) and radio-alarm ( with interface, high level ); it allow sequentially program and event program; it allow to build interface without complicate knowledge; then for me would be too much hard starting to know C++ programming ( would be very interesting, but when I study it I can no build any other thing, and I have many other things to build...: for example I need to control some external digital card fot hardware control... ).
For example I was a C programmer, and I like C very much ( at DOS time... ) but to build the same prgrams by C or C++ requires many knowledge that I can no have, building all other... I can no made all, even I try it...
Then if I can no install C++ SDK I would be many happy...
I am trying to detect many keybords ( all remote ) to allow, at the same time, for a person to view some image ( by remote connection ) and to another person to hear radio Internet and to another person to hear stereo ( I made only an example to understand, I hope it is clear... )
Now I go ahead.
:-)
Title: Re: dllCall to call getkeyboardlayout
Post by: ChuckC on June 20, 2022, 05:09:34 AM
As long you keep focusing on saying that you can't do something you'll never be able to do it.

C++ is a superset of C.  In terms of basic Win32 API usage, although you are using the Visual C++ compiler in Visual Studio, by and large, you are really only using C language features.

I can tell you with 100% certainty at this point that you will not be able to read input from multiple keyboards and determine which keyboard produced the input without delving into some C/C++ coding efforts.  You will either that you are willing to do so and will make your very best effort at doing so, of you will fail.

Tony's previous comment about getting the latest WIL SDK and learning to build an extender with C/C++ was spot-on accurate in terms of what would be the shortest path to writing code that does what you want it to do.

Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 21, 2022, 01:22:03 AM
Hallo.
It seem work well:

#DefineFunction iStructAlignedWidth ( iStructWidth, iWILByteness )
   Return iStructWidth + ( iWILByteness - iStructWidth mod iWILByteness ) mod iWILByteness
#EndFunction                                                         ; #DefineFunction iStructAlignedWidth ( iStructWidth, iWILByteness )

WILBYTENESS = ( 2 ** ( WinMetrics ( -2 ) + 3 )) / 8

POINTER32 = 4
POINTER64 = 8
INTDATA = 4
UINTDATA = 4
HANDLE32 = 4
HANDLE64 = 8
DWORD = 4

If WILBYTENESS == 4
   HANDLE = POINTER32
Else
   HANDLE = POINTER64
EndIf
sHIDDetectedItem = ""
sKbdDetectedItem = ""
sMouDetectedItem = ""

hpuiNumDevice = BinaryAlloc ( UINTDATA )
iStructWidth = HANDLE + DWORD
iStructWidth = iStructAlignedWidth ( iStructWidth, WILBYTENESS ) 

iReturnVal = DllCall ( DirWindows ( 1 ) : "user32.dll", long:"GetRawInputDeviceList", lpnull, lpbinary:hpuiNumDevice, word:iStructWidth )
iHIDCount = BinaryPeek4 ( hpuiNumDevice, 0 ) 

hpRawInputDeviceList = BinaryAlloc ( iStructWidth * iHIDCount )      ; Allocazione memoria per tutte le strutture generate dalla
iHIDDetected = DllCall ( DirWindows ( 1 ) : "user32.dll", long:"GetRawInputDeviceList", lpbinary:hpRawInputDeviceList, lpbinary:hpuiNumDevice, word:iStructWidth )

For iCnt = 0 To iHIDDetected - 1

   hDeviceOffset = iCnt * iStructWidth                              ; Offset hDevice
   dwTypeOffset = hDeviceOffset + HANDLE                            ; Offset dwType

   If WILBYTENESS == 4
      ihDevice = BinaryPeek4 ( hpRawInputDeviceList, hDeviceOffset )
   Else
      ihDevice = BinaryPeek8 ( hpRawInputDeviceList, hDeviceOffset )
   EndIf
   idwType = BinaryPeek4 ( hpRawInputDeviceList, dwTypeOffset )     ; dwType nella struttura N. iCnt

   Switch idwType

      Case 2                                                       ;  HID no mouse no keyboard

         sHIDDetectedItem = ItemInsert ( ihDevice, -1, sHIDDetectedItem, @TAB )

         Break

      Case 1                                                       ; HID keyboard

         sKbdDetectedItem = ItemInsert ( ihDevice, -1, sKbdDetectedItem, @TAB )

         Break

      Case 0                                                       ; HID mouse

         sMouDetectedItem = ItemInsert ( ihDevice, -1, sMouDetectedItem, @TAB )

         Break

   EndSwitch

Next

BinaryFree ( hpRawInputDeviceList )
BinaryFree ( hpuiNumDevice )

Now I try to detect every keyboard name.
Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 21, 2022, 03:16:52 AM
Hallo.
I am trying to get keyboard name.
I use this, but it seem no work:

#DefineFunction iStructAlignedWidth ( iStructWidth, iWILByteness )
   Return iStructWidth + ( iWILByteness - iStructWidth mod iWILByteness ) mod iWILByteness
#EndFunction

WILBYTENESS = ( 2 ** ( WinMetrics ( -2 ) + 3 )) / 8
DWORDDATA = 4                                                        ; Num. Bytes DWORD
WPARAMDATA32 = 4                                                     ; Num. Bytes WPARAM in WinBatch 32 bit
WPARAMDATA64 = 8                                                     ; Num. Bytes WPARAM in WinBatch 64 bit
HANDLE32 = 4                                                         ; Num. Bytes HANDLE in WinBatch 32 bit
HANDLE64 = 8                                                         ; Num. Bytes HANDLE in WinBatch 64 bit
If WILBYTENESS == 4
   HANDLE = HANDLE32
   WPARAMDATA = WPARAMDATA32

Else
   HANDLE = HANDLE64
   WPARAMDATA = WPARAMDATA64

EndIf

iRAWINPUTHEADERwidth = DWORDDATA + DWORDDATA + HANDLE + WPARAMDATA
iRAWINPUTHEADERwidth = iStructAlignedWidth ( iRAWINPUTHEADERwidth, WILBYTENESS )

hpRawInputDevice = BinaryAlloc ( iRAWINPUTHEADERwidth )
hpcbSize = BinaryAlloc ( UINTDATA )
BinaryPoke4 ( hpcbSize, 0, iRAWINPUTHEADERwidth )
xuiCommand = xHex ( "20000007" )
;xuiCommand = 20000007
iHIDDetected = DllCall ( DirWindows ( 1 ) : "user32.dll", long:"GetRawInputDeviceInfoA", lpbinary:hpRawInputDevice, word:xuiCommand, lpnull, lpbinary:hpcbSize )

It return -1
I think to use well the pointers...
I'm no sure about WPARAMDATA
Can you help me to understand the problem?
Thank you very much.
Title: Re: dllCall to call getkeyboardlayout
Post by: ChuckC on June 21, 2022, 05:15:40 AM
This parameter is being passed incorrectly:  lpbinary:hpRawInputDevice

This buffer isn't set up properly and isn't populated with a valid value:  hpRawInputDevice.  This is a handle to a device, and the value will have been obtained by iterating thru the output buffer that you got from calling GetRawInputDeviceList().

This parameter is being passed incorrectly:  word:xuiCommand.  The value is a UINT [4 byte value], not a WORD [2 byte value].  Reread the documentation for the function.

This buffer isn't set up properly and isn't populated with a valid value:  hpcbSize.  Again, you are going to call GetRawInputDeviceInfo[A|W] twice... once with pData being a null pointer and cbSize being zero.  Provided that the first call succeeds, cbSize will be populated with the number of bytes [not characters] that pData must have allocated to hold the string.  For the second call, you allocate a buffer for pData that contains at least the minimum number of bytes and then make the function call.  If it succeeds, then you'll perform some kind of BinarPeek*() function call in WinBatch to read a string from the buffer.  Make sure that if you call GetRawInputDeviceInfoA() that you peek an ANSI string from the pData buffer, and if you call GetRawInputDeviceInfoW(), then peek a Unicode [wide character] string from the pData buffer.


The output buffer, "pData", isn't a structure in this case.  It's nothing more than a buffer used to receive a string that is the device name.

Reread the function documentation.  You aren't understanding the meaning of the parameters and how to properly set them up with initial values and pass them to the function.


Also, you are ignoring what I told you about getting started with C/C++ development using these function.  Directly within WinBatch, you will eventually find yourself able to enumerate the raw input devices, which you have apparently done successfully, followed eventually by obtaining information [e.g. the device name] for each raw input device that is of interest.  After that you are going to run head first into a brick wall and you won't get around/over/under/thru it without resorting to writing C/C++ code, most likely in a WinBatch Extender library.  That will happen when you attempt to register for the receipt of raw input messages and find that the only way to do so is to write C/C++ code that creates an invisible/hidden window with its own private incoming message loop to receive those messages.  Do not dismiss this as a "maybe" kind of thing... it's an absolute necessity.
Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 21, 2022, 07:10:18 AM
Hallo.
You have reason, I no red well documentation.
I re-read it documentation many times, and your suggestions many times.
First thing: on WinBatch documentation named "How To Call Windows API Functions" is reported UINT C++ type must translated to WORD WinBatch type ( I red it before, after that I red your suggestion and I followed it ).
I made it:
hDevice = BinaryAlloc ( HANDLE )                                                                        ; HANDLE  == 4
BinaryPoke4 ( hDevice, 0, BinaryPeek4 ( hpRawInputDeviceList, 32 ))               ; hpRawInputDeviceList is the buffer generated by GetRawInputDeviceList(...), the offset is calculated before to point an ihDevice of a keyboard
iTest = BinaryPeek4 ( hDevice, 0 )                                                                       ; this test show me the valure is correctly red from the buffer list and correctly write on the hDevice buffer
hcbSize = BinaryAlloc ( UINTDATA )                                                                      ; UINTDATA  == 4
BinaryPoke4 ( hcbSize, 0, 0 )                                                                               ; 0 because pData is a null pointer
iTest = BinaryPeek4 ( hcbSize, 0 )                                                                       ; This show me the data is OK
xuiCommand = Int ( xHex ( "20000007" ))                                                          ; I hope, but I'm no sure, the data is OK: uiCommand 0x20000007 I think must be translated to INT value
ipDataBytes = DllCall ( DirWindows ( 1 ) : "user32.dll", long:"GetRawInputDeviceInfoA", lpbinary:hDevice, long:xuiCommand, lpnull, lpbinary:hcbSize )

The return value is -1: can you help me why?
Title: Re: dllCall to call getkeyboardlayout
Post by: ChuckC on June 21, 2022, 07:27:29 AM
Tony will have to provide some input about the WinBatch docs related to calling Win32 API functions and whether there may be an error in there.  In all cases, UINT is the same as DWORD on both 32-bit & 64-bit Windows.

You don't say if you're using 32-bit WinBatch or 64-bit WinBatch.  Pick one and consistently use it for your testing.  Do not switch unless there is some overriding highly logical reason to do so.  I would recommend always using 64-bit WinBatch.

On 64-bit Windows, a HANDLE is basically the same as a pointer, and is always 8 bytes, not 4 bytes.

Unless you were using 32-bit WinBatch the following is invalid:

hDevice = BinaryAlloc ( HANDLE )
BinaryPoke4 ( hDevice, 0, BinaryPeek4 ( hpRawInputDeviceList, 32 ))

The same applies to all instances of BinaryPeek4() and BinaryPoke4() as it relates to HANDLE values in a 64-bit program.

Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 21, 2022, 07:39:57 AM
Hallo.
I think I found the problem.
Now function returns 0 and pcbSize contain 122.
I think the first argument is no an handhe but an integer that contain an handle; then I wrote this:
hDeviceNum = ItemExtract ( 1, sKbdDetectedItem, @TAB )                                    ; ItemList with many handles previously detected by GetRawInputDeviceList(...)
ipDataBytes = DllCall ( DirWindows ( 1 ) : "user32.dll", long:"GetRawInputDeviceInfoA", long:hDeviceNum, long:xuiCommand, lpnull, lpbinary:hcbSize )

if it is right I go ahead...
Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 21, 2022, 07:43:59 AM
Normally I am on WIL 32 bit because the debugger works on 32 bit.
When I compile the program works on 64 bit.
Normally I am testing, but after test it must work on 32 and 64 bit.
Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 21, 2022, 07:48:30 AM
The program knows everytime if working WIL is 32 or 64 bit because the first program line is:
WILBYTENESS = ( 2 ** ( WinMetrics ( -2 ) + 3 )) / 8
that generates 4 for WIL 32 bit and 8 for WIL 64 bit
Title: Re: dllCall to call getkeyboardlayout
Post by: ChuckC on June 21, 2022, 08:09:57 AM
long:hDeviceNum will work OK for 32-bit WinBatch.  It will very likely fail on 64-bit WinBatch since HANDLE would be 8 bytes, not 4.  Make sure that whatever populates sKbdDetectedItem with HANDLE values is using the correct BinaryPeek4() / BinaryPeek8() function.  There may be cases were incorrectly peeking only the lower 4 bytes of an 8 byte HANDLE from a buffer value will work on 64-bit WinBatch *IF* the upper 4 bytes just happen to be zero.

cbSize having a value of 122 sounds like it might be a reasonable value.  use one of the Peek*() functions to read an ANSI string from the binary buffer and then examine the string value.

Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 21, 2022, 08:42:59 AM
Hallo.
The Device interface name I found is:
\\?\HID#VID_
or very similar for other keyboards.
I made:
iInfoLen = BinaryPeek4 ( hcbSize, 0 )
hData = BinaryAlloc ( iInfoLen )
iReturnVal = DllCall ( DirWindows ( 1 ) : "user32.dll", long:"GetRawInputDeviceInfoA", long:hDeviceNum, long:xuiCommand, lpbinary:hData, lpbinary:hcbSize )
sTest = BinaryPeekStr ( hData, 0, iInfoLen )
The Device interface name is contained in sTest variable.
Do you think it is exactly?
Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 21, 2022, 09:09:18 AM
long:hDeviceNum will work OK for 32-bit WinBatch
If I have 64-bit WinBatch how must I define and declare variables/memory?
I tried
hDeviceNum = BinaryAlloc ( HANDLE )
and declare
iReturnVal = DllCall ( DirWindows ( 1 ) : "user32.dll", long:"GetRawInputDeviceInfoA", lpbinary:hDeviceNum, long:xuiCommand, lpnull, lpbinary:hcbSize )
but it no works...
Title: Re: dllCall to call getkeyboardlayout
Post by: ChuckC on June 21, 2022, 09:13:02 AM
That device name looks like it's been truncated a bit.  If the required buffer was 122 bytes long, then the string most likely should have been 121 or 122 characters long depending on whether a trailing NULL byte is present to terminate the string.

The second call to GetRawInputDeviceInfoA() results in the content of the buffer hcbSize being updated, so the value of iInfoLen needs to be updated by calling BinaryPeek4() again, and the new value is then used to in the call to BinaryPeekStr().
Title: Re: dllCall to call getkeyboardlayout
Post by: ChuckC on June 21, 2022, 09:18:32 AM
Quote from: archimede on June 21, 2022, 09:09:18 AM
long:hDeviceNum will work OK for 32-bit WinBatch
If I have 64-bit WinBatch how must I define and declare variables/memory?
I tried
hDeviceNum = BinaryAlloc ( HANDLE )
and declare
iReturnVal = DllCall ( DirWindows ( 1 ) : "user32.dll", long:"GetRawInputDeviceInfoA", lpbinary:hDeviceNum, long:xuiCommand, lpnull, lpbinary:hcbSize )
but it no works...


no no no... take what you were doing for 32-bit:

Quote
iReturnVal = DllCall ( DirWindows ( 1 ) : "user32.dll", long:"GetRawInputDeviceInfoA", long:hDeviceNum, long:xuiCommand, lpbinary:hData, lpbinary:hcbSize )

and adjust the data sizes for 64-bit Windows:

hDeviceNum is a HANDLE value as defined for 64-bit WinBatch/Windows, meaning it is 8 bytes, not 4, so ensure that you obtained it previously from a structure field in a binary buffer using BinaryPeek8().

then use "int_ptr:hDeviceNum" instead of "long:hDeviceNum".
Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 21, 2022, 09:32:26 AM
ok I will try it.
Now I obtained a very strange result:
After the call
iReturnVal = DllCall ( DirWindows ( 1 ) : "user32.dll", long:"GetRawInputDeviceInfoA", long:hDeviceNum, long:xuiCommand, lpbinary:hData, lpbinary:hcbSize )
if I put
sTest = BinaryPeekStr ( hData, 0, iInfoLen )
the sTest == ""
if I put
sTest = BinaryPeek4 ( hData, 0 )
sTest = BinaryPeekStr ( hData, 0, iInfoLen )
the sTest == "\\?\"
if I put
sTest = BinaryPeek4 ( hData, 0 )
sTest = BinaryPeek4 ( hData, 4 )
sTest = BinaryPeekStr ( hData, 0, iInfoLen )
the sTest == "\\?\HID#"
if I put
sTest = BinaryPeek4 ( hData, 0 )
sTest = BinaryPeek4 ( hData, 4 )
sTest = BinaryPeek4 ( hData, 8 )
sTest = BinaryPeekStr ( hData, 0, iInfoLen )
the sTest == "\\?\HID#VID_"
Do you know how is it possible?
Title: Re: dllCall to call getkeyboardlayout
Post by: ChuckC on June 21, 2022, 10:33:18 AM
Looking thru the function documentation, it's the return value of the function that specifies how many *characters* [not bytes] were returned in the buffer.  This can differ from the value placed in cbSize that represented the minimum number of characters the buffer must hold, which would typically be the length of the string plus one additional null terminator character.

Looking at your code, it's the value in iReturnVal, not iInfoLen, which should be used with BinaryPeekStr().


On my system, there are 3 keyboard devices available for raw input, and the names returned for them are as follows:

"\\?\HID#VID_046D&PID_C52B&MI_00#8&25c01294&0&0000#{884b96c3-56ef-11d1-bc8c-00a0c91405dd}"
"\\?\ACPI#DLLK098F#4&21948c4c&0#{884b96c3-56ef-11d1-bc8c-00a0c91405dd}"
"\\?\HID#ConvertedDevice&Col01#5&28b906d8&0&0000#{884b96c3-56ef-11d1-bc8c-00a0c91405dd}"

Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 21, 2022, 02:08:47 PM
The function return value for me is half than length specified by the argument call.
My surprise is BinaryPeekStr(...) return "", but if BEFORE that I use BinaryPeek(...), the function BinaryPeekStr(...) works...
I controlled it well.
Can you tell me why?
Title: Re: dllCall to call getkeyboardlayout
Post by: ChuckC on June 21, 2022, 02:41:16 PM
You haven't posted enough information about the specific input parameter values, output parameter values and function return values, nor about your specific calls to the Binary*() functions to have any idea what you've done wrong.  I am, however, certain that the problem is happening some place between the keyboard and the chair.
Title: Re: dllCall to call getkeyboardlayout
Post by: td on June 21, 2022, 07:37:45 PM
Chuck, you have spent more time (and shown more patience) on this forum member's problem than is reasonable to expect.  It is probably time to end the topic.
Title: Re: dllCall to call getkeyboardlayout
Post by: ChuckC on June 22, 2022, 07:13:45 AM
Noted.  I had a blue & gold macaw for 14 years... attempts at modifying her behavior taught me the lesson of futility :o  Perhaps the quote from "Joshua" in "War Games" applies... "A strange game.  The only winning move is not to play..."

Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 25, 2022, 03:17:43 AM
Hallo.
I'm sorry.
Please no ban me :-(
I'm sorry if I made no good thing.
Please tell me what you no like, before ban me.
Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 25, 2022, 03:30:37 AM
For ChuckC:
I made the working utility to detect all Human Interface Devices.
If you are interested You can write me on this e-mail (because, elsewhere, I hope to be banned):
archimede00@bluewin.ch

For td:
With the ChuckC help I made a working HID detect.
Can I continue to go ahead with the help to detect keys on on every keyboard?
Need I tu buy newly WinBatch?
Thank you very much and sorry if I made some bad thing.
Title: Re: dllCall to call getkeyboardlayout
Post by: ChuckC on June 25, 2022, 05:53:30 AM
The problem is that I've explained that what you are trying to achieve is absolutely going to require that you write some C/C++ code for a critical part of the functionality that is beyond the capabilities of WinBatch, and you have stated that you will not or cannot learn to write code in C/C++.  That pretty much makes any and all time spent on this issue a waste of time.

To be more specific:

WinBatch will allow you to enumerate HID devices and obtain their names and their handle values.

All of that is useless if you can't register to receive raw input messages and then actually receive them.  That is the part where WinBatch is going to get into trouble.  Attempting to cobble together code relying on DllCall() will result in something almost unreadable, very difficult to understand and nearly impossible to troubleshoot if/when it doesn't work.  There's a high probability that the creation of a hidden message-only window and its associated message processing loop to receive & process raw input messages simply cannot be achieved with DllCall().

To make an analogy, you are trying to build an igloo out of mashed potatoes... it's a messy process, the results won't be what you wanted, it's going to take a long time and you probably won't be able to get it done at all.  Tony, myself and others have advised you that you've got a flawed approach to solving the problem and have tried to explain what you need to do and why you need to do it, but you keep on ignoring that advice.

It is, however, possible to write such code in C/C++ that is compact, clean, easily readable and performs well in a reliable way.  If you make efficient use of Google, it's fairly easy to find functional examples of handling raw input in C/C++ code.  The most direct route to success that any of the advanced users of WinBatch on this forum would take is to make that code work in a WinBatch extender and then write simpler WinBatch script code to consume the functionality provided by the extender.  The extender takes care of the hard parts that WinBatch isn't suited for and then makes the results available for use in a script.  From there, the script can use the functionality that WinBatch excels at like automating the control of other applications on the desktop.

Here's a link to a discussion that covers the core piece of functionality that you need:

https://stackoverflow.com/questions/62088236/how-do-i-register-an-invisible-window-class-in-a-win32-console-application (https://stackoverflow.com/questions/62088236/how-do-i-register-an-invisible-window-class-in-a-win32-console-application)

If understanding that discussion is beyond your ability, then you either need to do some independent study and learn to understand it, or you simply need to give up on this task.  I've read it a couple of times, and it provides an example that can readily be adapted to what you want to do.  All you'd need to do is make it work in a console application and then adapt that code to work inside a WinBatch extender.

None of this assessment is personal in nature.  Nobody wants to ban you or not answer questions you have about WinBatch related to other scripting projects.  It's simply a matter of looking at the stated goal, the choice of technology and the level of knowledge, experience and skill required to achieve the goal.  If you aren't willing to do the things that were recommended then there's no incentive to continue to provide any additional advice or assistance, and that leads to this raw input discussion becoming a dead topic that nobody feels inclined to continue to discuss.

Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 25, 2022, 06:51:20 AM
Thank you very much.

I think you have reason.
Sorry if I answer heare, but I no found any way to make a personal message.
I see the WinBatch language is not very good to launch API functions; this is the first time I need to solve a similar problem, and I think I will no have  any other similar problem.
I can no learn a language everytime I have a problem no suited to the language I know; then I must know a language very versatile.
To learn a language is no simple: it is necessary many engagement and many time: I used many engagement and many time to learn WinBatch and other ( AutoHotKey, that seem similar to WinBatch, but it allow the automation of existing program that WinBatch can no make ).
With your help I made a WinBatch utility to list all HID devices, divided by type, everyone with the handle.
Now I need only to detect what key is down on every specific HID ( keyboard ).
I think you have absolutely reason, but I think, with some small forcing, is possible to obtain the result: really, I can no learn a language to every different type of problem... :-(
Obviously, I red your link and, for what I understand, I think it is very interesting; but, after that, there is the problem to interface that solution ( on a different language ) with other WinBatch programs...
For example, with WinBatch I made a command prompt program executor function very much powerful than all Run (...) WinBatch functions, and I used it into many WinBatch programs I made...
If you like, I can send it here.
I need a similar thing, but for HID detect by API functions, to use it into one or much WinBatch programs.
I know it is messed up, no ideal; but from the function extern, it will works and it can be called from other WinBatch functions...
The list of all HID with name, type and handle is very good to now; it is 50% of the work made.

Thank you very much for patience.
Title: Re: dllCall to call getkeyboardlayout
Post by: ChuckC on June 25, 2022, 04:47:47 PM
Quote from: archimede on June 25, 2022, 06:51:20 AM

...

1)  To learn a language is no simple: it is necessary many engagement and many time

...

2)  Now I need only to detect what key is down on every specific HID ( keyboard ).

...

3)  I think you have absolutely reason, but I think, with some small forcing, is possible to obtain the result: really, I can no learn a language to every different type of problem... :-(



For #1:  Yes, you are correct, it takes time and effort to learn new things.

For #2:  Yes, you have stated that as your goal from the very beginning.  The word "only" is only 4 letters in length.  The C/C++ code you are going to write is going to be several 1000's of lines in length.  Just because the word used to describe the last little bit of functionality is a small word has absolutely no relationship to the size of the effort involved in implementing the desired functionality.

For #3:  No amount of effort involved in "some small forcing, is possible to obtain result" is going to make it work like you want it to no matter how much you want it to.  You still refuse to accept the reality of the situation.  Detecting the key presses and determining which keyboard device they were generated by requires writing some C/C++ code.

Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 26, 2022, 02:45:15 AM
:-)
It seem we are reciprocally understanging...
I searched the API function that seem to solve the problem:
GetRawInputData( ... )
If it solve the problem, at now it seem the only missing call... or not?
Title: Re: dllCall to call getkeyboardlayout
Post by: ChuckC on June 26, 2022, 06:21:19 AM
No, GetRawInputData() is only one of the API functions that needs to be called.

What you are failing to understand is when it needs to be called, and what input parameters must be passed to it.  Equally important is where/how those input parameters are obtained.

You don't just directly poll the known keyboard devices asking if they have input for you.  Windows itself has direct control over the keyboard hardware and user-mode program code has zero direct access to the keyboard hardware.  Instead, the device driver for the keyboard detects the key presses and then sends a message higher up in Windows to be handled elsewhere.  Programs interested in receiving raw input have to register for raw input and then listen for raw input messages sent to them from Windows itself.

The part you are still failing to understand is the message processing loop that is associated with a window and how that works.  When registering for raw input messages, the function that you call requires a window handle as an input parameter.  You are responsible for writing that program code that implements that window, including the program code for the message processing loop.  That is the part that is going to require C/C++ code.  I already posted a link to a discussion showing an example of how to do that.  It's the discussion about creating a hidden window from a console application.  I'm not going to explain that any further.  If you can't understand it, then what you want to do is simply beyond your skill.

Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 26, 2022, 08:03:13 AM
:-)
I no studied Windows theory :-(
I am an electronic engineer, no an informatic engineer.
You are very interesting for me; your help, to you, was be very useful.
In the RAWINPUTHEADER structure there is a param named
WPARAM wParam
what is that?
It seem important to set 0 or 1 but why? What is the meaning of that?
How I can to know what is the last keyboard that worked on the system?
:-)
Title: Re: dllCall to call getkeyboardlayout
Post by: JTaylor on June 26, 2022, 12:34:31 PM
You could engage in the time-honored and often-used tradition of paying someone to do what you cannot and/or do not wish to learn to do.

Just a thought.

Jim
Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 27, 2022, 01:08:39 AM
JTaylor.
I hope my English is good enough to undertand what you wrote me...
I think you would have right if I would make a commercial thing...
:-)
Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 27, 2022, 01:37:15 AM
Hallo.
I attach here the HID detect utility.
It is experimental but is completely working.
I'm sorry for Italian comments; if you like some explanation, ask me.
It works well on 32 and 64 bit WIL.
Title: Re: dllCall to call getkeyboardlayout
Post by: archimede on June 29, 2022, 03:27:11 AM
Hallo.
I see the function
WaitForKeyEx (...)
is very near what I need.

First all:
FOR MY OPINION that is no good described, because it no explain that function is "no sichronized", it detect the keyboard key at hardware level, NO THE INPUT KEY.

After that:
at this level I think is no impossible to detect the name ok the keyboard with the pressed key... or not?
Title: Re: dllCall to call getkeyboardlayout
Post by: ChuckC on June 29, 2022, 04:18:27 AM
It is not possible to do what you want to do with the functionality that's built into WinBatch.

This has been explained to you more times that I care to count.

If you are unwilling/unable to write an extender in C/C++, and are not willing to pay somebody else to develop it for you, then you'll have to give up on you project as it will be unachievable as it has become an exercise in futility.

Please, stop asking the same question over and over again expecting to get a different answer.
Title: Re: dllCall to call getkeyboardlayout
Post by: JTaylor on July 07, 2022, 08:38:19 AM
Should you decide to take the plunge and solve your problem, here is a Visual Studio solution to get started.   I think it has everything you need to do what you want.   Just need to make use of what is provided to accomplish your goal.

I have no answers to questions so please do not expect any.   While I tweaked things a bit to provide a display when pressing a key, the work is someone else's. 

I know what it is like to accomplish something like this without knowing C/C++ so thought I would help a bit.   All the Extenders I wrote were mainly because of persistence and not from knowing C.  I took a class back in the 80's and that was my only knowledge when I began.   If you need/want this bad enough you will do what is needed to make it happen.   Just like everything else in life :)

Jim