How to automate UI keyboard input? pywin32 PostMessage vs Powershell SendKeys - powershell

Motivation: I'm using a software that doesn't have an API to interface with ... I have no alternative and have to open the software, send simple key sequence, then close ... Again and again, so I want to automate this process.
Goal: Send combinations of keyboard inputs to an inactive window.
Progress: I wrote a powershell script that open, send keys, wait, then end the process, but it only works on active window. A part of the powershell code is as follows.
$appProcess = Start-Process -FilePath $path -PassThru
$wshell = New-Object -ComObject wscript.shell;
$wshell.AppActivate($appProcess.Id)
$wshell.SendKeys('%(E)E')
Stop-Process $appProcess -Force
It works, but only on active window (windows comes to the top). What I want is to run automate the window in the background. I found an article that point me to using PostMessage in Win32 API. Since the majority of my code uses python, I decided to move from powershell to pywin32.
Issue: I cannot get the PostMessage to send key to the right handler. I saw in this article that I may need to find the exact window, but I still don't really understand how. In powershell, I can directly send keys via $wshell.AppActivate($appProcess.Id).
hwndMain = win32gui.FindWindow(None, winname)
hwndChild = win32gui.GetWindow(hwndMain, win32con.GW_CHILD)
temp = win32api.PostMessage(hwndChild, win32con.WM_KEYDOWN, 0x45, 0)
# temp came out as None
Question: Is there a way to do this in pywin32 / Win32 API?
Edit: (May 8, 2020) Yes, I have heard that using SendKeys are not reliable, but since there is no alternative offered to questions like this one on SO, how should anyone learn the "right way"? If you think there is alternative, everyone will appreciate to see a solution in action. Please suggest edits to my post to improve the quality of the question instead of shooting it down.

Time ago I developed SendMessage utility that allows to post messages to other processes or Windows via the WIN32 API. I discovered that I can't send a message to an inactive window, so I searched for a method to activate a Window. After several tests, I found a trick that allows me to "reactivate" a Window:
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.
I think that using my SendMessage.exe utility you may test if you may send keys to your inactive window process or to activate it, so you may then translate such a method to your phyton code. For complete details on this matter, see this link.

Related

PowerShell Open URL in New Window - [system.Diagnostics.process]::Start("","")

So I'm using PowerShell (specifically version 5.1) in order to write a script for my work that essentially opens some websites depending on what the user selects from available options.
In order to open the website (for this example, let's just say I'm going to Google), I'm using [system.Diagnostics.Process]::Start("chrome","https://www.google.com"). This does open the website, but I'm attempting to open it into a new window entirely. In current state, I can only accomplish this by opening a new window manually and then running the script.
Any suggestions on any switches or anything that might work? I've seen a few on other sites talking about --new-window or -new = 1, but those were for other commands and don't seem to work on this one.
I'm also specifically using Chrome because the sites I am attempting to open require a sign-in, so I already have a Chrome Window open, and it defaults to opening all of my new tabs on the same window, which is what I'm attempting to avoid.
Any help would be greatly appreciated!
The Chrome CLI parameter that requests opening a given URL in a new window is -new-window
It's simpler to use PowerShell's Start-Process cmdlet rather than the underlying .NET API directly.
Therefore:
Start-Process chrome.exe '-new-window https://www.google.com'

Powershell won't pass keystroke to cli window

I first need to offer the disclaimer that I am barely a novice when it comes to scripting or coding, so I might be doing this all wrong in the first place. I'm running into an issue with a script I had written in powershell a while ago that is used on several hundred computers in my company daily to automate starting a certain app that everyone uses. So far everyone else hasn't had an issue except one machine.
Normally when I run the script, it starts the services and cli for the program which opens in a new window. Then the script, still open behind that, sends all of the key commands to that cli window, pauses for a brief line of user input after which it closes that cli window, does a few other clean up tasks and closes out itself.
The only difference on this one machine that's not working is the script won't pass the keystrokes to the cli window. If I put them in manually including the normal user input, it finishes the rest of the script just fine, but the cli window is just not receiving the keystroke input from the main script.
The only part of the script that send the keystrokes to the cli window is a few lines of this:
[System.Windows.Forms.SendKeys]::SendWait("Text{Enter}")
I have tried using the sleep command before and in between the key commands to make sure it's not going through the keystrokes to early. I've checked and windows had gone through an Update two days ago, but we would've heard about this issue right then if that was it. I'm at a loss for what the issue could be and would apreciate and help, tips, or direction to help figure this one out.
Is the console window the active window when the keys are sent? The documentation for the class you are calling indicates that it sends the keys to the active window. Not only that, but the documentation also seems to indicate that there are some issues that developers have run into when using that class to sends keys. I would try using the autoit powershell module instead. Autoit is its own scripting language which specializes in automating windows processes and being capable of automating interactions with windows forms, but also comes bundled with its own powershell module that I think is exactly what you need, so I would download the portable "zip" package, extract the powershell module from "Autoitx", and that should help you accomplish what you need
PowerShell automation especially when using SendKeys can / is glitchy due to many varying reasons. Can you use SendKeys sure, but you have to know the environment it will be run in and the needed performance details. Hence your futzing/guessing with Sleep.
There are purpose-built tools to help.
Auto HotKey
'PowerShell auto hotkey'
or the UIAutomation tool
'PowerShell automating other applications'

PowerShell script problem, cause of lacking knowledge

I have kinda 0 expirience of coding but wanted to get myself a script which would take some work of me.'
Started this yesterday and just googled myself everything together.
What I want to do is open a vnc viewer(remote desktop app for our raspberrys), copy paste a link there, log into the website, fullscreen it and leave the session of vnc viwer, go into the next pi (we have 6 different where we need to do this every 3 hours) and do exactly the same.
I get everything together just with simple phrases like:
$wshell.SendKeys("{TAB}")
$wshell.SendKeys("{Enter}")
etc. but the only problem I have no is to close the session of vncviewer without killing the overview console(both have different tasks in task manager but named the same, only different is the ProcessID but this is changing for every start.
I figured out myself 3 different solutions but couldn't find a working answer on this in different forums or tech discords.
LF a command like" (New-Object -ComObject WScript.Shell).AppActivate((Kill-Process vncviewer).MainWindowTitle) " which would kill only the vncviewer which is the main window atm (normaly this command is exactly the same only swap "Kill-Process" for "get-process" but I just wrote kill process to better descripe what Im looking for) and would leave the overview console open.
Get a command like .SendKeys for MouseClicks so I could work with fix "Set mouse coordinate" commands in PowerShell and then just simulate a mouseclick to close the window.
Do a "Get-Process | Out-File" and a "Get-Content" in the next line to write and read the processes which are running atm, to terminate the ProcessID of the session of nvcviewer to kill it via "Stop-Process "ProcessID"".
But couldnt find a command for "Get-Content | Where-Object_Name = "nvcviewer"" or smth like this to know what ProcessID its running atm, problem would be I would get 2 process IDs without knowing which one to kill.
Would be great to get some help on this, sorry for my bad english/grammar its not my native language and I'm rly new to coding etc so if you have a answer for me would be great to get this explained for an 5year old ^^ + I'm not allowed to install 3rd party software so all needs to be in PowerShell.

Keyboard Shortcut for Quitting Powershell

If you can start powershell in admin privileges with keyboard shortcut WIN+X>A, how do you exit it. Alt+F4 does not work. I have tried other possible combinations but none seems to work.
Note: I am not asking about Powershell ISE(which i can close with Alt+f4).
I am asking about the on-demand readily available terminal powershell.
To close a PowerShell window using the system menu you can use
Alt + Spacebar, C
Nearly every Windows window has this option. The Close or Exit option may differ based on the context so you'll just have try it and see.
This answer is accurate for the time of its writing but things may change.
Update: If you're using Windows Terminal, no workaround is needed: Alt-F4 works as-is.
The following therefore only applies to regular (conhost.exe) console windows.
Note:
For an ad hoc solution that uses different keys (strictly speaking, a keyboard shortcut followed by an additional, UI-language-dependent keypress), but requires no setup, see No Refunds No Returns's answer.
This answer provides a workaround that works transparently, i.e., with the original shortcut key, but it requires modification of your $PROFILE file; on the plus side, you can use the same technique to make other unavailable shortcuts available too.
It is the imported-by-default PSReadline module that prevents Alt+F4 from being effective[1].
While unloading PSReadline (Remove-Module -Force PSReadline) is an option in principle, you'll lose valuable command-line editing features if you do.
The best approach is therefore to define a Alt+F4 handler for PSReadline that emulates the default behavior:
Add the following to your $PROFILE, which sets up a PSReadline key-chord handler that uses the .SendKeys() method of the WScript.Shell COM object to send Alt+F4 to the caller's console window, which closes it:
Set-PSReadlineKeyHandler Alt+F4 -ScriptBlock {
(New-Object -ComObject WScript.Shell).SendKeys('%{F4}')
}
[1] As of v2.0.0-beta3. The longstanding underlying technical problem is explained in this GitHub issue, which also contains workarounds for other standard keyboard shortcuts, such as Ctrl+F.

Gently Terminate Explorer.exe

I have done a lot of searching but I have not been able to discover a straightforward way to gently terminate the explorer shell in an XP batch file.
Why would I want to, you ask (since almost every time someone asks this someone else wants to know why)? The answer is simply this: I run a batch job at the end of the day which saves my toolbar configuration. When the explorer shell terminates normally, it writes the toolbar configuration to a registry key, which can then be exported and restored after I run CCleaner at the beginning of the week. If explorer is terminated with, for example, taskkill, it usually fails to write the registry key and the resulting export file does not always accurately reflect the current configuration of the toolbars.
I have tried using CMDOW "program manager" /cls but this just brings up the "Shut Down Windows" dialog - sometimes it makes the computer shut down even if you hit the "Cancel" button. Since bringing up the "Shut Down Windows" dialog and hitting "Cancel" while holding down "Ctrl, Alt, Shift" will cause explorer to gently die, I have looked at a vbs script using sendkeys but sending "%{F4}" assumes that the desktop is the foremost window. I thought I might try coding some HotKeyP macros - it has the capability of raising the "Shut Down Windows" dialog - but once the "Shut Down Windows" dialog is raised, HotKeyP can't seem to access it.
What message is Explorer receiving so that it knows to terminate gently as opposed to shut the whole computer down - that's what I want to know.
The following command line text will terminate any EXPLORER.EXE process but not shut down the computer.
taskkill /fi "imagename eq EXPLORER.EXE" /f /t
to know more about taskkill use the /? parameter.
I don't think Microsoft provides a way to do what you describe without scripting the Explorer UI. The prescribed way to close explorer and save all settings is to log off.
There are generally three ways to close a program:
TerminateProcess()
This causes the kernel to stop scheduling the process threads, and unloads the process from memory. As you rightly point out, this doesn't give explorer a chance to save its settings.
This is what "taskkill /f" does.
Send a WM_CLOSE message to the program's main window
Programs can decide to handle this however they want. Most will close, but Explorer treats it as a signal to log off, which is not what you want.
Use some program-specific method of asking it to shut down.
Explorer.exe does provide this via the GUI (ctrl+shift+rightclick on any non-button part of the start menu), but it does not provide a command-line way to do this.
If you are really set in what you want to do, you can use wscript or another UI scripting library to manipulate the GUI and perform option 3.