0

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)
            {

            }
        }

    }
}
Shazter
  • 305
  • 1
  • 4
  • 17
  • considering that ctrl + c is kinda universally used as copy, would it not make sense to use a different bind? – Takarii Jun 10 '16 at 12:33
  • What do you mean with different bind? FFmpeg terminte if you have started and then press on keyboard ctrl-c. I cannot change the behavoiur of ffmpeg. – Shazter Jun 10 '16 at 13:13
  • 2
    @Takarii Ctrl+C in a console process is a signal to terminate. – Lasse V. Karlsen Jun 10 '16 at 13:39
  • @Shazter Have you looked at this question? http://stackoverflow.com/questions/283128/how-do-i-send-ctrlc-to-a-process-in-c – Lasse V. Karlsen Jun 10 '16 at 13:41
  • I checked the link, but i dont understand what he means here: Things become more complex if you need to send Ctrl+C from .NET console application. Approach will not work because AttachConsole returns false in this case (main console app already has a console). It is possible to call FreeConsole before AttachConsole call but as result original .NET app console will be lost which is not acceptable in most cases. – Shazter Jun 10 '16 at 13:54
  • Ok, I understood I made now Freeconsole() before Attachconsole(pid). His solution, about freeconsole not acceptable and how to solve then i understood too. @Lasse Thank you for Link – Shazter Jun 10 '16 at 14:06

0 Answers0