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.
Related
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)
}
The question is mostly in the title. I am trying to write an example using this method however when I run it with the ALL flag and the handle for a process I get a -1 returned instead of a valid handle to a snapshot and when calling GetLastError I get 2 (The system cannot find the file specified.)
My question is does the th32ProcessID referenced in the MSDN link refer to a normal process handle or is there a different way to get this process ID?
I don't have a great deal of code for this at the moment but what I do have is below:
[DllImport("kernel32", SetLastError = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)]
internal static extern IntPtr CreateToolhelp32Snapshot([In] SnapshotFlags dwFlags, [In] IntPtr th32ProcessID);
IntPtr Handle = CreateToolhelp32Snapshot(SnapshotFlags.All, ProcessHandle);
Console.WriteLine("ProcessHandle = {0}", ProcessHandle.ToString("X"));
uint flags = 0;
bool result = GetHandleInformation(ProcessHandle, out flags);
Console.WriteLine("Last error = {0} and handle is valid = {1}", WinErrors.GetLastWin32Error(), result);
Console.WriteLine((int)Handle);
A process HANDLE is not the same thing as a process ID. They are not interchangeable.
CreateToolhelp32Snapshot() takes a process ID. And that parameter is a DWORD, so you should be using (u)int (aka (U)Int32), not IntPtr.
GetHandleInformation() takes a process HANDLE.
Since you are passing the wrong type of parameter value to CreateToolhelp32Snapshot(), it is failing, returning INVALID_HANDLE_VALUE, and then GetLastError() is telling you that the specified process ID was not found.
You can get a process HANDLE from a process ID by using OpenProcess().
You can get a process ID from a process HANDLE by using GetProcessId().
I am trying to maximize my skype window from powershell.
I use the following script...
$sig = '[DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);'
Add-Type -MemberDefinition $sig -name NativeMethods -namespace Win32
$hwnd = #(Get-Process lync)[0].MainWindowHandle
# Restore window
[Win32.NativeMethods]::ShowWindowAsync($hwnd, 4)
I also tried
$hwnd = #(Get-Process -id 2560)[0].MainWindowHandle
Info
Major Minor Build Revision
----- ----- ----- --------
5 1 14409 1012
But when I run the command it doesn't maximize, just returns true. Can I maximize a Skype window from poershell?
You were close in your question statement, but you are using the wrong constant.
$SW_MAXIMIZE = 3
$sig = #'
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
'#
Add-Type -MemberDefinition $sig -Name Functions -Namespace Win32
$hWnd = (Get-Process -Name lync).MainWindowHandle
[Win32.Functions]::ShowWindow($hWnd, $SW_MAXIMIZE)
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.
I've been trying to write a program in C++ that will monitor running processes in the background and terminate a certain one if it's detected to be running. I have written a program that will do so, however the only way I can think of to do this is to use an infinite WHILE loop that keeps checking for the program. This, as you can imagine, constantly uses CPU power and resources to be constantly looping. In task manager, you can see that most processes that are running are always using 0% of the CPU. My question is: How can I write or modify this program to run in the background, utilizing 0% of the CPU until it detects the process it's supposed to terminate?
My entire program is below. In this example, I have used "Notepad.exe" in WinMain as the process the program should be terminating.
#include <Windows.h>
#include <iostream>
#include <tlhelp32.h>
#include <string>
#define TA_FAILED 0
#define TA_SUCCESS_CLEAN 1
#define TA_SUCCESS_KILL 2
DWORD WINAPI TerminateApp(DWORD dwPID, DWORD dwTimeout);
DWORD WINAPI Terminate16App(DWORD dwPID, DWORD dwThread, WORD w16Task, DWORD dwTimeout);
typedef struct {
DWORD dwID;
DWORD dwThread;
} TERMINFO;
BOOL CALLBACK TerminateAppEnum( HWND hwnd, LPARAM lParam ) ;
DWORD WINAPI TerminateApp( DWORD dwPID, DWORD dwTimeout ) {
HANDLE hProc ;
DWORD dwRet ;
// If we can't open the process with PROCESS_TERMINATE rights,
// then we give up immediately.
hProc = OpenProcess(SYNCHRONIZE|PROCESS_TERMINATE, FALSE,
dwPID);
if(hProc == NULL) {
return TA_FAILED ;
}
// TerminateAppEnum() posts WM_CLOSE to all windows whose PID
// matches your process's.
EnumWindows((WNDENUMPROC)TerminateAppEnum, (LPARAM) dwPID) ;
// Wait on the handle. If it signals, great. If it times out,
// then you kill it.
if(WaitForSingleObject(hProc, dwTimeout)!=WAIT_OBJECT_0)
dwRet=(TerminateProcess(hProc,0)?TA_SUCCESS_KILL:TA_FAILED);
else
dwRet = TA_SUCCESS_CLEAN ;
CloseHandle(hProc) ;
return dwRet ;
}
BOOL CALLBACK TerminateAppEnum( HWND hwnd, LPARAM lParam ) {
DWORD dwID ;
GetWindowThreadProcessId(hwnd, &dwID) ;
if(dwID == (DWORD)lParam) {
PostMessage(hwnd, WM_CLOSE, 0, 0) ;
}
return TRUE ;
}
DWORD FindProcessId(const std::string& processName);
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) {
std::string process1 = "Notepad.exe";
while (1) {
TerminateApp(FindProcessId(process1),0);
}
return 0;
}
DWORD FindProcessId(const std::string& processName) {
PROCESSENTRY32 processInfo;
processInfo.dwSize = sizeof(processInfo);
HANDLE processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (processesSnapshot == INVALID_HANDLE_VALUE) {
return 0;
}
Process32First(processesSnapshot, &processInfo);
if (!processName.compare(processInfo.szExeFile)) {
CloseHandle(processesSnapshot);
return processInfo.th32ProcessID;
}
while (Process32Next(processesSnapshot, &processInfo)) {
if (!processName.compare(processInfo.szExeFile)) {
CloseHandle(processesSnapshot);
return processInfo.th32ProcessID;
}
}
CloseHandle(processesSnapshot);
return 0;
}
You can use WMI and event notification to find when processes are created and destroyed. __InstanceCreationEvent is what you need to look for.
Creation of a resource: __InstanceCreationEvent
Suppose you are interested in receiving a notification if Notepad is run on a certain computer. When Notepad runs, a corresponding process is created. Processes can be managed by using WMI and are represented by the Win32_Process class. When Notepad starts running, a corresponding instance of the Win32_Process class becomes available through WMI. If you have registered your interest in this event (by issuing the appropriate event notification query), the availability of this instance results in the creation of an instance of the __InstanceCreationEvent class.