i have to use ffmpeg in our system, but our system doesnt support multitasking. I can start external programs with batchfile or exe. I use ffmpeg to record during testrun afterwords my system start my exceutable and then ffmpeg shall be terminated. I used process.kill(), but recognized, that the file seems not correct closed, because videofile ends with pause, instead of stop. I found the solution http://stanislavs.org/stopping-command-line-applications-programatically-with-ctrl-c-events-from-net/ . I used solution 4 in my code, but seems not to work.
My program is a toggle switch for ffmpeg start and stop, means 1. execute my program ffmpeg starts 2. execute my program ffmpeg shall stop
If I debug, then the method is executed, but ffmpeg is still open in Taskmanager. If I use p.kill(), then process will hard terminated.
Process p = Process.GetProcessById(Int32.Parse(appsettings.getAppSetting("ProcID")));
if (p.ProcessName == "ffmpeg")
{
//p.Kill(); //works
StopProgramByAttachingToItsConsoleAndIssuingCtrlCEvent(p); // doesnt work
Console.WriteLine("Recording stopped");
EDIT: Seems that I did not attached to console, but proc.Id is correct.
if (AttachConsole((uint)proc.Id))
is not true, but proc.ID seems correct.
Full Code:
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)
{
}
}
}
}