Saturday 17 April 2021

WaitForInputIdle.exe Starts a graphical program and returns when when any of its windows is waiting for user input.

This uses the inbuilt compilers in Windows 10 - there are three VB.NET compilers and three C# compilers - just copy each text file into the same folder and double click the batch file to make the program.
Window Manipulation Posts

REM Two files follow
REM WaitForInputIdle.bat
REM This file compiles WaitForInputIdle.vb to WaitForInputIdle.exe using the system VB.NET compiler.
REM Starts a program and returns when the program has finished initialising and waiting for the user
C:\Windows\Microsoft.NET\Framework\v4.0.30319\vbc "%~dp0\WaitForInputIdle.vb" /out:"%~dp0\WaitForInputIdle.exe" /target:exe
REM To use
REM       WaitForInputIdle <Timeout> <"Command to run"> <Other Parameters>
REM            -1 is no timeout. Program name must be enclosed in quotes.
REM       WaitForInputIdle 10 "notepad" c:\windows\win.ini
pause


'CreateRemoteProcess.vbs
imports System.Runtime.InteropServices 


Public Module MyApplication  
		
	Public Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Integer, ByVal bInheritHandle As Boolean, ByVal processId As UInt32) As IntPtr
	Public Declare Function WaitForInputIdle Lib "user32" (ByVal hProcess As IntPtr, ByVal dwMilliseconds As Integer) As Integer 

	Public Const AllAccess = &H1F0FFF
	Public Const Terminate = &H1
	Public Const CreateThread = &H2
	Public Const VirtualMemoryOperation = &H8
	Public Const VirtualMemoryRead = &H10
	Public Const VirtualMemoryWrite = &H20
	Public Const DuplicateHandle = &H40
	Public Const CreateProcess = &H80
	Public Const SetQuota = &H100
	Public Const SetInformation = &H200
	Public Const QueryInformation = &H400
	Public Const QueryLimitedInformation = &H1000
	Public Const Synchronize = &H100000

	Public Const WAIT_TIMEOUT = 258 
	Public Const WAIT_Failed = -1 

	Public Sub Main ()
		Dim Proc As Object
		Dim hProcess As IntPtr
		Dim Ret As IntPtr
		Dim CmdLine As String
		Dim A as String()
		Dim B as String()

		CmdLine = Command()
		If CmdLine = "" then 
			Console.writeline("WaitForInputIdle <Timeout> <""Program name""> [<arguments>] -1 is indefinite timeout, program name must be in quotes")
			exit sub
		End If
		A = Split(CmdLine, Chr(32), 2, 1)
		B = Split(A(1), """", 3, 1)
		On Error Resume Next
		console.writeline("WaitForInputIdle")
		console.writeline("Waiting for " & B(1) & " started with args " & Trim(B(2)) & " to be ready")
		Proc = System.Diagnostics.Process.Start(B(1), Trim(B(2)))
		If err.number <> 0 then
			Console.writeline("Program could not be started - Error is " & err.description)
			Console.writeline("WaitForInputIdle <Timeout> <""Program name""> [<arguments>] -1 is indefinite timeout, program name must be in quotes")
			Exit Sub
		End If
			
		hProcess = OpenProcess(QueryInformation, False, Proc.ID)
		Ret = WaitForInputIdle(hProcess, CInt(A(0)) * 1000)
		If Ret = 0 then
			Console.Writeline("Program is ready for user input")
		ElseIf Ret = 258
			Console.Writeline("Program timed out")
		Else
			Console.Writeline("Error " & err.lastdllerror)
		End If


		Environment.ExitCode = Ret
	End Sub 
End Module 

Monday 12 April 2021

HideTaskbarBtn.exe Hides or shows a window on the taskbar.

This uses the inbuilt compilers in Windows 10 - there are three VB.NET compilers and three C# compilers - just copy each text file into the same folder and double click the batch file to make the program.
Window Manipulation Posts

Windows that are normal, a titlebar with a system menu, appear on the taskbar. Windows that want to appear on the taskbar but don't meet the requirements can set an extended window's style of an AppWindow. To remove a window from the taskbar requires the window to be hidden, the AppWindow extended style forcing it onto the taskbar to be removed and the extended style of a tool palette window applied. Then show the window. The new window won't have a titlebar icon.

This does not work with UWP apps, only Win32 applications and consoles.

If you run this on a minimised window there is no way of activating the window. See Assigns a hotkey to a window if you need to. If you run this on a non-minimised program then minimise the program a Windows 3.11 minimised desktop icon appears.


@Echo Off
Echo HideTaskbarBtn.bat
Echo This file compiles HideTaskbarBtn.vb to HideTaskbarBtn.exe
Echo HideTaskbarBtn.exe hides or shows a window'a button on the taskbar
Echo To use 
Echo     HideTaskbarBtn Hide ^<Window Title^>
Echo     HideTaskbarBtn Show ^<Window Title^>
Echo E.G.
Echo     HideTaskbarBtn Hide Untitled - Notepad
Echo     HideTaskbarBtn Show Untitled - Notepad
Echo -----------------------------------------------------
"C:\Windows\Microsoft.NET\Framework\v4.0.30319\vbc.exe" /target:winexe /out:"%~dp0\HideTaskbarBtn.exe" "%~dp0\HideTaskbarBtn.vb" 
pause

'HideTaskbarBtn.vb
Imports System
Imports System.IO
Imports System.Runtime.InteropServices
Imports Microsoft.Win32

Public Module TopMost
	Public Declare UNICODE Function FindWindowW Lib "user32" (ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
	Public Declare Function SetWindowPos Lib "user32" (ByVal hwnd As IntPtr, ByVal hWndInsertAfter As Integer, ByVal x As Integer, ByVal y As Integer, ByVal cx As Integer, ByVal cy As Integer, ByVal wFlags As Integer) As Integer
	Public Declare Function SetWindowLongPtrW Lib "user32" (ByVal hwnd As IntPtr, ByVal Index As Integer, ByVal NewValue As Integer) As Integer
	Public Declare Function GetWindowLongPtrW Lib "user32" (ByVal hwnd As IntPtr, ByVal Index As Integer) As Integer
	Public Declare Function GetParent Lib "user32.dll" (ByVal hwnd As Intptr) As IntPtr

	Public Const WS_EX_APPWINDOW = &h40000
	Public Const WS_EX_TOOLWINDOW = &h80
	Public Const WS_MINIMIZEBOX = &h20000
	Public Const GWL_EXSTYLE = -20
	Public Const GWL_STYLE = -16

	Public Const HWND_TOPMOST = -1
	Public Const HWND_NOTOPMOST = -2
	Public Const SWP_NOMOVE = &H2
	Public Const SWP_NOSIZE = &H1
	Public Const SWP_SHOWWINDOW = &H40
	Public Const SWP_HIDEWINDOW = &H80
	Public Const SWP_NOOWNERZORDER = &H200      '  Don't do owner Z ordering
	Public Const SWP_NOREDRAW = &H8
	Public Const SWP_NOREPOSITION = &H200
	Public Const SWP_NOZORDER = &H4

	Sub Main()
		On Error Resume Next
		Dim hWindows as IntPtr
		Dim CmdLine as String
		Dim Ret as Integer
		Dim ExStyle as Integer
		Dim Style as Integer
		CmdLine = Mid(Command(),6)
		hwindows = FindWindowW(vbNullString, CmdLine)
		If hwindows = 0 then
			Msgbox(Cmdline & " cannot be found.")
		Else
			If LCase(Left(Command(), 4)) = LCase("Hide") then
				Ret = GetWindowLongPtrW(hWindows, GWL_EXSTYLE)
				ExStyle = Ret
				'Test AppWindow is set and if so remove it
				If (ExStyle And WS_EX_APPWINDOW) = WS_EX_APPWINDOW then ExStyle = ExStyle - WS_EX_APPWINDOW
				If (ExStyle And WS_EX_TOOLWINDOW) <> WS_EX_TOOLWINDOW then ExStyle = ExStyle + WS_EX_TOOLWINDOW
				Ret = SetWindowPos(hwindows, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE + SWP_NOSIZE + SWP_NOZORDER + SWP_HIDEWINDOW)
				Ret = GetWindowLongPtrW(hWindows, GWL_EXSTYLE)
				'SetWindowLongPtr does not clear GetLastError if sucessful.
				err.clear
				Ret = SetWindowLongPtrW(hWindows, GWL_EXSTYLE, ExStyle)
				If (Ret = 0 And err.LastDLLError <> 0) Then MsgBox("SetWindowLongPtrW is " & Err.LastDllError)
				err.clear
'				Ret = SetWindowLongPtrW(hWindows, GWL_STYLE, Style)
'				If (Ret = 0 And err.LastDLLError <> 0) Then MsgBox("SetWindowLongPtrW is " & Err.LastDllError)
				Ret = SetWindowPos(hwindows, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE + SWP_NOSIZE + SWP_NOZORDER + SWP_SHOWWINDOW)
				If Ret = 0 Then MsgBox("Set Pos Error is " & Err.LastDllError)
			ElseIf LCase(Left(Command(), 4)) = LCase("Show") then
				Ret = GetWindowLongPtrW(hWindows, GWL_EXSTYLE)
				'Test AppWindow is set and if so remove it
				ExStyle = Ret
				If (ExStyle And WS_EX_APPWINDOW) <> WS_EX_APPWINDOW then ExStyle = ExStyle + WS_EX_APPWINDOW
				If (ExStyle And WS_EX_TOOLWINDOW) = WS_EX_TOOLWINDOW then ExStyle = ExStyle - WS_EX_TOOLWINDOW
				Ret = SetWindowPos(hwindows, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE + SWP_NOSIZE + SWP_NOZORDER + SWP_HIDEWINDOW)
				If Ret = 0 Then MsgBox("Set Pos Error is " & Err.LastDllError)
				err.clear
				Ret = SetWindowLongPtrW(hWindows, GWL_EXSTYLE, ExStyle)
				If (Ret = 0 And err.LastDLLError <> 0) Then MsgBox("SetWindowLongPtrW is " & Err.LastDllError)
				Ret = SetWindowPos(hwindows, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE + SWP_NOSIZE + SWP_NOZORDER + SWP_SHOWWINDOW)
				If Ret = 0 Then MsgBox("Set Pos Error is " & Err.LastDllError)
			Else
				Msgbox("Command line not recognised")
			End If
		End If
	End Sub
End Module