4

I am working from a windows platform. In my python script, I can make a call to an external program in the following way:

os.system("C:\mainfolder\menu.exe C:\others\file1.inp C:\others\file2.inp")

os.popen("C:\mainfolder\menu.exe C:\others\file1.inp C:\others\file2.inp")

subprocess.call(["C:\mainfolder\menu.exe","C:\others\file1.inp" "C:\others\file2.inp"])

where:

menu.exe: is my external program.

file1 and file2: are input files to my external program.

All the above works fine. Now that my external program has finished successfully, I need to totally close it along with all the windows that are left opened by it. I have gone through lots of other posts, python documentation, etc and found commands as for example:

os.system("taskkill /im C:\mainfolder\menu.exe")

os.kill(proc.pid,9)

child.kill() 

But they did not work. I spent a lot of time trying to find something that worked for me, until I realised that no matter which commands I type after, they will not be read as the computer does not know that my external program has finished. That is the reason why I can easily terminate the program from the command line anytime just by typing taskkill /im menu.exe, but not from python.

Does anybody know how to sort this out?, should I include something else when I make the call to my external program?

m.wasowski
  • 6,329
  • 1
  • 23
  • 30
Sarah
  • 325
  • 6
  • 17
  • If the program you call opens other programs, you may not be able to do anything about that. I'm not sure though. – Cody Piersall Nov 08 '14 at 17:39
  • Seriously?, I do not know too much about programming but I always thought that in programming everything is possible. Hope I find an answer for this:-( – Sarah Nov 08 '14 at 17:42
  • If you can modify the opened programs to send a signal to your process right before/as they close (such as via DBus) you could use IPC to figure this out. – BlackVegetable Nov 08 '14 at 17:43
  • 1
    It is not true that you cannot do anything about it, it's just that the answer may be deeply hidden for windows. In linux kill with -9 switch will terminate said process and its children/parents, and I am not sure which switch of taskkill does the equivalent of that. – Tymoteusz Paul Nov 08 '14 at 17:44
  • Actually, did you try adding /F to your taskkill? – Tymoteusz Paul Nov 08 '14 at 17:45
  • @Puciek, If you mean typing: os.system(taskkill /f /im C:\mainfolder\menu.exe), yes, I tried it. But anything that I type after the call to my program will work, as python will not go further than that. – Sarah Nov 08 '14 at 17:51
  • your problem is, that the program `menu.exe` does not terminate, so how should python know about something not happening? How do you know, when `menu.exe` finished? – Daniel Nov 08 '14 at 18:09
  • @Daniel: Yes, I also think that is my problem. Is there anything that I can do to terminate that program automatically?, maybe changing the code from menu.exe is the only solution?, there is nothing that can be done from python? Calling the program in a different way with other additional commands?. I do not think I have the source code of menu.exe.. – Sarah Nov 08 '14 at 18:14
  • @Sarah: without knowing what `menu.exe` does, any advice is difficult. You could kill the program after some period of time or check for some file written or some window opened, or something else. – Daniel Nov 08 '14 at 18:18
  • @Daniel: When menu.exe does all it processing, it leaves an active window that I need to close manually, once I close it, then python recognises that menu.exe has finished. Maybe killing the program after some time could work? Is there anything better that could be done? – Sarah Nov 08 '14 at 18:25
  • Perhaps my problem is related with something about multithreading?. If it is not, I would really appreciate if somebody let me know it as I am really struggling to understand the python documentation of this topic.. – Sarah Nov 08 '14 at 18:34
  • Most of the time, I take the opposite approach. Instead of the parent killing the child, I make the child kill itself if the pid of his parent (ppid) becomes "1". – DevLounge Nov 08 '14 at 18:40
  • @Sarah: has nothing to do with multithreading. As I said, `menu.exe` doesn't terminate. The best way is, to wait for the opening of the message window and kill the program afterwards. – Daniel Nov 08 '14 at 18:41
  • @Apero: Sorry, I am not sure how this is possible, would it be possible for you to give me a short example?. – Sarah Nov 08 '14 at 18:47
  • @Daniel: Do you mean killing the program after some period of time? – Sarah Nov 08 '14 at 18:48
  • For controlling it manually you could check it in the taskmanager. It helps to check whether the os.kill() worked or not. – picibucor Jul 18 '17 at 07:08

3 Answers3

2

Here's some example code, how to detect if a program opens a window. All you need to know is the title of the message box, that menu.exe opens, when it is finished:

import subprocess
import win32gui
import time


def enumHandler(hwnd, lParam):
    if win32gui.IsWindowVisible(hwnd):
        if 'Menu.exe Finished' in win32gui.GetWindowText(hwnd):
            proc.kill()

proc = subprocess.Popen(["C:\mainfolder\menu.exe","C:\others\file1.inp" "C:\others\file2.inp"])
while proc.poll() is None:
    win32gui.EnumWindows(enumHandler, None)
    time.sleep(1)
Daniel
  • 42,087
  • 4
  • 55
  • 81
  • Does this code terminate my program when it detects that there is an active window? – Sarah Nov 08 '14 at 19:04
  • Terminates the program, if there is a window with a specific title. – Daniel Nov 08 '14 at 19:06
  • This is a really useful code, I did not know about it. However in my case that window opens at the very beginning of the process. While my program is running, it will say: Processing, and when it finishes processing my data, it will say: Finished. The title of the window remains the same. The title of the window is the name of my program. There might be a way to extract the word finished from the window instead of the title?. – Sarah Nov 08 '14 at 19:13
  • @Sarah: so that's more complicated. You have to find the window, look through all it's child-elements for some label with the text `finished`. Here's a stackoverflow answer: http://stackoverflow.com/questions/14500026/python-how-to-get-the-text-label-from-another-program-window – Daniel Nov 08 '14 at 19:18
  • Thanks Daniel, I am still reading the link that you have provided. Regarding to your code above, does it mean that if I call my program using: proc = subprocess.Popen(["C:\mainfolder\menu.exe","C:\others\file1.inp" "C:\others\file2.inp"]), will it read the following code? – Sarah Nov 08 '14 at 19:43
0

If you want to have a process end immediately, i.e., wait for it to end, this is a blocking call, and os.system() normally waits, discussed here as well as .communicate[0] using subprocess there.

If you want to end a process later in your program, an asynchronous, non-blocking process, perhaps get its pid and depending on whether shell=True or not that will either be the pid of the spawned shell or of the child process.

That pid can be used to end it either immediately by using psutil or os, or wait until it ends using little cpu time, though then other tasks can be done while waiting, or threads could be used.

Community
  • 1
  • 1
Stan S.
  • 237
  • 7
  • 22
0

It might be a bit late to post my findings to this question as I asked it some months back but it may still be helpful for other readers.

With the help of the people who tried answering my question, especially Daniel's answer, I found out a solution for my problem. Not sure if it is the best one but I got what I was needing.

Basically instead of looking for the word "finished" on my pop up window, I looked for the results that my external program generates. If these results have been generated, it means that the program has finished so I then kill the process:

 proc=subprocess.Popen(["C:\mainfolder\menu.exe","C:\others\file1.inp" "C:\others\file2.inp"])
 while proc.poll() is None:
     if os.path.exists("D:\\Results_folder\\Solution.txt"):
           time.sleep(10)
           os.system('taskkill /im menu.exe')
Sarah
  • 325
  • 6
  • 17
  • You can use `Popen.pid` to get the process ID of the subprocess you have spawned. Then you can call `kill -0 PID` (if you're using Windows check how `kill` behaves there!) in a loop until it's return value becomes 0 or `None`. – rbaleksandar Jan 26 '16 at 16:35