当在没有WM_CLOSE
选项的进程上使用taskkill
时,是否有一种方法可以处理Windows发送的/F
事件? ffmpeg并非旨在捕获此事件,因此我想编写一个nim
程序,该程序可以管理ffmpeg进程,并告诉它在收到WM_CLOSE
事件时正常关闭。我是Windows API和nim
的新手,所以我不知道从哪里开始。
这是显示ffmpeg https://imgur.com/a/JxGjiiX问题的gif文件
如果我使用/F
选项强行杀死ffmpeg进程,则它将在编码/录制过程中以文件损坏结尾。
这是我用来测试该功能的代码:
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
这不会捕获WM_CLOSE
生成的taskkill
事件。如何使
nim
程序捕获WM_CLOSE
事件? 最佳答案
问题
如果没有与进程关联的窗口句柄,则在没有WM_CLOSE
标志的情况下使用它时,将无法捕获taskkill
发送的/F
事件。
Send WM_CLOSE message to a process with no windowtaskkill
在不带/F
标志的情况下使用时,会将WM_CLOSE
或WM_QUIT
消息发送到与您在调用taskkill
时使用的pid相关联的窗口句柄
Difference between taskkill and taskkill /f
请注意,我在Windows 10上,因此我必须捕获WM_CLOSE
而不是WM_QUIT
您可以自己打开一个命令提示符并运行ffmpeg,然后尝试taskkill /PID {pid}
或taskkill /IM ffmpeg.exe
来测试问题。 ffmpeg将继续编码/记录,并且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
事件的窗口句柄。https://imgur.com/a/JxGjiiX
^我使用自定义的ffmpeg包装器捕获了
WM_CLOSE
事件,并优雅地关闭了ffmpeg以记录此剪辑。解决方案
将窗口与Windows上的任何可执行文件相关联的一种方法是使用start命令:
start "Your window title" program.exe -option1 -option2
然后,您可以使用taskkill
发送WM_CLOSE
事件,该事件可以被拦截,但是ffmpeg实际上并未捕获该事件,因为它并非最初旨在捕获该事件的。要在
nim
中解决此问题,您可以结合使用3个其他模块来创建窗口句柄,启动/监视进程并拦截WM_CLOSE
事件,并在ffmpeg进程的stdout
中写入“q”。wNim
winim
nimpy
在其他版本的Windows中,
taskkill
发送WM_QUIT
事件,因此您可能也需要处理该事件。用
wNim
创建窗口句柄; winim
仅用于访问用于WM_CLOSE
事件处理程序的wNim
常量。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
多处理库imho更易于使用)# 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
这是一个存储库,其中包含您碰巧遇到同一问题所需的一切:https://github.com/Wykleph/ffmpeg_wrapper
关于windows - 如何使用nim语言在Windows上处理taskkilll命令?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/63649070/