windows - 如何使用 nim 语言在 Windows 上处理 taskkilll 命令?

标签 windows winapi nim-lang

是否有办法处理在没有 /F 选项的进程上使用 taskkill 时 Windows 发送的 WM_CLOSE 事件? ffmpeg 并非旨在捕获此事件,我想编写一个 nim 程序来管理 ffmpeg 进程并告诉它在收到 WM_CLOSE 事件时正常关闭。我对 Windows API 和 nim 非常陌生,所以我不知道从哪里开始。

这是一个显示 ffmpeg 问题的 gif /image/d5v6l.jpg

如果我使用 /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

这不会捕获 taskkill 生成的 WM_CLOSE 事件。

如何让我的 nim 程序捕获 WM_CLOSE 事件?

最佳答案

问题

如果没有与进程关联的窗口句柄,则无法捕获 taskkill 在不使用 /F 的情况下发送的 WM_CLOSE 事件 标志。

Send WM_CLOSE message to a process with no window

taskkill 在不带 /F 标志的情况下使用时,会向窗口发送 WM_CLOSEWM_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 事件的窗口句柄。

/image/d5v6l.jpg

^ 我使用自定义 ffmpeg 包装器捕获 WM_CLOSE 事件并正常关闭 ffmpeg 来录制此剪辑。

解决方案

将窗口与 Windows 上的任何可执行文件关联的一种方法是使用启动命令:

启动“您的窗口标题”program.exe -option1 -option2

然后您可以使用taskkill发送WM_CLOSE事件,该事件可以被拦截,但ffmpeg实际上并没有捕获该事件,因为它并不是设计来捕获的首先是它。

要在 nim 中解决此问题,您可以结合使用 3 个附加模块来创建窗口句柄、启动/监视进程、拦截 WM_CLOSE 事件以及将“q”写入 ffmpeg 进程的 stdout

wNim

winim

nimpy

在其他版本的 Windows 中,taskkill 会发送 WM_QUIT 事件,因此您可能还需要处理该事件。

使用wNim创建窗口句柄; 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/用户名/AppData/Local/Microsoft/WindowsApps

这里是一个存储库,其中包含您遇到相同问题时所需的一切:

https://github.com/Wykleph/ffmpeg_wrapper

关于windows - 如何使用 nim 语言在 Windows 上处理 taskkilll 命令?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63649070/

相关文章:

c++ - Windows中的变量类型

while-loop - Nim : works when assigning the iterator, 中的迭代器生成函数在直接调用时卡住

c++ - 如何在 C++ 中获取 OpenGL 使用的总内存(以字节为单位)?

php - wkhtmltopdf 窗口错误

windows - 如何检查 Windows 防火墙中的规则?

winapi - 我可能会泄露哪些未命名的 GDI 资源

delphi - 当子窗口卡住时,父窗口卡住,尽管它来自另一个进程

java - 一个非常令人困惑的 "ParseException: Unparseable date"(SimpleDateFormat) 错误

naming-conventions - Nim 中避免变量阴影的惯用方法

function-pointers - 在 Nim 中使用前向声明的原型(prototype)分配函数指针