我必须在我们的系统中使用ffmpeg,但我们的系统不支持多任务处理。我可以使用batchfile或exe启动外部程序。我使用ffmpeg记录testrun后,我的系统启动我的exceutable,然后ffmpeg将被终止。我使用了process.kill(),但是认识到该文件似乎没有正确关闭,因为videofile以暂停结束,而不是停止。我找到了解决方案http://stanislavs.org/stopping-command-line-applications-programatically-with-ctrl-c-events-from-net/。我在我的代码中使用了解决方案4,但似乎无法工作。
我的程序是ffmpeg启动和停止的切换开关,意思是 1.执行我的程序ffmpeg启动 2.执行我的程序ffmpeg将停止
如果我调试,则执行该方法,但在Taskmanager中仍然打开ffmpeg。如果我使用p.kill(),那么进程将很难终止。
Process p = Process.GetProcessById(Int32.Parse(appsettings.getAppSetting("ProcID")));
if (p.ProcessName == "ffmpeg")
{
//p.Kill(); //works
StopProgramByAttachingToItsConsoleAndIssuingCtrlCEvent(p); // doesnt work
Console.WriteLine("Recording stopped");
修改 似乎我没有附加到控制台,但proc.Id是正确的。
if (AttachConsole((uint)proc.Id))
不是,但proc.ID似乎是正确的。
完整代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Threading;
using BizArk.Core;
using BizArk.Core.CmdLine;
using System.Runtime.InteropServices;
using System.Collections;
namespace Recorder
{
class Program
{
#region pinvoke
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AttachConsole(uint dwProcessId);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern bool FreeConsole();
[DllImport("kernel32.dll")]
static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate HandlerRoutine, bool Add);
// Delegate type to be used as the Handler Routine for SCCH
delegate Boolean ConsoleCtrlDelegate(CtrlTypes CtrlType);
// Enumerated type for the control messages sent to the handler routine
enum CtrlTypes : uint
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT,
CTRL_CLOSE_EVENT,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT
}
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GenerateConsoleCtrlEvent(CtrlTypes dwCtrlEvent, uint dwProcessGroupId);
private enum ShowCommands
{
SW_HIDE = 0,
SW_SHOWNORMAL = 1,
SW_NORMAL = 1,
SW_SHOWMINIMIZED = 2,
SW_SHOWMAXIMIZED = 3,
SW_MAXIMIZE = 3,
SW_SHOWNOACTIVATE = 4,
SW_SHOW = 5,
SW_MINIMIZE = 6,
SW_SHOWMINNOACTIVE = 7,
SW_SHOWNA = 8,
SW_RESTORE = 9,
SW_SHOWDEFAULT = 10,
SW_FORCEMINIMIZE = 11,
SW_MAX = 11
}
[DllImport("shell32.dll")]
static extern IntPtr ShellExecute(IntPtr hwnd, string lpOperation, string lpFile, string lpParameters, string lpDirectory, ShowCommands nShowCmd);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct STARTUPINFO
{
public Int32 cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public Int32 dwX;
public Int32 dwY;
public Int32 dwXSize;
public Int32 dwYSize;
public Int32 dwXCountChars;
public Int32 dwYCountChars;
public Int32 dwFillAttribute;
public Int32 dwFlags;
public Int16 wShowWindow;
public Int16 cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int nLength;
public IntPtr lpSecurityDescriptor;
public int bInheritHandle;
}
[DllImport("kernel32.dll")]
static extern bool CreateProcess(string lpApplicationName,
string lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes,
ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles,
uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory,
[In] ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
private const int WM_VSCROLL = 277;
private const int SB_BOTTOM = 7;
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CreatePipe(out IntPtr hReadPipe, out IntPtr hWritePipe, ref SECURITY_ATTRIBUTES lpPipeAttributes, uint nSize);
[DllImport("user32.dll")]
private static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool ShowWindow(IntPtr hWnd, ShowCommands nCmdShow);
[DllImport("user32.dll", SetLastError = true)]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
public delegate bool EnumedWindow(IntPtr handleWindow, ArrayList handles);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumWindows(EnumedWindow lpEnumFunc, ArrayList lParam);
[DllImport("User32.dll", CharSet = CharSet.Auto, EntryPoint = "SendMessage")]
private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true)]
static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
private const int VK_CONTROL = 0x11;
private const int WM_KEYDOWN = 0x100;
private const int WM_CHAR = 0x102;
private const int WM_KEYUP = 0x101;
private const int VK_CANCEL = 0x03;
private const int VK_C = 0x0043;
#endregion pinvoke
static Process proc = null;
static ProcessStartInfo psi = null;
static AppSettings appsettings = new AppSettings();
static void Main(string[] args)
{
ConsoleApplication.RunProgram<Arguments>(RunMain);
}
static void RunMain(Arguments args)
{
bool state = bool.Parse(appsettings.getAppSetting("State"));
state = !state;
appsettings.setAppSetting("State", state.ToString());
if (state)
{
Run(args.OFileName);
appsettings.setAppSetting("ProcID", proc.Id.ToString());
appsettings.setAppSetting("ProcName", proc.ProcessName);
Console.WriteLine("Recording started");
}else{
Process p = Process.GetProcessById(Int32.Parse(appsettings.getAppSetting("ProcID")));
if (p.ProcessName == "ffmpeg")
{
//p.Kill(); //works
StopProgramByAttachingToItsConsoleAndIssuingCtrlCEvent(p); // doesnt work
Console.WriteLine("Recording stopped");
}
}
}
public static void StopProgramByAttachingToItsConsoleAndIssuingCtrlCEvent(Process proc)
{
//This does not require the console window to be visible.
if (AttachConsole((uint)proc.Id))
{
//Disable Ctrl-C handling for our program
SetConsoleCtrlHandler(null, true);
GenerateConsoleCtrlEvent(CtrlTypes.CTRL_C_EVENT, 0);
//Must wait here. If we don't and re-enable Ctrl-C handling below too fast, we might terminate ourselves.
proc.WaitForExit();
FreeConsole();
//Re-enable Ctrl-C handling or any subsequently started programs will inherit the disabled state.
SetConsoleCtrlHandler(null, false);
}
}
static void Run(string OutputFileName)
{
try
{
psi = new ProcessStartInfo(appsettings.getAppSetting("ffmpegpath"));
psi.Arguments = "-r 25 -i " + appsettings.getAppSetting("CameraURL") + " -y -c:v copy -vcodec libx264 -pix_fmt yuv420p -f h264 -b:v 128k -bufsize 128k -r 25 " + OutputFileName;
psi.WorkingDirectory = appsettings.getAppSetting("Workingpath");
psi.CreateNoWindow = false;
psi.RedirectStandardInput = false;
psi.RedirectStandardOutput = false;
psi.RedirectStandardError = false;
psi.WindowStyle = ProcessWindowStyle.Normal;
psi.UseShellExecute = false;
proc = Process.Start(psi);
}
catch(Exception)
{
}
}
}
}