Skipping 'press any key to continue' on external exe program - windows

I have a cmd line exe provided for me which I cannot change at and I need to write a script around, but it has a Pause in built to it and I cannot see any way to skip this pause so the rest of my script can continue.
I have tried all sorts of things, including
#echo | call program.exe
program.exe < nul
cmd /c echo y &echo.| program.exe
Jay's answer here
Variations and combinations of those
Checked the program /? to see if there's a skip pause toggle, but there is not
Appreciate any advice

You can send data to a process using interop. It's called hooking the process, and there are a few resources on it. I like this answer.
This is a little code that allows you to send message to a
backgrounded application. To send the "A" char for example, simply
call sendKeystroke(Keys.A), and don't forget to use namespace
System.windows.forms to be able to use the Keys object.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace keybound
{
class WindowHook
{
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
public static extern IntPtr PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
public static void sendKeystroke(ushort k)
{
const uint WM_KEYDOWN = 0x100;
const uint WM_SYSCOMMAND = 0x018;
const uint SC_CLOSE = 0x053;
IntPtr WindowToFind = FindWindow(null, "Untitled1 - Notepad++");
IntPtr result3 = SendMessage(WindowToFind, WM_KEYDOWN, ((IntPtr)k), (IntPtr)0);
//IntPtr result3 = SendMessage(WindowToFind, WM_KEYUP, ((IntPtr)c), (IntPtr)0);
}
}
}
You'll probably have an easier time than they did getting the application instead of searching for the process because you can start it from your application:
Process proc = new Process();
proc.StartInfo.FileName = executablePath;
proc.Start();
proc.WaitForInputIdle();
Then proc.Id will be the PID.
As an alternative, I just ran into a VB type of example that seems even simpler using the Shell function, but I haven't used it before. You'll need to add a pause in your application to wait for the prompt, but this seems cleaner to read than Interop:
Dim ProcID As Integer
' Start the Calculator application, and store the process id.
ProcID = Shell("CALC.EXE", AppWinStyle.NormalFocus)
' Activate the Calculator application.
AppActivate(ProcID)
' Send the keystrokes to the Calculator application.
My.Computer.Keyboard.SendKeys("22", True)
My.Computer.Keyboard.SendKeys("*", True)
My.Computer.Keyboard.SendKeys("44", True)
My.Computer.Keyboard.SendKeys("=", True)
' The result is 22 * 44 = 968.
If you wind up with a System.ArgumentException, it's probably because the Shell function didn't get a process ID. This is because it needs full trust. The application would work if run as administrator. I don't think you'd find an easy way around this if you can't do that since it's a security issue to have applications run each other, but I could be wrong.

Related

Can Powershell minimize ShareX UI when errors occur

I have some Windows 10 LTSC/IoT digital signs starting the ShareX screen capture application on boot like so:
"C:\Program Files\ShareX\ShareX.exe" -silent -startautocapture
After the Autocapture fires the Destination config is set to post the image to a URL but when network outages occur the ShareX UI spawns at a higher z-index than the Four Winds digital sign software display and remains there until manually closed or minimized.
I have been over the ShareX docs but this behavior isn't mentioned and the Custom Uploader options only include specifying an error message to display. Anyone have ideas on suppression or minimizing the UI? Possibly a PowerShell trick for shifting the focus?
Per #zett42 comment I started out with this answer. Unfortunately neither ShareX nor ShareX 8.14 is apparently the correct ConsoleWindowClass.
[int]$handle = [WPIA.ConsoleUtils]::FindWindow('ConsoleWindowClass','ShareX 8.14')
In the end this answer got it working using
$handle = $fw::FindWindow([IntPtr]::Zero, 'ShareX 8.14' )
Since this has a version number in the name I will need to sort out an enumeration strategy to iterate over the open windows and do substring matching but for now this succeeds.
I am very new to PowerShell so this may be ungracefully done.
$sig = #'
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(IntPtr sClassName, String sAppName);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int SendMessage(int hWnd, uint Msg, int wParam, int lParam);
[DllImport("kernel32.dll")]
public static extern uint GetLastError();
public const int WM_SYSCOMMAND = 0x0112;
public const int SC_MINIMIZE = 0xF020;
'#
$fw = Add-Type -Namespace WPIA -Name ConsoleUtils -MemberDefinition $sig -PassThru
$wname='ShareX 14.1' # any existing window name
[int] $handle = $fw::FindWindow([IntPtr]::Zero, $wname ) # returns the Window Handle
if ($handle -gt 0)
{
[void][WPIA.ConsoleUtils]::SendMessage($handle, [WPIA.ConsoleUtils]::WM_SYSCOMMAND, [WPIA.ConsoleUtils]::SC_MINIMIZE, 0)
}

Get parent process id by Window Handle

Is it possible to get process id based on it's child Window Handle in powershell?
For example, this script looking for Window Handle with titile "Warning":
$sig = #"
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(IntPtr sClassName, String sAppName);
[DllImport("kernel32.dll")]
public static extern uint GetLastError();
"#
$fw = Add-Type -Namespace Win32 -Name Funcs -MemberDefinition $sig -PassThru
$wname='Warning'
$find_window = $fw::FindWindow([IntPtr]::Zero, $wname )
Write-Host $find_window
How it is shown in Task Manager:
Program.exe
|
\Warning
Now i need to determine - what PID have this warning window. I know it is possible to implement in C# (which i don't use at all), but this should be done on powershell only.
You are looking for GetWindowThreadProcessId. According to this answer, you can call that from PowerShell with this p/invoke:
[DllImport("User32.dll")]
public static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
You supply a window handle in the first argument. The process and thread IDs are returned in the second argument and return value, respectively.

Hooking a window with SetWinEventHook sometimes doesn't work

I wrote some code to watch for window title changes. It works fine with different windows in my Windows 7. I use SetWinEventHook like that:
SetWinEventHook(EVENT_OBJECT_NAMECHANGE,
EVENT_OBJECT_NAMECHANGE,
0,
WinEventCallback,
processId,
threadId,
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS | WINEVENT_SKIPOWNTHREAD);
Callback:
void CALLBACK WinEventCallback(HWINEVENTHOOK hWinEventHook,
DWORD dwEvent,
HWND hwnd,
LONG idObject,
LONG idChild,
DWORD dwEventThread,
DWORD dwmsEventTime)
{
qDebug("Window %p", hwnd);
...
GetWindowText(hwnd, ...);
}
For one specific window I see the debug message "Window 0x0" all the time, e.g. I get the window handle set to zero in the callback. In this case GetWindowText fails. All other windows work fine. The question is why? I don't see anything extraordinary in Spy++:
Not all events generated may be associated with a window, especially for something as generic as a name change. The hook documentation specifically states that NULL windows are possible, so simply ignore them if your hook logic is window-oriented. If you are seeing a window change its title but you are getting a NULL window in your callback, then either it is not a real window, or there was an issue marshaling the window to your callback, or something like that.
The problem comes for the WinEventCallback's signature you are using.
Fix it by using this one: WinEventCallback(IntPtr hWinEventHook, uint iEvent, IntPtr hWnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)

Create a native Windows window in JNA and some GetWindowLong with GWL_WNDPROC

Good day,
I have been using JNA for a while to interact with the Windows API and now I am stuck when creating a window. As far as I have done the following:
1. Have created a child window of an existing window and obtained a valid handler to it.
2. Understood that every window in Windows has a non-stop message-dispatch loop.
3. Understood that the best way to include my window in the message-dispatch loop is to use something like the following code (not mine, but that is what I would do as well):
final LONG_PTR prevWndProc = new LONG_PTR(User32.INSTANCE.GetWindowLong(hwnd, User32.GWL_WNDPROC)); //this is to obtain a pointer to the WNDPROC of the parent window, which we are going to need later
wndProcCallbackListener = new WndProcCallbackListener()
{
public LRESULT callback(HWND hWnd, int uMsg, WPARAM uParam, LPARAM lParam)
{
if (uMsg == WTSAPI.WM_POWERBROADCAST)
{
System.out.println("WM_POWERBROADCAST Event: hWnd="+hwnd+", uMsg="+uMsg+", uParam="+uParam+", lParam="+lParam);
}
else if (uMsg == WTSAPI.WTS_SESSION_CHANGE)
{
System.out.println("WTS_SESSION_CHANGE Event: hWnd="+hwnd+", uMsg="+uMsg+", uParam="+uParam+", lParam="+lParam);
}
//Call the window's actual WndProc so the events get processed.
return User32.INSTANCE.CallWindowProc(prevWndProc, hWnd, uMsg, uParam, lParam);
}
};
//Set the WndProc function to use our callback listener instead of the window's one.
int result = User32.INSTANCE.SetWindowLong(hwnd, User32.GWL_WNDPROC, wndProcCallbackListener);
However, my problem is when I call the GetWindowLong() for the parent window (my first line of code) I get a 0 for the pointer which indicated the function did not complete successfully. A subsequent call to GetLastError() and a quick check in the error codes give me an 'Access is denied' error. This, of course, is logical, since I am trying from my own thread to access the address of the WNDPROC of another, but I was wondering if there is any way (there should be, of course) to circumvent that.
Any pointers? (pun intended)
Do not use GetLastError() after a JNA call. JNA & JNI may call other APIs that may change the last error. Declare SetWindowLong with the clause throws LastErrorException, like this:
int SetWindowLongA(int hWnd, int nIndex, WndProcCallbackListener dwNewLong)
throws LastErrorException;
Notice the 'A' after the name. It makes explicit use of ANSI version. You could use SetWindowLongW as well.
Make sure your callback implements both Callback and StdCall. I prefer using primitive types as much as possible, because this makes mapping fast and obvious to JNA:
public interface WndProcCallbackListener extends Callback, StdCall {
int callback(int hWnd, int Msg, int wParam, int lParam);
}

How to turn screensaver on (windows 7) by a code (in cmd)? [closed]

Closed. This question is off-topic. It is not currently accepting answers.
Want to improve this question? Update the question so it's on-topic for Stack Overflow.
Closed 10 years ago.
Improve this question
How to turn screensaver on (windows 7) by a code (in cmd)?
Does the following meet your requirements?
start logon.scr /s
As long as the .scr is on the PATH the above command should work.
EDIT: I don't know if Windows 7 comes with logon.scr, make sure you're testing it with a .scr that is actually installed in Windows 7.
Note that I got the idea of just invoking the .scr with /s from Screensaver Sample Command Line Options:
When Windows runs your screensaver, it
launches it with one of three command
line options:
/s – Start the screensaver in full-screen mode.
/c – Show the configuration settings dialog box.
/p #### – Display a preview of the screensaver using the specified
window handle.
EDIT 2:
I did some additional searching and found that you could create lock.cmd:
#start /wait logon.scr /s & rundll32 user32.dll,LockWorkStation
Or lock.vbs:
Set objShell = CreateObject("Wscript.Shell")
' The "True" argument will make the script wait for the screensaver to exit
returnVal = objShell.Run("logon.scr", 1, True)
' Then call the lock functionality
objShell.Run "rundll32.exe user32.dll,LockWorkStation"
Neither of these answers is perfect, both reveal a flicker of the desktop after the screen saver is disabled and just prior to the workstation being locked.
It may not be possible to reproduce the system behaviour of starting the screen saver and password protecting on resume. Even the answer to Launch System Screensaver from C# Windows Form only starts the screen saver, it does not password protect on resume.
Putting together the cmd and vbs script ideas with the code from the answer to Launch System Screensaver from C# Windows Form I came up with the following:
using System;
using System.Runtime.InteropServices;
public static class LockDesktop
{
[DllImport("user32.dll", EntryPoint = "GetDesktopWindow")]
private static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);
[DllImport("user32.dll", EntryPoint = "LockWorkStation")]
private static extern IntPtr LockWorkStation();
private const int SC_SCREENSAVE = 0xF140;
private const int WM_SYSCOMMAND = 0x0112;
public static void SetScreenSaverRunning()
{
SendMessage(GetDesktopWindow(), WM_SYSCOMMAND, SC_SCREENSAVE, 0);
LockWorkStation();
}
public static void Main()
{
LockDesktop.SetScreenSaverRunning();
}
}
To build it, install the .NET Framework, copy and paste the above code into lock.cs, then run:
%SystemRoot%\Microsoft.NET\Framework\v3.5\csc.exe lock.cs
Put the created lock.exe in your path, after that, typing lock should engage the configured screen saver and lock your workstation.
using System;
using System.Runtime.InteropServices;
public static class LockDesktop
{
[DllImport("user32.dll", EntryPoint = "GetDesktopWindow")]
private static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);
private const int SC_SCREENSAVE = 0xF140;
private const int WM_SYSCOMMAND = 0x0112;
public static void SetScreenSaverRunning()
{
SendMessage(GetDesktopWindow(), WM_SYSCOMMAND, SC_SCREENSAVE, 0);
}
public static void Main()
{
LockDesktop.SetScreenSaverRunning();
}
}
This works - only downside is that u cant interact with pc for something like 7 sec, but i guess its 7's to give ppl time before making screensaver 'permanent'.
I have Windows 7. I placed the line:
#start /wait %windir%\ExtraPath\ScreenSaverName.scr /s & rundll32 user32.dll,LockWorkStation
in a batch (.bat) file, place it in a appropriate dir, and created a shortcut pointing to this, with the desired shortcut key.
In this line, \ExtraPath is the additional path under your win dir (usually this is \system32) where the screen savers are located, and ScreenSaverName.scr is the name of the desired screen saver itself.
It works perfectly.
Now I can press the shortcut keys to run the screen saver and lock the pc.
You could try Powershell Script To Start A Random Screen Saver.

Resources