SendMessage.exe: Access to advanced Windows features

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Message
Author
Aacini
Expert
Posts: 1914
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

SendMessage.exe: Access to advanced Windows features

#1 Post by Aacini » 02 Dec 2016 20:28

SendMessage.exe is an auxiliary .exe program that works as a wrapper for the Win-32 API SendMessage function. This function allows you to have access to several interesting Windows features in a very simple way. You may read an introduction to the use of SendMessage at this article.

The operation of SendMessage.exe program is simple: it takes 3 values from the parameters and invoke the SendMessage function with them. This is the general usage format:

Code: Select all

SendMessage.exe  ["title"]  Message  [wParam  [lParam]]

In this format, Message is the number of the wanted System-Defined Message and the other two parameters vary accordingly. The optional window title allows to send the message to another window, instead of the current cmd.exe one. The key to make good use of SendMessage features is to know the values that can be used with it, but this information is not presented in the Windows API documentation in a coherent way. I spent more time browsing the documentation looking for useful cmd.exe message numbers than the time it tooks me to write the SendMessage.asm program! :shock: :? This is a list of the message numbers that I have successfully tested so far:

WM_VSCROLL=277 request: Scrolls the window's vertical scroll bar.
Possible values for wParam=request:
  • SB_TOP=6: Scrolls to top.
  • SB_PAGEUP=2: Scrolls one page up.
  • SB_LINEUP=0: Scrolls one line up.
  • SB_LINEDOWN=1: Scrolls one line down.
  • SB_PAGEDOWN=3: Scrolls one page down.
  • SB_BOTTOM=7: Scrolls to bottom.
  • SB_THUMBPOSITION=4: Scrolls the line given in high part of wParam to top of screen:
    set /A request=(line<<16)+SB_THUMBPOSITION

WM_HSCROLL=276 request: Scrolls the window's horizontal scroll bar.
Possible values for wParam=request:
  • SB_LEFT=6: Scrolls to left-most column.
  • SB_PAGELEFT=2: Scrolls one page left.
  • SB_LINELEFT=0: Scrolls one column left.
  • SB_LINERIGHT=1: Scrolls one column right.
  • SB_PAGERIGHT=3: Scrolls one page right.
  • SB_RIGHT=7: Scrolls to right-most column.
  • SB_THUMBPOSITION=4: Scrolls the column given in high part of wParam to left of screen:
    set /A request=(column<<16)+SB_THUMBPOSITION

WM_SYSCOMMAND=274 item: Selects an item from the window menu.
Possible values for wParam=item:
  • SC_MONITORPOWER=61808: Power the display on/low/off via lParam= -1/1/2.
  • SC_KEYMENU=61696: Open the Alt-letter menu; code of letter in lParam.
  • SC_TASKLIST=61744: Open the Start menu.
  • SC_SCREENSAVE=61760: Executes the screen saver.
  • SC_NEXTWINDOW=61504: Selects next window.
  • SC_PREVWINDOW=61520: Selects previous window.
  • SC_MOVE=61456: Enables window move.
  • SC_SIZE=61440: Enables window resize.
  • SC_MINIMIZE=61472: Minimizes the window.
  • SC_MAXIMIZE=61488: Maximizes the window.
  • SC_RESTORE=61728: Restores the window.
  • SC_CLOSE=61536: Closes the window.
  • SC_SCROLL=65523: Enables "Scroll mode" in cmd.exe window. (Note 1)
  • MF_ENABLED=0: Enables the menu item. (Note 2)
  • MF_GRAYED=1: Disables and gray the menu item. (Note 2)
  • MF_DISABLED=2: Disables the menu item, but not gray it. (Note 2)

Note 1: SC_SCROLL=0xFFF3=65523 is an undocumented value that was researched by S.O. user MC ND.

All previous values are defined in SendMessageValues.bat companion file as auxiliary variables via SET /A commands. For example:

Code: Select all

set /A WM_VSCROLL=277, SB_PAGEDOWN=3

As conclusion of previous description, the next command scrolls the cmd.exe window one page down:

Code: Select all

SendMessage.exe  %WM_VSCROLL%  %SB_PAGEDOWN%

Interesting! Isn't it? 8) Try it yourself:
SendMessage.zip
SendMessage.exe, SendMessage.txt and SendMessageValues.bat
(1.74 KiB) Downloaded 1057 times

Note 2: MF_ENABLED, MF_GRAYED and MF_DISABLED values are not menu items, but parameters of Win-32 API EnableMenuItem function instead; this function is also included in SendMessage.exe program for convenience. When you use these values in wParam, the lParam must have the menu item that is processed; for example: SendMessage %WM_SYSCOMMAND% %MF_DISABLED% %SC_CLOSE% disable the red X "close" button. You may also specify an item by its position in the menu instead of its name; for example: SendMessage %WM_SYSCOMMAND% %MF_GRAYED% 3 disable and gray the third item in the menu.

Note that this is work in progress: there are much more messages that could be successfully used in the cmd.exe window, but it is necessary to test each one of them. My next step is try to implement the ACM messages that would permit the playing of silent .AVI video files. If you want that I test other values, post here such request with a link to the wanted messages.

IMPORTANT: SendMessage.exe does NOT check the values given in the parameters in any way; it just use such values to invoke SendMessage API function. I don't know if some of the values not listed here may be potentially dangerous! The full documentation of all possible values is given in a link above. If you don't know what you are doing, just don't use any value not listed here. I will NOT be responsible for any damage that could be caused by the use of SendMessage.exe program! :evil: On the other hand, I'll appreciate it if you post here some additional values that you may successfully tested.

Antonio

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: SendMessage.exe: Access to advanced Windows features

#2 Post by dbenham » 02 Dec 2016 22:54

Very cool 8)

I'm not sure that I will ever use this utility, but it looks like it could be really powerful :!:

Given that a window title is not always unique, could you provide some other mechanism to specify the window :?: I'm thinking the PID might be a good candidate. We know how to derive the PID of the running batch script, and we also know how to launch a process with WMIC and return the PID. Perhaps we would need to walk up the parent tree to find the containing window, but that also sounds doable.

I found some VB code to derive the Window handle from the PID at http://forums.codeguru.com/showthread.p ... -ProcessID


Dave Benham

Aacini
Expert
Posts: 1914
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: SendMessage.exe: Access to advanced Windows features

#3 Post by Aacini » 03 Dec 2016 12:06

This program may be very useful in several ways. For example, when a command outputs several pages in the screen with no pause, I used to repeat the command adding "| more" or scroll the screen contents via clicks on the scroll bar, but that is annoying. :?

I defined a Batch file called S.bat with this line:

Code: Select all

@SendMessage 274 65523

Now, when a command outputs several pages in the screen I just run S.bat to enable the "Scroll mode", so I can scroll the screen contents with PageUp, UpArrow, DownArrow and PageDown keys. :D After that, I just press Enter key to disable the "Scroll mode"...

Antonio

mcnd
Posts: 27
Joined: 08 Jan 2014 07:29

Re: SendMessage.exe: Access to advanced Windows features

#4 Post by mcnd » 04 Dec 2016 05:41

Nice utility, thank you for the work and the invitation.

I think that in your initial list of messages to you missed this

Code: Select all

@echo off
    setlocal enableextensions disabledelayedexpansion
   
    SendMessage 258 84
    SendMessage 258 101
    SendMessage 258 115
    SendMessage 258 116

    set /p "=What is this? "

Where 258 = WM_CHAR

aGerman
Expert
Posts: 4678
Joined: 22 Jan 2010 18:01
Location: Germany

Re: SendMessage.exe: Access to advanced Windows features

#5 Post by aGerman » 04 Dec 2016 07:29

That's a helpful utility Antonio. Thanks for sharing :)

dbenham wrote:Given that a window title is not always unique, could you provide some other mechanism to specify the window :?: I'm thinking the PID might be a good candidate.

That's quite difficult Dave. Processes can have several windows. Every button, every check box, every text box, etc. is a child window. There is no direct relation between PID and a window. You have to enumerate everything. I assume it's a mess in Assembly (even if I don't speak ASM).

Antonio
I don't know if it could be helpful for you. I wrote a C/WinAPI tool that sends WM_CLOSE to a window specified by its handle, process name, process ID, thread ID, or window title. (It's not the same as killing the process but rather similar to pressing the red X button which enables the process to tidy up.) It's pretty much what Dave said unless that the message sent is always the same. If it would be too comlicated to adopt it in your ASM code I could easily rewrite the C code ... Just tell me.

Steffen

Code: Select all

Tries to close the main window of a process by sending the WM_CLOSE message.

Usage:
closewindow /L | /H HWND | /N "process name" | /P PID | /T TID | /W "window title"

  option /L      Creates a list of all main windows.
                 (window handle|process ID|process name|window title)
  HWND           Hexadecimal window handle as displayed using option /L.
  process name   Name (including .exe extension) of the process owning the window.
  PID            Process ID of the process owning the window.
  TID            Thread ID of the thread owning the window.
  window title   Window title as displayed in the task bar.

Lists window handle, process ID, process name and window title of the window whereto
WM_CLOSE was sent.

Returns 0 if the WM_CLOSE message was successfully sent to the found window. Otherwise 1
will be returned.

NOTE: Sending WM_CLOSE to a window does neither guarantee that the window will be closed
nor that the process will be terminated.
Attachments
closewindow.zip
(11.04 KiB) Downloaded 747 times

Aacini
Expert
Posts: 1914
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: SendMessage.exe: Access to advanced Windows features

#6 Post by Aacini » 04 Dec 2016 21:54

@mcnd,

I quickly reviewed most messages and just completed more intensive tests on messages that correctly works in the first test. As I said before, there are much more messages that could be successfully used in the cmd.exe window. Please, include a link to the message you refer to; this avoids a further annoying search. For example: WM_CHAR message.


@dbenham, @aGerman,

I started to do tests with this program just a couple hours before I posted it here. My firsts tests show that messages that are sent to other windows just works in certain cases, and this point depends on the relation that the cmd.exe window have with the recipient window. Given these conditions, and the fact that the same user will create both the recipient window and the corresponding SendMessage.exe command, I don't see a scenario where the recipient window could not be located by its title.

However, if you show me an example of a real scenario where the recipient window could not be located by its title, but just by its PID or another value, I would evaluate the possibility of include such feature in SendMessage.exe, although I really would like to keep it small. When the code needed to parse parameters and complete preliminary steps is much larger than the code used to perform the program's task, then is time to choose an high level programming language instead, like C++.

In order to put previous point in perspective, I am posting below the source code of SendMessage.asm program:

Code: Select all

;   SendMessage ["title"] message [wParam [lParam]]
;   Send a System Defined Message to a given or current window
;   Antonio Perez Ayala - Version 1.0 - Nov/2016

    .data
                       
        hWnd            DD              ?               ;handle of window
        message         DD              ?               ;message number
        wParam          DD              0               ;first parameter
        lParam          DD              0               ;second parameter

    .code

Main    PROC

        invoke  GetConsoleWindow        ;EAX = console window handle
        mov     hWnd, eax               ;store in its place
        ;
        mov     ecx, 3                  ;ECX = number of numeric params
        mov     edi, OFFSET message     ;EDI -> first number
        ;
        cld                             ;to increment indexes
        invoke  GetCmdLine              ;ESI -> Command Line
        invoke  GetNextArg              ;ESI -> next arg
        mov     al, [esi]               ;load next char
        inc     esi                     ;and advance index
        ;
        test    al, al                  ;parameter ends?
        jz      end_main                ;yes: terminate
        ;
        cmp     al, '"'                 ;is quote?
        jne     SHORT get_number        ;no: jump
        ;
        mov     ebx, esi                ;EBX -> "title"
        ;
@@:
        mov     al, [esi]               ;load next char
        inc     esi                     ;and advance index
        ;
        or      al, al                  ;parameter ends?
        jz      end_main                ;yes: error (quote expected)
        cmp     al, '"'                 ;closing quote?
        jne     SHORT @B                ;no: go back
        ;
        mov     BYTE PTR [esi-1], 0     ;change closing quote by ending zero
        invoke  FindWindow, NULL, ebx   ;EAX = handle of given window
        or      eax, eax                ;window found?
        jz      end_main                ;no: terminate
        mov     hWnd, eax               ;else: store handle
        ;
@@:
        call    GetNextArg              ;ESI -> next arg
        mov     al, [esi]               ;load next char
        inc     esi                     ;and advance index
        ;
        or      al, al                  ;parameter ends?
        jz      SHORT end_numbers       ;yes: jump
        ;
get_number:
        cmp     al, '0'                 ;less than '0'?
        jb      SHORT end_numbers       ;yes: continue
        cmp     al, '9'                 ;greater than '9'?
        ja      SHORT end_numbers       ;yes: continue
        ;
        call    GetNumber               ;EAX = number, ESI -> after number
        mov     [edi], eax              ;store it
        add     edi, 4                  ;and advance index
        ;
        loop    SHORT @B                ;go back for up to 3 numbers
        ;
end_numbers:
        cmp     ecx, 3                  ;at least one number?
        je      SHORT end_main          ;no: terminate
        ;
        cmp     message, WM_SYSCOMMAND  ;is this message?
        jne     SHORT send_message      ;no: continue
        cmp     wParam, 0F000H          ;wParam (item) is "standard"?
        jae     SHORT send_message      ;yes: continue
        ;                               ;else: is the special case
        cmp     lParam, 0F000H          ;menu item is "standard"?
        jae     SHORT @F                ;yes: continue
        or      wParam, MF_BYPOSITION   ;else: the item is a position
        ;
@@:
        invoke  GetSystemMenu, hWnd, FALSE              ;EAX = handle of system menu
        invoke  EnableMenuItem, eax, lParam, wParam     ;enable/disable menu item
        jmp     SHORT end_main                          ;and terminate
        ;
send_message:
        invoke  SendMessage, hWnd, message, wParam, lParam      ;send the message
        ;
end_main:
        invoke  ExitProcess, eax                ;return result value

Main    ENDP

GetNumber PROC

        and     eax, 0FH                ;EAX = first digit
        xor     ebx, ebx                ;initialize EBX = 0
        ;
@@:
        mov     bl, [esi]               ;load next digit
        inc     esi                     ;and advance index
        cmp     bl, '0'                 ;below '0'?
        jb      SHORT @F                ;yes: number ends
        cmp     bl, '9'                 ;above '9'?
        ja      SHORT @F                ;yes: number ends
        ;
        and     bl, 0FH                 ;EBX = this digit
        lea     eax, [eax*4+eax]        ;EAX = number * 5
        lea     eax, [eax*2+ebx]        ;EAX = number * 10 + this digit
        jmp     SHORT @B                ;go back for next digit
        ;
@@:
        dec     esi                     ;get index back to delimiter
        ret

GetNumber ENDP

        end     Main

The real executable code of this program occupy just 290 bytes! 8) This is what I call "small code" and is the reason because I don't include help screens nor error messages in my assembly programs...

Antonio

aGerman
Expert
Posts: 4678
Joined: 22 Jan 2010 18:01
Location: Germany

Re: SendMessage.exe: Access to advanced Windows features

#7 Post by aGerman » 05 Dec 2016 06:05

I don't see a scenario where the recipient window could not be located by its title.

I agree. It's rather unlikely (but still possible).


When the code needed to parse parameters and complete preliminary steps is much larger than the code used to perform the program's task, then is time to choose an high level programming language instead, like C++.

That's what I already offered. If you want me to write a C utility I will do so. It's still your idea and hence your decision :wink:


This is what I call "small code" and is the reason because I don't include help screens nor error messages in my assembly programs...

Seems that we have a different approach :lol:
It's quite seldom that I share one of my own utilities because there are tools like NirCmd out there that already cover a range of applications that we reinvented.
Whenever I share a utility I push myself hard in order to be at least as descriptive as Microsoft and have more stable and a more predictable tools than Microsofts are. That includes a reasonable error handling especially for the user input / passed arguments. And yes, sometimes the actual code for the main task is only a fraction.
Anyway, including a help message will for sure increase the size of the code and the binary but has nothing to do with the performance of the tool. Loading some bytes more or less into memory doesn't make a difference. Waiting for the OS to start the new process takes much more time. Having a help message included is just a matter of usability and comfort (imho :wink:).

Steffen

misol101
Posts: 475
Joined: 02 May 2016 18:20

Re: SendMessage.exe: Access to advanced Windows features

#8 Post by misol101 » 05 Dec 2016 06:46

Small code, large comments! :mrgreen:

I'm amazed by the fact that every single line is commented. Do you always do that?

Then again, if I had done that, maybe I would still understand some of my more cryptic old assembler code :wink:

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: SendMessage.exe: Access to advanced Windows features

#9 Post by dbenham » 05 Dec 2016 14:44

Aacini wrote:I started to do tests with this program just a couple hours before I posted it here. My firsts tests show that messages that are sent to other windows just works in certain cases, and this point depends on the relation that the cmd.exe window have with the recipient window. Given these conditions, and the fact that the same user will create both the recipient window and the corresponding SendMessage.exe command, I don't see a scenario where the recipient window could not be located by its title.

I didn't realize your utility had such constraints. With these constraints, I can see how the title should normally be adequate.

Aacini wrote:However, if you show me an example of a real scenario where the recipient window could not be located by its title, but just by its PID or another value, I would evaluate the possibility of include such feature in SendMessage.exe, although I really would like to keep it small. When the code needed to parse parameters and complete preliminary steps is much larger than the code used to perform the program's task, then is time to choose an high level programming language instead, like C++.

The user might not always be able to control the window title, even if the target is the window of a batch script. The user may not have written the target batch script, in which case the script could set its own title, even if the user STARTed the script with a unique title. I think it is even more likely to be a problem with exe files. If there are multiple windows running with the same program, and you cannot control the title, then you are out of luck.

But I agree with your decision based on the complexity of my request, coupled with the tiny size of your existing program.


Dave Benham

aGerman
Expert
Posts: 4678
Joined: 22 Jan 2010 18:01
Location: Germany

Re: SendMessage.exe: Access to advanced Windows features

#10 Post by aGerman » 05 Dec 2016 15:55

Aacini wrote:I spent more time browsing the documentation looking for useful cmd.exe message numbers than the time it tooks me to write the SendMessage.asm program! :shock: :?

I took the winuser.h header file of MinGW and sorted all the macro definitions with integer values. Then I edited the lines using regex. Now they can be processed with SET /A. That's the result:
winuser.zip
(19.54 KiB) Downloaded 673 times

Still too many values and without explanation :( I'm not sure if it could be even useful.

Steffen

kero
Posts: 16
Joined: 12 Jul 2016 00:49
Location: Moscow

Re: SendMessage.exe: Access to advanced Windows features

#11 Post by kero » 06 Dec 2016 00:31

Hi, Aacini!

You wrote: "SC_SCROLL=0xFFF3=65523 is an undocumented value that was researched by S.O. user MC ND."

But it would be strange to document this value - it is just menuitem's ID from standard system menu of CMD window:

Code: Select all

Restore       F120
Move           F010
Size            F000
Minimize      F020
Maximize     F030
Close           F060
Edit --------------------> Mark          FFF2
Defaults       FFF8        Copy          FFF0
Properties    FFF7         Paste         FFF1
                           Select All    FFF5
                           Scroll        FFF3
                           Find...       FFF4

Aacini
Expert
Posts: 1914
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: SendMessage.exe: Access to advanced Windows features

#12 Post by Aacini » 06 Dec 2016 01:32

kero wrote:Hi, Aacini!

You wrote: "SC_SCROLL=0xFFF3=65523 is an undocumented value that was researched by S.O. user MC ND."

But it would be strange to document this value - it is just menuitem's ID from standard system menu of CMD window:

Well, I think you are right: these values are just internal values used by cmd.exe, so there is no reason to document they...

I suppose you wrote a program similiar to this pseudo-C:

Code: Select all

hMenu = GetSystemMenu( GetConsoleWindow(), FALSE );
for ( i = 0; i < GetMenuItemCount(hMenu); i++ ) {
   printf("%X\n", GetMenuItemID(hMenu,i));
}

... that show the list of item ID's, but after that you should tested they one by one in order to know what is the purpose of each one; right? 8) Good job! :D

Antonio

kero
Posts: 16
Joined: 12 Jul 2016 00:49
Location: Moscow

Re: SendMessage.exe: Access to advanced Windows features

#13 Post by kero » 06 Dec 2016 02:27

> I suppose you wrote a program similiar to this

Much easier: I already had handy self-made program on menu - combination of monitor and "encyclopedia" :)
If it is interesting - here it is: http://files.rsdn.org/42164/menuspy.zip (screenshot: http://files.rsdn.org/42164/menuspy.png).

Aacini
Expert
Posts: 1914
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: SendMessage.exe: Access to advanced Windows features

#14 Post by Aacini » 17 Dec 2016 20:27

I completed several tests with most System-Defined messages and I have now more precise details about this matter. The bad news first: these messages can be send to existing windows components only, that is, a menu bar, scroll bar, selection boxes, etc. There is no way to create a new window component in order to make good use of the rest of messages, like creating an animation control to display an .AVI file via the ACM messages. However, there are still some messages not covered in the first post of this thread that can be used in the cmd.exe Window or in other applications.

One of the simplest ones is WM_CHAR=0x0102=258 message (previously suggested by user mcnd) that allows to inject a key in the keyboard input buffer this way: SendMessage %WM_CHAR% AsciiCode. Although this message is similar to other keyboard input methods (like JScript's SendKeys) there are subtle differences, so I need to complete more tests in order to get precise details on this message (and also on WM_SYSCHAR similar one).

Another useful message is WM_COMMAND=0x0111=273, that selects an item from the topmost menu of an application. All windows have a Window menu (formerly known as System menu, name that I prefer) represented by the title bar, that include the SC_MINIMIZE, SC_MAXIMIZE and SC_CLOSE item ID's at least. Most windows also include a Topmost menu, that is the standard application menu (an exception is cmd.exe, that groups its few edition options in the system menu). This means that each menu item must be selected with the right message: WM_SYSCOMMAND for System menu items and WM_COMMAND for Topmost menu items. This point return us to the same problem stated in the first post of this thread: know the values that allow us to access useful features in application programs.

In order to solve this point, I wrote a program called InspectMenu.exe that inspect the system and topmost menus of an active application and list the values of their item ID's and the short description that appears in the corresponding pop-up menu. When I ran this program in the cmd.exe window I got a series of additional values for the system menu:

Code: Select all

C:\Users\Antonio\Documents\ASMB> InspectMenu.exe

System  menu:
0 - F120: &Restaurar
1 - F010: &Mover
2 - F000: &Tama±o
3 - F020: Mi&nimizar
4 - F030: Ma&ximizar
5 - 0000:
6 - F060: &Cerrar
7 - --->  &Editar
    0 - FFF2: M&arcar
    1 - FFF0: C&opiar   Entrar
    2 - FFF1: &Pegar
    3 - FFF5: &Seleccionar todo
    4 - FFF3: &Desplazar
    5 - FFF4: &Buscar...
8 - FFF8: Pre&determinados
9 - FFF7: &Propiedades

Topmost menu:
No menu

This point confirms that the SC_SCROLL=0xFFF3=65523 value, described in the first post of this thread, is not a standard item used to enable the scroll mode in any application, but a particular value used just by cmd.exe, so there is no reason to document it as user kero indicated. This means that "SC_SCROLL" is an incorrect name because it is not a general value, like the other standard names that starts with "SC_", so I now choose the "CMD_" prefix for the particular values used in cmd.exe application.

Code: Select all

rem Values for ItemID's in the System (Window) menu of cmd.exe:
set /A CMD_Mark=0xFFF2, CMD_Copy=0xFFF0, CMD_Paste=0xFFF1, CMD_SelectAll=0xFFF5, CMD_Scroll=0xFFF3, CMD_Find=0xFFF4
set /A CMD_Defaults=0xFFF8, CMD_Properties=0xFFF7

InspectMenu.exe may also show the menu items of other applications; just give the window title between quotes in the parameter. For example, to get the menu items of Windows Calculator accessory I used InspectMenu.exe "Calculadora" (in Spanish) that show a three-level topmost menu with 21 total final item ID's. Below there are some of these values (with "CAL_" prefix) and the most useful item ID's of Windows Notepad text editor (with "NP_" prefix).

Code: Select all

rem Values for ItemID's in the topmost menu of calc.exe (Windows Calculator):
set /A CAL_Copy=0x012C, CAL_Paste=0x012D, CAL_GroupDigits=0x012F

rem Values for ItemID's in the topmost menu of Windows Notepad.exe:
set /A NP_New=0x0001, NP_Open=0x0002, NP_Save=0x0003, NP_SaveAs=0x0004, NP_Quit=0x0007
set /A NP_Cut=0x0300, NP_Copy=0x0301, NP_Paste=0x0302, NP_Delete=0x0303
set /A NP_Find=0x0015, NP_FindNext=0x0016, NP_Replace=0x0017, NP_SelectAll=0x0019

When I was writting this text (in Notepad), I execute InspectMenu and then copy the output from the screen via the rudimentary right-click mouse method in order to paste it into the Notepad document. After just two times of do that I tired of the cumbersome procedure, so I opted to write the following PasteInto.bat Batch file:

Code: Select all

@echo off
setlocal

rem PasteInto.bat: Take the screen contents of the current cmd.exe window
rem                and paste it into another Notepad active window
rem Antonio Perez Ayala

if "%~1" neq "" goto begin
echo Copy-paste the contents of the cmd.exe screen into a Notepad window
echo/
echo PasteInto "Title of Notepad recipient window"
goto :EOF

:begin

rem Define auxiliary values
call SendMessageValues.bat

rem Select all text in this window and copy it to clipboard
SendMessage %WM_SYSCOMMAND% %CMD_SelectAll%
SendMessage %WM_SYSCOMMAND% %CMD_Copy%

rem Activate the other window via Minimize/Restore trick
SendMessage "%~1" %WM_SYSCOMMAND% %SC_MINIMIZE%
SendMessage "%~1" %WM_SYSCOMMAND% %SC_RESTORE%

rem Paste clipboard contents into the other window
SendMessage "%~1" %WM_COMMAND% %NP_Paste%

The development of this program presented a problem: the items in the Notepad topmost menu can not be selected if the window was not active. I did some tests and discovered that this point depends on the relation that the cmd.exe window have (has?) with the other window and the particular program in the other window. For example, in the case of Calculator Windows accessory, its menu items can be selected when the window is not active, and even when the window is minimized! I tried to develop a method to activate other window from the cmd.exe one using just System-Defined messages. I did several tests using diverse combinations of WM_ACTIVATE, WM_CANCELMODE, WM_ENABLE, WM_ACTIVATEAPP, WM_SETFOCUS and WM_KILLFOCUS messages, with no success. Fortunately, the items in the system menu of any window can be selected from another window, and after a SC_RESTORE the restored window remains active; this behavior makes possible to activate Notepad and other windows from the cmd.exe one via a Minimize/Restore procedure.

The mechanism used in this Batch file allow us to use other Windows applications as "servers" for a "client" Batch file in the way described in Dynamic Data Exchange, although in a rudimentary way. The following Batch file is an example of this technique that use the Calculator Windows accessory to perform arithmetic operations.

Code: Select all

@echo off
setlocal

rem UseCalc.bat: Example of using Windows Calculator accessory as "server" for a "client" Batch file
rem Antonio Perez Ayala

rem Define auxiliary variables
call SendMessageValues.bat
set "1/x=R" & set "Inv=I" & set "Mod=D" & set "Log=L" & set "Int=;" & set "Sqrt=@"
set "Sin=S" & set "Cos=O" & set "Tan=T" & set "DMS=M" & set "F-E=V" & set "Pi=P"
set "Exp=X" & set "x^2=Q" & set "x^y=Y" & set "x^3=#" & set "Ln=N"

rem Start the "server" and minimize it
start calc
ping -n 2 localhost > NUL
SendMessage "Calculator" %WM_SYSCOMMAND% %SC_MINIMIZE%

rem Define a list of arithmetic expressions as examples
set "X=1.5"
for %%n in (^"^
% Do NOT remove this line %
^") do for /F "tokens=1,2" %%a in ("
   "e 1%Inv%%Ln%" %%~n
   "Pi %Pi%" %%~n
   "Ln(10) 10%Ln%" %%~n
   "Sqrt(2) 2%Sqrt%" %%~n
   "TanH(%X%) (%X%%Inv%%Ln%-(0-%X%)%Inv%%Ln%)/(%X%%Inv%%Ln%+(0-%X%)%Inv%%Ln%)" %%~n
   "(4+5)/(6+7) (4+5)/(6+7)" %%~n
   "(((4+5)*(2+3)+6)/(8+7))^9 (((4+5)*(2+3)+6)/(8+7))%x^y%9" "
) do (

   rem Copy the Calculator expression into clipboard
   < NUL (set /P "=%%~b="| clip)

   rem Paste the expression to Calculator
   SendMessage "Calculator" %WM_COMMAND% %CAL_Paste%

   rem Copy the result into clipboard
   SendMessage "Calculator" %WM_COMMAND% %CAL_Copy%

   rem Read the result from clipboard (paste clipboard)
   for /F %%c in ('PasteClip') do echo %%~a  =  %%c

)

rem Change number format to show a last result
SendMessage "Calculator" %WM_COMMAND% %CAL_GroupDigits%
echo 2%x^y%64=| clip
SendMessage "Calculator" %WM_COMMAND% %CAL_Paste%
SendMessage "Calculator" %WM_COMMAND% %CAL_Copy%
SendMessage "Calculator" %WM_COMMAND% %CAL_GroupDigits%
echo/
for /F %%c in ('PasteClip') do echo 2^^64  =  %%c

rem Close the "server"
SendMessage "Calculator" %WM_SYSCOMMAND% %SC_CLOSE%

Output:

Code: Select all

e  =  2.7182818284590452353602874713527
Pi  =  3.1415926535897932384626433832795
Ln(10)  =  2.3025850929940456840179914546844
Sqrt(2)  =  1.4142135623730950488016887242097
TanH(1.5)  =  0.9051482536448664382423036964565
(4+5)/(6+7)  =  0.69230769230769230769230769230769
(((4+5)*(2+3)+6)/(8+7))^9  =  60716.992766464

2^64  =  18,446,744,073,709,551,616

You should note that the names of all Windows accessories are language dependant. I copied the keys used to assemble the arithmetic expression for Calculator from a list of Keyboard shortcuts; the next step is test if WM_CHAR/WM_SYSCHAR message could be used to generate the rest of Alt- and Ctrl- Calculator keys. Although this Batch file is somewhat slow (because all those copy to/from clipboard commands), the interesting point is that it works! 8) :mrgreen: The next step is write a Batch file that use Notepad as server to replace strings in a text file. If I have success, I think that it would be possible to edit an Excell spreadsheet or access any other Window application in the same way! :shock: :D

Technicall note. The cmd.exe system menu include the CMD_Paste item ID that effectively paste the clipboard contents into cmd.exe keyboard input buffer; this means that such input could be get via a SET /P command or any other method that read input via Stdin. For example, the "Read the result from clipboard" part in previous Batch file could be written this way:

Code: Select all

rem Read the result from clipboard via SET /P
SendMessage %WM_SYSCOMMAND% %CMD_Paste%  &  rem Paste clipboard contents
SendMessage %WM_CHAR% %EnterKey%         &  rem Add an Enter to complete SET /P input
set /P "result="                         &  rem Read a line from clipboard in result variable

You may also use a FOR that run a filter command to get such input, like FIND or FINDSTR, or you may use a COPY CON FILE.TXT command to copy all lines from clipboard to a file, but in these cases it would be necessary to insert a Ctrl-Z before the Enter in order to mark the end of input for these commands. For example:

Code: Select all

rem Copy-Paste clipboard contents into Clipboard.txt file
SendMessage %WM_SYSCOMMAND% %CMD_Paste%  &  rem Paste clipboard contents
SendMessage %WM_CHAR% %Ctrl_Z%           &  rem Mark the End-Of-File for COPY command
SendMessage %WM_CHAR% %EnterKey%         &  rem Add an Enter to complete the input
copy con Clipboard.txt                   &  rem Do it!

These methods works correctly, but have a disadvantage: they all show in the screen the characters being pasted! This is logical because supposedly these characters are being typed in the keyboard, but they interfere with any program that require such paste operations. I tried to develop a method to avoid this text appearing in the screen, with no success. For this reason, I wrote PasteClip.exe program that performs the complementary function of native CLIP.EXE command: it read the clipboard contents and show it via its Stdout channel, so this output can be processed in the usual way with no problems.
SendMessage 2nd part.zip
InspectMenu.exe, PasteClip.exe, PasteClip.txt, PasteInto.bat and UseCalc.bat; and updated version of SendMessageValues.bat
(4.75 KiB) Downloaded 776 times

Antonio :P

Thor
Posts: 43
Joined: 31 Mar 2016 15:02

Re: SendMessage.exe: Access to advanced Windows features

#15 Post by Thor » 20 Dec 2016 11:03

Thanks for the useful tool.
I have a question regarding the use of the "Calculator" in "UseCalc.bat".
How could you define a key to use the "factorial" function, i.e. "n!"?
I want to calculate "5!"

Post Reply