如何使用 nim 语言在 windows 上处理 taskkilll 命令?
How can I handle a taskkilll command on windows using the nim language?
当 taskkill
用于没有 /F
选项的进程时,有没有办法处理 Windows 发送的 WM_CLOSE
事件? ffmpeg 并非设计用于捕获此事件,我想编写一个 nim
程序来管理 ffmpeg 进程并告诉它在收到 WM_CLOSE
事件时正常关闭。我对 Windows API 和 nim
很陌生,所以我不知道从哪里开始。
这是一张显示 ffmpeg 问题的 gif https://imgur.com/a/JxGjiiX
如果我使用 /F
选项强制终止 ffmpeg 进程,它将在 encoding/recording 期间以文件损坏结束。
这是我用来测试此功能的代码:
import os, times, strutils
let t = epochTime()
proc handler() {.noconv.} =
echo "Program has run for ", formatFloat(epochTime() - t, precision = 0), " seconds."
quit(0)
setControlCHook(handler)
while true:
sleep 500
这不会捕获 taskkill
生成的 WM_CLOSE
事件。
如何让我的 nim
程序捕捉到 WM_CLOSE
事件?
问题
如果没有与进程关联的 window 句柄,它无法捕获 taskkill
在没有 /F
的情况下发送的 WM_CLOSE
事件旗帜.
Send WM_CLOSE message to a process with no window
taskkill
,在没有 /F
标志的情况下使用时,会向与您使用的 pid 关联的 window 句柄发送 WM_CLOSE
或 WM_QUIT
消息调用 taskkill
时
请注意,我在 Windows 10,所以我必须赶上 WM_CLOSE
而不是 WM_QUIT
您可以通过打开命令提示符并运行ning ffmpeg,然后尝试taskkill /PID {pid}
或taskkill /IM ffmpeg.exe
来自行测试问题。 ffmpeg 将继续 encode/record 并且 taskkill
会告诉您它已成功终止进程。
ffmpeg.exe -rtbufsize 150M -f gdigrab -framerate 30 -offset_x 448 -offset_y 240 -video_size 1024x600 -draw_mouse 1 -show_region 1 -i desktop -r 30 -preset ultrafast -tune zerolatency -movflags +faststart output.mp4
这是一个尝试使用 taskkill
关闭 ffmpeg 进程的演示。 taskkill
的默认行为是未处理的,因为 ffmpeg 不会创建可以向其发送 WM_CLOSE
事件的 window 句柄。
^ 我使用我的自定义 ffmpeg 包装器捕获 WM_CLOSE
事件并正常关闭 ffmpeg 以录制此剪辑。
解决方案
将 window 与 Windows 上的任何可执行文件相关联的一种方法是使用启动命令:
start "Your window title" program.exe -option1 -option2
然后你可以使用taskkill
发送WM_CLOSE
事件,这个事件可以被拦截,但是ffmpeg实际上并没有捕捉到这个事件,因为它并没有被设计成在第一时间捕捉到它地点。
要在 nim
中解决这个问题,您可以使用 3 个附加模块的组合来创建一个 window 句柄,start/monitor 一个进程,并拦截 WM_CLOSE
事件并将“q”写入ffmpeg进程的stdout
.
在 windows 的其他版本中,taskkill
发送 WM_QUIT
事件,因此您可能也需要处理它。
用wNim
创建window句柄; winim
仅用于访问用于 wNim
事件处理程序的 WM_CLOSE
常量。
import os
import wnim
import winim
import nimpy
# Put python3.8.dll into following folder. It is required by nimpy
# C:/Users/username/AppData/Local/Microsoft/WindowsApps
let app = App()
let frame = Frame(title="ffmpeg", size=(400, 300)) # Size doesn't matter because we never show the frame.
使用 nimpy 加载 python3.8.dll
文件,这样您就可以使用 python 中的 subprocess
模块。 (比内置 nim
多处理库恕我直言更容易使用)
# This is used to check if ffmpeg has been launched
var running_process = false
# The process must be a PyObject, not a Process object from nim
var the_proc: PyObject
# Get reference to the subprocess module
let subprocess = pyImport("subprocess")
# Get reference to python builtin modules
let py = pyBuiltinsModule()
# Get a reference to the subprocess PIPE
let pipe = subprocess.PIPE
创建事件处理程序。这些将处理应用程序首次启动和 windows.
中的 WM_CLOSE
事件
# We catch the WM_MOVE event and start the process.
# We force the event to trigger when we center
# the frame and start the app.
frame.connect(WM_MOVE) do (event: wEvent):
# Make sure we only start 1 process.
if running_process == false:
running_process = true
# Create command and start the process.
var command_str: string = paramStr(1)
for i in 2..paramCount():
command_str = command_str & " " & paramStr(i)
# Use python subprocess module to execute command and set the stdin to the pipe.
the_proc = subprocess.Popen(command_str, stdin=pipe)
# When WM_CLOSE is triggered from taskkill, write the utf-8 encoded "q"
# to the ffmpeg process
frame.connect(WM_CLOSE) do (event: wEvent):
var command = "q"
# Make sure objects are all safe to use
# They should by python objects. Call the
# standard library to create the python objects.
var pycommand = py.str.encode(py.str(command), "utf-8")
# With a python process you call `communicate` on
# the process when you want it to wait to complete.
# Since ffmpeg just needs the "q" character we send
# the utf-8 encoded "q" with the `input` keyword
discard the_proc.communicate(input=pycommand)
sleep(1000)
# Get rid of process and quit.
discard the_proc.terminate()
quit(0)
然后需要做的就是将框架居中并启动应用程序。
frame.center()
app.mainLoop()
请记住,这使用了 python 标准库,因此您需要将 python dll 放入以下文件夹中,以便 nimpy
获得访问权限。
C:/Users/username/AppData/Local/Microsoft/WindowsApps
如果您碰巧 运行 遇到同样的问题,这里有一个存储库,其中包含您需要的一切:
当 taskkill
用于没有 /F
选项的进程时,有没有办法处理 Windows 发送的 WM_CLOSE
事件? ffmpeg 并非设计用于捕获此事件,我想编写一个 nim
程序来管理 ffmpeg 进程并告诉它在收到 WM_CLOSE
事件时正常关闭。我对 Windows API 和 nim
很陌生,所以我不知道从哪里开始。
这是一张显示 ffmpeg 问题的 gif https://imgur.com/a/JxGjiiX
如果我使用 /F
选项强制终止 ffmpeg 进程,它将在 encoding/recording 期间以文件损坏结束。
这是我用来测试此功能的代码:
import os, times, strutils
let t = epochTime()
proc handler() {.noconv.} =
echo "Program has run for ", formatFloat(epochTime() - t, precision = 0), " seconds."
quit(0)
setControlCHook(handler)
while true:
sleep 500
这不会捕获 taskkill
生成的 WM_CLOSE
事件。
如何让我的 nim
程序捕捉到 WM_CLOSE
事件?
问题
如果没有与进程关联的 window 句柄,它无法捕获 taskkill
在没有 /F
的情况下发送的 WM_CLOSE
事件旗帜.
Send WM_CLOSE message to a process with no window
taskkill
,在没有 /F
标志的情况下使用时,会向与您使用的 pid 关联的 window 句柄发送 WM_CLOSE
或 WM_QUIT
消息调用 taskkill
请注意,我在 Windows 10,所以我必须赶上 WM_CLOSE
而不是 WM_QUIT
您可以通过打开命令提示符并运行ning ffmpeg,然后尝试taskkill /PID {pid}
或taskkill /IM ffmpeg.exe
来自行测试问题。 ffmpeg 将继续 encode/record 并且 taskkill
会告诉您它已成功终止进程。
ffmpeg.exe -rtbufsize 150M -f gdigrab -framerate 30 -offset_x 448 -offset_y 240 -video_size 1024x600 -draw_mouse 1 -show_region 1 -i desktop -r 30 -preset ultrafast -tune zerolatency -movflags +faststart output.mp4
这是一个尝试使用 taskkill
关闭 ffmpeg 进程的演示。 taskkill
的默认行为是未处理的,因为 ffmpeg 不会创建可以向其发送 WM_CLOSE
事件的 window 句柄。
^ 我使用我的自定义 ffmpeg 包装器捕获 WM_CLOSE
事件并正常关闭 ffmpeg 以录制此剪辑。
解决方案
将 window 与 Windows 上的任何可执行文件相关联的一种方法是使用启动命令:
start "Your window title" program.exe -option1 -option2
然后你可以使用taskkill
发送WM_CLOSE
事件,这个事件可以被拦截,但是ffmpeg实际上并没有捕捉到这个事件,因为它并没有被设计成在第一时间捕捉到它地点。
要在 nim
中解决这个问题,您可以使用 3 个附加模块的组合来创建一个 window 句柄,start/monitor 一个进程,并拦截 WM_CLOSE
事件并将“q”写入ffmpeg进程的stdout
.
在 windows 的其他版本中,taskkill
发送 WM_QUIT
事件,因此您可能也需要处理它。
用wNim
创建window句柄; winim
仅用于访问用于 wNim
事件处理程序的 WM_CLOSE
常量。
import os
import wnim
import winim
import nimpy
# Put python3.8.dll into following folder. It is required by nimpy
# C:/Users/username/AppData/Local/Microsoft/WindowsApps
let app = App()
let frame = Frame(title="ffmpeg", size=(400, 300)) # Size doesn't matter because we never show the frame.
使用 nimpy 加载 python3.8.dll
文件,这样您就可以使用 python 中的 subprocess
模块。 (比内置 nim
多处理库恕我直言更容易使用)
# This is used to check if ffmpeg has been launched
var running_process = false
# The process must be a PyObject, not a Process object from nim
var the_proc: PyObject
# Get reference to the subprocess module
let subprocess = pyImport("subprocess")
# Get reference to python builtin modules
let py = pyBuiltinsModule()
# Get a reference to the subprocess PIPE
let pipe = subprocess.PIPE
创建事件处理程序。这些将处理应用程序首次启动和 windows.
中的WM_CLOSE
事件
# We catch the WM_MOVE event and start the process.
# We force the event to trigger when we center
# the frame and start the app.
frame.connect(WM_MOVE) do (event: wEvent):
# Make sure we only start 1 process.
if running_process == false:
running_process = true
# Create command and start the process.
var command_str: string = paramStr(1)
for i in 2..paramCount():
command_str = command_str & " " & paramStr(i)
# Use python subprocess module to execute command and set the stdin to the pipe.
the_proc = subprocess.Popen(command_str, stdin=pipe)
# When WM_CLOSE is triggered from taskkill, write the utf-8 encoded "q"
# to the ffmpeg process
frame.connect(WM_CLOSE) do (event: wEvent):
var command = "q"
# Make sure objects are all safe to use
# They should by python objects. Call the
# standard library to create the python objects.
var pycommand = py.str.encode(py.str(command), "utf-8")
# With a python process you call `communicate` on
# the process when you want it to wait to complete.
# Since ffmpeg just needs the "q" character we send
# the utf-8 encoded "q" with the `input` keyword
discard the_proc.communicate(input=pycommand)
sleep(1000)
# Get rid of process and quit.
discard the_proc.terminate()
quit(0)
然后需要做的就是将框架居中并启动应用程序。
frame.center()
app.mainLoop()
请记住,这使用了 python 标准库,因此您需要将 python dll 放入以下文件夹中,以便 nimpy
获得访问权限。
C:/Users/username/AppData/Local/Microsoft/WindowsApps
如果您碰巧 运行 遇到同样的问题,这里有一个存储库,其中包含您需要的一切: