1

I have created a python script to regularly scrape a website and store the results in a .json file while at work. This script is set to run without the command line on an infinite loop, so I can have it start in the morning and then just run unimpeded throughout the day.

My issue is that at night I'm going to want to kill it so that I can go home, but I don't want to cause any issues with killing it mid-connection or mid-write. I was wondering what was the correct way to handle creating, and subsequently destroying, such a script on Windows 10 to handle exit behaviours.

  • Generally you'd create a service that can be started and stopped using services.msc or, from the command line, with sc.exe or net.exe. PyWin32 has everything you need to implement a service. It comes with basic example code, and there are lots of examples on the web. – Eryk Sun Apr 25 '17 at 08:12

1 Answers1

1

Here's an example that uses a hidden window to listen for a WM_CLOSE message sent by taskkill.exe.

demo.py

import win32con
import win32gui

def shutdown_monitor():
    def wndproc(hwnd, msg, wparam, lparam):
        if msg == win32con.WM_CLOSE:
            win32gui.DestroyWindow(hwnd)
            return 0
        elif msg == win32con.WM_DESTROY:
            win32gui.PostQuitMessage(0)
            return 0
        return win32gui.DefWindowProc(hwnd, msg, wparam, lparam)
    wc = win32gui.WNDCLASS()
    wc.lpszClassName = 'SpamMessages'
    wc.lpfnWndProc = wndproc
    win32gui.RegisterClass(wc)
    hwnd = win32gui.CreateWindow('SpamMessages', 'Python Spam App',
                0, 0, 0, 0, 0, 0, 0, 0, None)
    win32gui.PumpMessages()

if __name__ == '__main__':
    import sys
    import time
    import atexit
    import threading

    atexit.register(print, 'PYTHON SPAM APP: SHUTDOWN')

    shutdown_thread = threading.Thread(target=shutdown_monitor)
    shutdown_thread.start()
    shutdown_thread.join()
    time.sleep(1)
    sys.exit(0x2A)

demo.bat

@echo off
setlocal
set killquit=taskkill /fi "windowtitle eq Python Spam App"
set killkill=taskkill /f /fi "windowtitle eq Python Spam App"

echo Quit Test
start /b cmd /c "(timeout 3 >nul) & %killquit%" & demo.py
echo ExitCode=%=ExitCode%
echo.
echo Kill Test
start /b cmd /c "(timeout 3 >nul) & %killkill%" & demo.py
echo ExitCode=%=ExitCode%

output

Quit Test
SUCCESS: Sent termination signal to the process with PID 5612.
PYTHON SPAM APP: SHUTDOWN
ExitCode=0000002A

Kill Test
SUCCESS: The process with PID 5432 has been terminated.
ExitCode=00000001

The Windows 8/10 Task Manager doesn't consider this hidden window as belonging to an interactive application and doesn't list the process in its "Apps" list, so unlike taskkill.exe it forcefully terminates the process instead of posting WM_CLOSE. There may be some other way to force Task Manager to classify the process as an 'app'.

Eryk Sun
  • 33,190
  • 5
  • 92
  • 111