How to switch to an external application

Just use os.remove(path) to delete the file

The .exe probably won't work in the switching script, so you will probably have to add a second variable. It would look something like this:

taskName = "DisplayClient.exe"
switchName = "DisplayClient"

The isAppRunning function would stay the same, but the second line of your scriptLines array would use switchName instead of taskName.

Putting it all together, the second half of the script would look like the revised code below:

Revised Code
taskName = "DisplayClient.exe"
switchName = "DisplayClient"
if isAppRunning(taskName):
	username = getpass.getuser()
	path = "C:/Users/" + username + "/.ignition/cache/tempFile.bat"
	if os.path.exists(path):
		os.remove(path)
	scriptLines = [
		"@echo WScript.CreateObject(\"WScript.Shell\").AppActivate(WScript.Arguments.Item(0))>%tmp%\switch.vbs",
		"%tmp%\switch.vbs \""+switchName+"\"",
		"exit"]
	with open(path, "w") as tf:
		for line in scriptLines:
			tf.write(line + "\n")
	system.util.execute([path])

This makes perfect sense:
image

1 Like

Smart Man! I did. I am using:

taskName = "DisplayClient.exe"
taskName2 = "DisplayClient"

I'll review your Revised Code shortly.

1 Like

Ok. I think I beat this one to death. I was unable to get the isAppRunning to work with my app. It does work with other apps like notepad, chrome etc just not with my app. Not sure, why but I gave up, at least using the VBScript approach. I used Justin's caching method and tweaked it do the same in PowerShell rather than VBScript. PowerShell seems to work for me on both Win10 & WinServer2019. It also works when using the Task name with or without the .EXE. It also works using the application description (the name that shows up when you ALT+TAB to that window) .

The only caveat using PowerShell is to set PS Script Authorization. Both LocalMachine & CurrentUser should work:
PowerShell.exe Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope LocalMachine

I ended up turning the code into 2 functions. Function #1 isAppRunning scans the TaskList and returns a True or False if the required application is running & exists in the task list. Function #2 AppActivate will perform the AppActivate or switch focus to the required application.

If I have more time, I would like to add code when the isAppRunning returns a False, to manually run the application.

All the credit goes to justinedwards.jle as I used his original source code above and guidance to complete this task - Thanks.

The GoTo Button runs the code as follows:

taskName = "DisplayClient.exe"
switchName = "Tea_Room" # < DisplayClient or DisplayClient.exe > also works
	
if shared.Functions.isAppRunning(taskName,0):

	shared.Functions.AppActivate(switchName)

The 2 functions are as follows:


import os
import getpass
from java.lang import Runtime
from java.io import InputStreamReader
from java.io import BufferedReader

def isAppRunning(taskName,printTaskList=0):
	# Rev.2022-12-11
	
	x = 0
	testLines = ""
	TaskExistsCount = 0
	
	processList = BufferedReader(InputStreamReader(Runtime.getRuntime().exec("tasklist.exe").getInputStream()))
	
	while testLines != None:

		if printTaskList == 1:
			print testLines
		
		if taskName in testLines:
			TaskExistsCount = TaskExistsCount + 1
			
			if printTaskList == 1:
				print \r\r\r\r
				print 'Task #: %d (Task Name: %s)' %(TaskExistsCount,testLines)
				print \r\r\r\r
			
		testLines = processList.readLine()
		
		x = x+1
	
	if printTaskList == 1:
		print \r\r\r\r
		print 'Task Count: ' + str(x)
	
	processList.close()
	
	if TaskExistsCount > 0:
		if printTaskList == 1:
			print 'Task Exists: Yes (Occurence: %d)' %(TaskExistsCount)
			
		return True
		
	if TaskExistsCount == 0:
		if printTaskList == 1:
			print 'Task Exists: No'
			
		return False
		
	processList.close()
def AppActivate(taskName):
	# Rev.2022-12-11
	
	# taskName can be:
	#	Window Name/Description
	#	Program Executable (without the EXE)
	#	Program Executable (with the EXE)
	#
	# If PS error < UnauthorizedAccess > is displayed (Probably a non-admin account)
	#	Run PowerShell as Admin and execute:
	#	PowerShell.exe Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope LocalMachine
	
	username = getpass.getuser()
	ingTmpPath = "C:/Users/" + username + "/.ignition/cache/tempFile.bat"
	winTmpPath = "C:/Users/" + username + "/AppData/Local/Temp/switch.ps1"
	
	if os.path.exists(ingTmpPath): os.remove(ingTmpPath)
	if os.path.exists(winTmpPath): os.remove(winTmpPath)
	
	if not os.path.exists(ingTmpPath):
		
		scriptLines = [
			"@echo $wshell = New-Object -ComObject wscript.shell>%tmp%\switch.ps1",
			"@echo #$wshell.Run('exe goes here')>>%tmp%\switch.ps1",
			"@echo $wshell.AppActivate('"+taskName+"')>>%tmp%\switch.ps1",
			"@echo #$wshell.Sleep('500')>>%tmp%\switch.ps1",
			"@echo #$wshell.SendKeys('sendKeys goes here')>>%tmp%\switch.ps1",
			"PowerShell.exe -command %tmp%\switch.ps1",
			"exit"]
			
		with open(ingTmpPath, "w") as tf:
			for line in scriptLines:
				tf.write(line + "\n")
	system.util.execute([ingTmpPath])
1 Like

@yiotis I have something similar to @justinedwards.jle solution but I'm using a Powershell command "Get-Process" to get a listing of all running processes and the window title's. I'm also looking for a particular process that's running and it spawns the same process name but has different window titles for the particular application. I use the exitValue as a quick way to know if it's found or not. I also set the directory to execute from sys32 just to make sure powershell is executing from the right location. It's just a slightly different take on the approach.

def checkPluginExe():
	from java.lang import ProcessBuilder
	from java.io import BufferedReader,InputStreamReader, File
	cmd = "powershell.exe"
	sep = "|"
	mod = "Where-Object {$_.MainWindowTitle}"
	mod2 = "format-table ProcessName, MainWindowTitle" 
	command = r"Get-Process"
	Process =  r"exe Name" 
	
	pb = ProcessBuilder([cmd,command,Process,sep,mod,sep,mod2])
	pb.redirectErrorStream(True)
	dirFile = File(r"C:\\WINDOWS\system32")
	pb.directory(dirFile)

	try:
		p = pb.start()
		bsr = BufferedReader(InputStreamReader(p.inputStream))
		#print bsr.lines()
		raw = []
		text = ''
		for line in bsr.lines().iterator():
			raw.append(line.strip())
		output = [x for x in raw if len(x)>0] 		
		exitValue = p.exitValue()
		if exitValue == 0:
			listing = []
			for line in output:
				temp = line.split()
				listing.append(temp[0])
				if len(temp)>2:	
					temp2 = ''.join([e for i, e in enumerate(temp) if i>0])
					listing.append(temp2)
				else:
					listing.append(temp[1])
			if 'Name of exe' in listing and 'Window Title' in listing:
				#do something exe and window found
			else:
				#do something exe found but window not found
		elif exitValue >0:
			# do something exe not found
	except:
		import sys
		from java.lang import Exception
		exceptionType, exception, stacktrace = sys.exc_info()
		if exceptionType == Exception:
			exception = exception.getCause()
		exceptionName = exception.__class__.__name__
		#do something with error
3 Likes

Excellent and well structured work; thanks for sharing this.

I tested it on an instance of notepad changing line 9 to read:

Process =  r"notepad"

and changing line 37 to read:

if 'notepad' in listing and 'Untitled-Notepad' in listing:

It perfectly identified whether or not the specific instance was opened. The big advantage in this usage case is that because this example is iterating through a list instead of a string, the name has to be exact. Therefore, this resolves the issue of differentiating DisplayClient from DisplayClientManager.

@michael.ruel Thanks for sharing. Just got around to testing your script and it works like a charm. In my case I changed line 9 to:

Process = r"DisplayClient"

and lines 37-42 to:

if 'DisplayClient' in listing and 'Tea_Room' in listing:
	#do something exe and window found
	shared.Functions.AppActivate('Tea_Room')
else:
	#do something exe found but window not found
	pass