Convert PowerShell Command to Script - powershell

The PowerShell command below turns off the screen when run from a batch file (or command prompt). I would prefer to run this as a PowerShell script.
Turn Off Screen - TechNet Script Center
powershell (Add-Type '[DllImport(\"user32.dll\")]^public static
extern int SendMessage(int hWnd, int hMsg, int wParam, int lParam);'
-Name a
-Pas)::SendMessage(-1,0x0112,0xF170,2)
I looked at Add-Type - Microsoft Docs, but I could not get the parameters correct.
What is the equivalent PowerShell script for this?

Add-Type -MemberDefinition #"
[DllImport("user32.dll")]
public static extern int SendMessage(int hWnd, int hMsg, int wParam, int lParam);
"# -Name "Win32SendMessage" -Namespace "Win32Functions"
[Win32Functions.Win32SendMessage]::SendMessage(-1,0x0112,0xF170,2)

Related

PowerShell: AppActivate with PID not working

Under Windows 10, I would like to send a keypress to an existing and running windows application. The sending of the keypress works fine but the AppActivate via the PID of the windows application does not work. Here my code:
Function SendCommandToExistingProcess([int] $processId, [string] $processName, [string] $command)
{
$functName = 'SendCommandToExistingProcess()' # function name for log
Add-Type -AssemblyName Microsoft.VisualBasic
Add-Type -AssemblyName System.Windows.Forms
[Microsoft.VisualBasic.Interaction]::AppActivate($processId)
[System.Windows.Forms.SendKeys]::SendWait($command)
WriteLogEntry -severity $sevInfo -functName $functName `
-entryText ("Command '" + $command + "' sent to process '" + `
$processName + "' with id '" + $processId)
}
$processId contains the PID of the windows application to set focus to
$command contains the keypress to send ('p')
For AppActivate I use the PID instead of the application windows title because the application title contains two special characters (similar to the copyright sign). It seems that AppActivate only works with the title (tested this successfully) but not with the PID though the AppActivate documentation shows an AppActivate overlay that acceptes a PID. I tried to set focus to the windows calculator by typing its PID as a number directly in AppActivate; did not work.
*** Update ***
The PID is obtained the following way:
Function SendCommandToProcess([string] $processName, [string] $command)
{
$result = $false # initialise to process does not exist
$functName = 'SendCommandToProcess()' # function name for log
$processId = (Get-Process -Name $processName -erroraction 'silentlycontinue').Id
if ($processId.Count -gt 0) # procss(es) exist(s)
{ # normally just one process but could be serveral
Foreach ($id IN $processId)
{ SendCommandToExistingProcess -processId $id -processName $processName -command $command }
# send command to each of them
$result = $true # command sent to specified process
}
else
{
WriteLogEntry -severity $sevWarning -functName $functName `
-entryText ("Process '" + $processName + "' not found (not running)")
}
return $result
}
$processName contains the string 'Prepar3D'
When I run the above code in PowerShell with admin rights I get the following error message:
Ausnahme beim Aufrufen von "AppActivate" mit 1 Argument(en): "Der Prozess {0} wurde nicht gefunden."
English: Exception when calling "AppActivate' with 1 argument(s): "The process {0} could not be found"
What is fooling me? Thanks for your help
Hannes
I have the same problem as I want to focus on an application called aces.exe. The solution that worked for me was following this guide:
https://powershell.one/powershell-internals/extending-powershell/vbscript-and-csharp#c-to-the-rescue
In this, Dr. Tobias Weltner, talks about 3 ways to focus on application windows. The one that will work for you is the Chapter: C# to the Rescue.
I used the admin ISE-PowerShell and copied the code from the link above. (Chapter: C# to the Rescue)
DO NOT COPY THIS CODE BUT FOLLOW THE INSTRUCTION AT THE LINKED PAGE.
This is what code I copied and used:
PS C:\Windows\system32> $code = #'
using System;
using System.Runtime.InteropServices;
namespace API
{
public class FocusWindow
{
[DllImport("User32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern bool AttachThreadInput(IntPtr idAttach, IntPtr idAttachTo, bool fAttach);
[DllImport("User32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("User32.dll")]
private static extern IntPtr GetWindowThreadProcessId(IntPtr hwnd, IntPtr lpdwProcessId);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32")]
private static extern int BringWindowToTop(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool SystemParametersInfo(uint uiAction, uint uiParam, IntPtr pvParam, uint fWinIni);
private const uint SPI_GETFOREGROUNDLOCKTIMEOUT = 0x2000;
private const uint SPI_SETFOREGROUNDLOCKTIMEOUT = 0x2001;
private const int SPIF_SENDCHANGE = 0x2;
private const int SW_HIDE = 0;
private const int SW_SHOWNORMAL = 1;
private const int SW_NORMAL = 1;
private const int SW_SHOWMINIMIZED = 2;
private const int SW_SHOWMAXIMIZED = 3;
private const int SW_MAXIMIZE = 3;
private const int SW_SHOWNOACTIVATE = 4;
private const int SW_SHOW = 5;
private const int SW_MINIMIZE = 6;
private const int SW_SHOWMINNOACTIVE = 7;
private const int SW_SHOWNA = 8;
private const int SW_RESTORE = 9;
private const int SW_SHOWDEFAULT = 10;
private const int SW_MAX = 10;
public static void Focus(IntPtr windowHandle)
{
IntPtr blockingThread = GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero);
IntPtr ownThread = GetWindowThreadProcessId(windowHandle, IntPtr.Zero);
if (blockingThread == ownThread || blockingThread == IntPtr.Zero)
{
SetForegroundWindow(windowHandle);
ShowWindow(windowHandle, 3);
}
else
{
if (AttachThreadInput(ownThread, blockingThread, true))
{
BringWindowToTop(windowHandle);
SetForegroundWindow(windowHandle);
ShowWindow(windowHandle, SW_MAXIMIZE);
AttachThreadInput(ownThread, blockingThread, false);
}
}
if (GetForegroundWindow() != windowHandle)
{
IntPtr Timeout = IntPtr.Zero;
SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, Timeout, 0);
SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, IntPtr.Zero, SPIF_SENDCHANGE);
BringWindowToTop(windowHandle);
SetForegroundWindow(windowHandle);
ShowWindow(windowHandle, SW_MAXIMIZE);
SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, Timeout, SPIF_SENDCHANGE);
}
}
}
}
'#
# remove -PassThru in production. It is used only to
# expose the added types:
Add-Type -PassThru -TypeDefinition $code
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False FocusWindow System.Object
PS C:\Windows\system32> Add-Type -PassThru -TypeDefinition $code |
Where-Object IsPublic |
Select-Object -Property FullName
FullName
--------
API.FocusWindow
PS C:\Windows\system32> # get the main window handle for the process
# you want to switch to the foreground:
# in this example, the first instance of notepad is used
# (make sure notepad runs)
$process = Get-Process -Name aces -ErrorAction SilentlyContinue |
Select-Object -First 1
$mainWindowHandle = $process.MainWindowHandle
if (!$mainWindowHandle)
{
Write-Host "Window may be minimized, or process may not be running" -Foreground Red
}
# focus application window (and maximize it)
[API.FocusWindow]::Focus($mainWindowHandle)
PS C:\Windows\system32>
I also tried to use the nuget pack "PSOneApplicationWindow" that is in the first chapter in the link provided and that did not work to get focus on the aces.exe.
I use the Main Window Handle to solve this:
Note I have a com object built named $ie
$app = Get-Process | ?{$_.MainWindowHandle -eq $ie.hwnd}
[Microsoft.VisualBasic.Interaction]::AppActivate($app.Id)
sleep -Milliseconds 50
So build the object, search the process by the hwnd property, pass that as the object and make that the active app. Don't forget to add the sleep to give the app a moment to pop up before moving on to the next command. It makes the call much more stable.

Set console Top-Most in PowerShell

So while there is much advise about how to set forms topmost, i couldnt find anything that makes my console run topmost.
So my question: How do I make my console run top-most during a script?
This requires some .NET interop, as detailed in this blog:
Scripts From TechEd 2012… Part 1 (Keeping PowerShell Window On Top)
I've copied the relevant code below in case the linked site disappears:
$signature = #'
[DllImport("user32.dll")]
public static extern bool SetWindowPos(
IntPtr hWnd,
IntPtr hWndInsertAfter,
int X,
int Y,
int cx,
int cy,
uint uFlags);
'#
$type = Add-Type -MemberDefinition $signature -Name SetWindowPosition -Namespace SetWindowPos -Using System.Text -PassThru
$handle = (Get-Process -id $Global:PID).MainWindowHandle
$alwaysOnTop = New-Object -TypeName System.IntPtr -ArgumentList (-1)
$type::SetWindowPos($handle, $alwaysOnTop, 0, 0, 0, 0, 0x0003)
Edit:
As described in the comments: If you're from a batch file, PowerShell runs in a child process and doesn't own the console window, so you'll have to make changes:
$signature = #'
[DllImport("kernel32.dll")] public static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
public static extern bool SetWindowPos(
IntPtr hWnd,
IntPtr hWndInsertAfter,
int X,
int Y,
int cx,
int cy,
uint uFlags);
'#
$type = Add-Type -MemberDefinition $signature -Name SetWindowPosition -Namespace SetWindowPos -Using System.Text -PassThru
$handle = $type::GetConsoleWindow()
$type::SetWindowPos($handle, -1, 0, 0, 0, 0, 0x0003)

How to stop result of function showing in Command Prompt

I have the below code in Powershell which brings the Command Prompt Window back to the foreground while running a script. This works just fine, but it returns "True" in the Command Prompt window each time. Is there a way to hide/remove this without using cls?
Add-Type -Name Window -Namespace Console -MemberDefinition '
[DllImport("Kernel32.dll")]
public static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow);
'
function Show-Console {
$consolePtr = [Console.Window]::GetConsoleWindow()
[Console.Window]::ShowWindow($consolePtr, 9)
}
Show-Console
You could call your function like that:
Show-Console | Out-Null

FindWindow Method error

I am trying get window of the Notepad using the following PowerShell script:
$pinvokes = #'
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr Connect(string className, string Notepad);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetForegroundWindow(IntPtr hWnd);
'#
Add-Type -AssemblyName System.Windows.Forms
Add-Type -MemberDefinition $pinvokes -Name NativeMethods -Namespace MyUtils
$hwnd = [MyUtils.NativeMethods]::FindWindow($null, "Notepad")
But when I use FindWindow() I am getting the error below:
Method invocation failed because [MyUtils.NativeMethods] doesn't contain a method named 'FindWindow'.
At line:1 char:44
+ $hwnd = [MyUtils.NativeMethods]::FindWindow <<<< ($null, "Notepad")
+ CategoryInfo : InvalidOperation: (FindWindow:String) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
You haven't specified the "FindWindow" method in your $pinvokes definition.
Do the following after e.g. your last method in $pinvokes:
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
Example:
$pinvokes = #'
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr Connect(string className, string Notepad);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
'#
Add-Type -AssemblyName System.Windows.Forms
# Using Passthru for example to show you how to return type directly
$t = Add-Type -MemberDefinition $pinvokes -Name NativeMethods -Namespace MyUtils -PassThru
$hwnd = $t::FindWindow($null, "Notepad")

Get PID of COM server

I create a com object in powershell like so:
$application = new-object -ComObject "word.application"
Is there a way to get the PID (or some other unique identifier) of the launched MS Word instance?
I want to check if the program is blocked e.g. by modal dialogs asking for passwords, and I can't do it from whithin PowerShell.
Ok, I found out how to do it, we need to call the Windows API. The trick is to get the HWND, which is exposed in Excel and Powerpoint, but not in Word. The only way to get it is to change the name of the application window to something unique and find it using "FindWindow". Then, we can get the PID using the "GetWindowThreadProcessId" function:
Add-Type -TypeDefinition #"
using System;
using System.Runtime.InteropServices;
public static class Win32Api
{
[System.Runtime.InteropServices.DllImportAttribute( "User32.dll", EntryPoint = "GetWindowThreadProcessId" )]
public static extern int GetWindowThreadProcessId ( [System.Runtime.InteropServices.InAttribute()] System.IntPtr hWnd, out int lpdwProcessId );
[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
}
"#
$application = new-object -ComObject "word.application"
# word does not expose its HWND, so get it this way
$caption = [guid]::NewGuid()
$application.Caption = $caption
$HWND = [Win32Api]::FindWindow( "OpusApp", $caption )
# print pid
$myPid = [IntPtr]::Zero
[Win32Api]::GetWindowThreadProcessId( $HWND, [ref] $myPid );
"PID=" + $myPid | write-host
you may be able to use
get-process -InputObject <Process[]>