Search code examples
c#windowsrecording

How to start and stop the Windows audio/video recorder from C#?


I would like to start and stop the audio and video screen recording function in Windows programmatically in C#/.NET 6 - preferably in a console program.

Manually, this can be done by holding down the Windows/Menu key and the Alt-key while pressing R on the keyboard.

I have tried to use the user32.dll and InterOp to send Alt (MENU) + LWIN + R key without success. Since I have a GeForce ShadowPlay-enabled video card, I have also tried to capture video sending Alt + F9, but this also fails.

I do not want to encode the audio and video in the program, just start and stop the Windows audio/video recording. Is there an API for this in Windows or any other way to start/stop recording?

using System.Runtime.InteropServices;

namespace StartStopWindowsBuiltInAudioVideoRecorder
{
    internal class NativeWin32
    {
        public const ushort KEYEVENTF_KEYUP = 0x0002;

        public enum VK : ushort
        {
            MENU = 0x12,
            F9 = 0x78,
            R = 0x52,
            LWIN = 0x5B,
        }

        public struct KEYBDINPUT
        {
            public ushort wVk;
            public ushort wScan;
            public uint dwFlags;
            public long time;
            public uint dwExtraInfo;
        };

        [StructLayout(LayoutKind.Explicit, Size = 28)]
        public struct INPUT
        {
            [FieldOffset(0)] public uint type;
            [FieldOffset(4)] public KEYBDINPUT ki;
        };

        [DllImport("user32.dll")]
        public static extern uint SendInput(uint nInputs, ref INPUT pInputs, int cbSize);

        // Method will hold down Windows and Left Alt while pressing R
        internal static void StartOrStopVideoRecorder()
        {
            NativeWin32.INPUT structInput;
            structInput = new NativeWin32.INPUT();
            structInput.type = (uint)1;
            structInput.ki.wScan = 0;
            structInput.ki.time = 0;
            structInput.ki.dwFlags = 0;
            structInput.ki.dwExtraInfo = 0;

            //Press Alt key
            structInput.ki.wVk = (ushort)NativeWin32.VK.MENU;
            NativeWin32.SendInput((uint)1, ref structInput, Marshal.SizeOf(structInput));
            Thread.Sleep(100);

            //Press LeftWin key
            structInput.ki.wVk = (ushort)NativeWin32.VK.LWIN;
            NativeWin32.SendInput((uint)1, ref structInput, Marshal.SizeOf(structInput));
            Thread.Sleep(100);

            //Press the R key
            structInput.ki.wVk = (ushort)NativeWin32.VK.R;//vk;
            NativeWin32.SendInput((uint)1, ref structInput, Marshal.SizeOf(structInput));
            Thread.Sleep(500);

            //Release the R key
            structInput.ki.dwFlags = NativeWin32.KEYEVENTF_KEYUP;
            structInput.ki.wVk = (ushort)NativeWin32.VK.R;//vk;
            NativeWin32.SendInput((uint)1, ref structInput, Marshal.SizeOf(structInput));
            Thread.Sleep(100);

            //Release LeftWin key
            structInput.ki.wVk = (ushort)NativeWin32.VK.LWIN;
            NativeWin32.SendInput((uint)1, ref structInput, Marshal.SizeOf(structInput));
            Thread.Sleep(100);

            //Release the ALT key
            structInput.ki.wVk = (ushort)NativeWin32.VK.MENU;
            NativeWin32.SendInput((uint)1, ref structInput, Marshal.SizeOf(structInput));
        }
    }
}

I expected the code to start the audio/video recording, but nothing happens. Other software reacts to these keys if configured to listen for them, so the NativeWin32 API still works as it should.


Solution

  • You were missing some stuff, first of all

    SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
    

    Sendinput expects an input array with: MouseInput,KeyboardInput,HardwareInput

    In the mouse input, dwExtraInfo is a IntPtr public readonly IntPtr dwExtraInfo;

    You were using SendInput with ref which should also not be used as far as I know. SendInput((uint)1, structInput, Marshal.SizeOf(typeof(INPUT)));

    Also the Thread.Sleeps are useless, but that’s irrelevant.

    public const ushort KEYEVENTF_KEYUP = 0x0002;
    
        public enum VK : ushort
        {
            MENU = 0x12,
            F9 = 0x78,
            R = 0x52,
            LWIN = 0x5B,
        }
    
        public struct KEYBDINPUT
        {
            public ushort wVk;
            public ushort wScan;
            public uint dwFlags;
            public long time;
            public uint dwExtraInfo;
        };
    
        public struct INPUT
        {
            public int type;
            public InputUnion u;
        }
    
        [StructLayout(LayoutKind.Explicit)]
        public struct InputUnion
        {
            [FieldOffset(0)] public readonly MouseInput mi;
            [FieldOffset(0)] public KeyboardInput ki;
            [FieldOffset(0)] public readonly HardwareInput hi;
        }
        [StructLayout(LayoutKind.Sequential)]
        public struct MouseInput
        {
            public readonly int dx;
            public readonly int dy;
            public readonly uint mouseData;
            public readonly uint dwFlags;
            public readonly uint time;
            public readonly IntPtr dwExtraInfo;
        }
    
        [StructLayout(LayoutKind.Sequential)]
        public struct KeyboardInput
        {
            public ushort wVk;
            public ushort wScan;
            public uint dwFlags;
            public readonly uint time;
            public IntPtr dwExtraInfo;
        }
    
        [StructLayout(LayoutKind.Sequential)]
        public struct HardwareInput
        {
            public readonly uint uMsg;
            public readonly ushort wParamL;
            public readonly ushort wParamH;
        }
    
        [DllImport("user32.dll", SetLastError = true)]
        private static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
        [DllImport("user32.dll")]
        private static extern IntPtr GetMessageExtraInfo();
    
        // Method will hold down Windows and Left Alt while pressing R
        internal static void StartOrStopVideoRecorder()
        {
            INPUT[] structInput =
            {
            new INPUT
            {
                type =  1,
                u = new InputUnion
                {
                    ki = new KeyboardInput
                    {
                        wVk = 0,
                        dwFlags = 0x0,
                        dwExtraInfo = GetMessageExtraInfo()
                    }
                }
            } };
    
    
            //Press Alt key
            structInput[0].u.ki.wVk = (ushort)VK.MENU;
            SendInput((uint)1, structInput, Marshal.SizeOf(typeof(INPUT)));
    
            //Press LeftWin key
            structInput[0].u.ki.wVk = (ushort)VK.LWIN;
            SendInput((uint)1, structInput, Marshal.SizeOf(typeof(INPUT)));
    
            //Press the R key
            structInput[0].u.ki.wVk = (ushort)VK.R;//vk;
            SendInput((uint)1, structInput, Marshal.SizeOf(typeof(INPUT)));
            //Thread.Sleep(500);
    
            //Release the R key
            structInput[0].u.ki.dwFlags = KEYEVENTF_KEYUP;
            structInput[0].u.ki.wVk = (ushort)VK.R;//vk;
            SendInput((uint)1, structInput, Marshal.SizeOf(typeof(INPUT)));
    
            //Release LeftWin key
            structInput[0].u.ki.wVk = (ushort)VK.LWIN;
            SendInput((uint)1, structInput, Marshal.SizeOf(typeof(INPUT)));
    
            //Release the ALT key
            structInput[0].u.ki.wVk = (ushort)VK.MENU;
            SendInput((uint)1, structInput, Marshal.SizeOf(typeof(INPUT)));
        }