Application.Run () уничтожает другие функции - PullRequest
0 голосов
/ 17 апреля 2020

Я использую код из этого ответа для записи нескольких секунд записи всего экрана, и все работает отлично - я получаю GIF с записью экрана, сделанной из PNG. Но когда я добавляю Application.Run(); в Main() метод, чтобы и моя ловушка KeyHook работала как положено, запись экрана перестала работать ... Есть идеи, почему это происходит?

Application.Run(); - это одно из пространства имен System. Windows .Forms.

Код из класса методов Main:

    public static void Main(string[] args)
    {
        ScreenRecorder.Start();

        // need for LowLevelKeyboardListener
        Application.Run();

        int n = 5; //seconds of capturing
        //DispatcherTimer dispacherTimer = new DispatcherTimer();

        Timer timer = new Timer(n * 1000);
        timer.Interval = n * 1000;
        timer.Elapsed += _timer_Tick;
        timer.AutoReset = false;
        timer.Enabled = true;
        //timer.Start();

        _listener = new LowLevelKeyboardListener();
        _listener.OnKeyPressed += _listener_OnKeyPressed;
        _listener.HookKeyboard();

        Console.ReadKey();
        _listener.UnHookKeyboard();
    }

    public static void _listener_OnKeyPressed(object sender, KeyPressedArgs e)
    {
        Console.WriteLine(e.KeyPressed.ToString());
        Console.Beep();
    }  

    public static void _timer_Tick(object sender, EventArgs e)
    {
        ScreenRecorder.Stop();
        ScreenRecorder.Save(Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\video.gif");
        ScreenRecorder.ClearRecording();
        Console.WriteLine("recording saved");
    }

Код из ScreenRecorder:

public class ScreenRecorder
{

    private static string tempDir = Path.GetTempPath() + "/snapshot/";
    private static System.Threading.Thread snap = new System.Threading.Thread(Snapshot);

    private static System.Drawing.Rectangle _Bounds = System.Windows.Forms.Screen.PrimaryScreen.Bounds;
    public static System.Drawing.Rectangle Bounds
    {
        get { return _Bounds; }
        set { _Bounds = value; }
    }

    private static void Snapshot()
    {
        if (!Directory.Exists(tempDir))
            Directory.CreateDirectory(tempDir);
        int Co = 0;
        do
        {
            Co += 1;
            System.Threading.Thread.Sleep(50);
            System.Drawing.Bitmap X = new System.Drawing.Bitmap(_Bounds.Width, _Bounds.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            using(System.Drawing.Graphics G = System.Drawing.Graphics.FromImage(X)) {
                G.CopyFromScreen(_Bounds.Location, new System.Drawing.Point(), _Bounds.Size);
                System.Drawing.Rectangle CurBounds = new System.Drawing.Rectangle(System.Drawing.Point.Subtract(System.Windows.Forms.Cursor.Position,Bounds.Size), System.Windows.Forms.Cursor.Current.Size);
                System.Windows.Forms.Cursors.Default.Draw(G, CurBounds);
           }
            System.IO.FileStream FS = new System.IO.FileStream(tempDir + FormatString(Co.ToString(), 5, '0') + ".png", System.IO.FileMode.OpenOrCreate);
            X.Save(FS, System.Drawing.Imaging.ImageFormat.Png);
            X.Dispose();
            FS.Close();
        } while (true);
    }

    public static void ClearRecording()
    {
        if (Directory.Exists(tempDir))
            Directory.Delete(tempDir, true);
            Directory.CreateDirectory(tempDir);
    }

    public static void Save(string Output)
    {
        System.Windows.Media.Imaging.GifBitmapEncoder G = new System.Windows.Media.Imaging.GifBitmapEncoder();

        List<System.IO.FileStream> X = new List<System.IO.FileStream>();
        foreach (string Fi in Directory.GetFiles(tempDir, "*.png", SearchOption.TopDirectoryOnly))
        {
            System.IO.FileStream TempStream = new System.IO.FileStream(Fi, System.IO.FileMode.Open);
            System.Windows.Media.Imaging.BitmapFrame Frame = System.Windows.Media.Imaging.BitmapFrame.Create(TempStream);
            X.Add(TempStream);
            G.Frames.Add(Frame);
        }
        System.IO.FileStream FS = new System.IO.FileStream(Output, System.IO.FileMode.OpenOrCreate);
        G.Save(FS);
        FS.Close();

        foreach (System.IO.FileStream St in X)
        {
            St.Close();

        }

    }

    public static void Start()
    {
        snap = new System.Threading.Thread(Snapshot);
        snap.Start();
    }

    public static void Stop()
    {
        snap.Abort();
    }

    private static string FormatString(string S, int places, char character)
    {
        if (S.Length >= places)
            return S;
        for (int X = S.Length; X <= places; X++)
        {
            S = character + S;
        }
        return S;
    }

}

Код для KeyboardListener:

public class LowLevelKeyboardListener
{

    private const int WH_KEYBOARD_LL = 13;
    private const int WM_KEYDOWN = 0x0100;
    private const int WM_SYSKEYDOWN = 0x0104;

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool UnhookWindowsHookEx(IntPtr hhk);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr GetModuleHandle(string lpModuleName);


    public delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
    public event EventHandler<KeyPressedArgs> OnKeyPressed;

    private LowLevelKeyboardProc _proc;
    private IntPtr _hookID = IntPtr.Zero;

    public LowLevelKeyboardListener()
    {
        //_setHookReference = SetHook;
        _proc = HookCallback;
    }

    public void HookKeyboard()
    {
        _hookID = SetHook(_proc);
    }

    public void UnHookKeyboard()
    {
        UnhookWindowsHookEx(_hookID);
    }

    private IntPtr SetHook(LowLevelKeyboardProc proc)
    {
        using (Process curProcess = Process.GetCurrentProcess())
        using (ProcessModule curModule = curProcess.MainModule)
        {
            IntPtr hook = SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0);
            //SetWindowsHookEx(WH_MOUSE_LL, proc, GetModuleHandle("user32"), 0);
            if (hook == IntPtr.Zero)
            {
                throw new Win32Exception();
            }
            return hook;
        }
    }

    public IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_SYSKEYDOWN)
        {
            int vkCode = Marshal.ReadInt32(lParam);
            if (OnKeyPressed != null)
                OnKeyPressed(this, new KeyPressedArgs(KeyInterop.KeyFromVirtualKey(vkCode)));
        }
        return CallNextHookEx(_hookID, nCode, wParam, lParam);
    }
}

Обычно оба функциональные возможности (перехват клавиш и запись экрана) работают правильно, но не вместе, когда Application.Run() регулируется.

1 Ответ

0 голосов
/ 19 апреля 2020

Рабочее решение ниже:

Как и Йорген упомянул Application.Run () блокирует, но перемещение оператора и в целом игра с кодом было очень непредсказуемым и из-за блокировки l oop и поток в коде было трудно отлаживать. Наконец, после исправления кода ScreenRecorder, который был чистым однозначным Delphi переписыванием, мне удалось сделать его рабочим решением. Теперь я могу записывать экран и ловить клавиши, даже когда терминал активен windows:

public class ScreenRecorder
{
    // C:\Users\sebas.000\AppData\Local\Temp\snapshot
    private static string tempSnapshotDir = Path.GetTempPath() + "snapshot\\";
    private static Thread snapThread = new Thread(Snapshot);
    private static bool flag = false;

    private static Rectangle _Bounds = Screen.PrimaryScreen.Bounds;

    public static Rectangle Bounds
    {
        get { return _Bounds; }
        set { _Bounds = value; }
    }

    private static void Snapshot()
    {
        ClearRecording();

        using (var memoryBitmap = new Bitmap(_Bounds.Width, _Bounds.Height, Imaging.PixelFormat.Format32bppArgb))
        using (var graphSurface = Graphics.FromImage(memoryBitmap))
        {
            //var currentBounds = new Rectangle();
            var Counter = (UInt64)0;                
            var freshPoint = new System.Drawing.Point();

            flag = true;
            do
            {                    
                Thread.Sleep(100);
                graphSurface.CopyFromScreen(_Bounds.Location, freshPoint, _Bounds.Size);

                // add cursor
                //currentBounds.Size = Cursor.Current.Size;
                //currentBounds.Location = System.Drawing.Point.Subtract(Cursor.Position, Bounds.Size);
                //Cursors.Default.Draw(graphSurface, currentBounds);

                Counter++;

                var fileName = FormatFileName(Counter.ToString(), 6, '0', ".png");
                using (var FS = new FileStream(string.Concat(tempSnapshotDir, fileName), FileMode.Create, FileAccess.Write))
                {
                    memoryBitmap.Save(FS, ImageFormat.Png);
                }

            } while (flag);
        }
    }

    private static void ClearRecording()
    {
        if (Directory.Exists(tempSnapshotDir))
            Directory.Delete(tempSnapshotDir, true);

        Directory.CreateDirectory(tempSnapshotDir);
    }

    public static void StartRecording()
    {
        //snapThread = new Thread(Snapshot);
        snapThread.Start();
    }

    public static void StopRecording()
    {
        flag = false;
        snapThread.Join();
    }

    public static void Save(string outpuFinlename)
    {
        var gifBitmapEncoder = new GifBitmapEncoder();
        var fileStreamList = new List<FileStream>();

        // encode GIF from PNGs
        foreach (string pngFile in Directory.GetFiles(tempSnapshotDir, "*.png", SearchOption.TopDirectoryOnly)) // efficiency !!! create list like Counter !!!
        {
            var tempStream = new FileStream(pngFile, FileMode.Open);            
            var bitmapFrame = BitmapFrame.Create(tempStream);
            fileStreamList.Add(tempStream);
            gifBitmapEncoder.Frames.Add(bitmapFrame);                
        }

        // save GIF to disk
        using (var fileStream = new FileStream(outpuFinlename, FileMode.Create, FileAccess.Write))
        {                
            gifBitmapEncoder.Save(fileStream);
        }

        fileStreamList.Clear();
        ClearRecording();
    }

    private static string FormatFileName(string S, int places, char character, string extension)
    {
        if (S.Length >= places)
            return S;

        return S.PadLeft(places, '0') + extension;
    }
}

Основной поток:

    public static void Main(string[] args)
    {

        ScreenRecorder.StartRecording();
        int n = 5; //seconds of capturing

        //DispatcherTimer dispacherTimer = new DispatcherTimer();            
        var timer = new Timers.Timer(n * 1000);           
        timer.Elapsed += _timer_Tick;
        timer.AutoReset = false;
        timer.Start();

        _listener = new LowLevelKeyboardListener();
        _listener.OnKeyPressed  += _listener_OnKeyPressed;
        _listener.HookKeyboard();

        // message loop needed for LowLevelKeyboardListener
        Application.Run();

        _listener.UnHookKeyboard();
    }

Код выше позволяет мне записывать экран и то же Нажатие кнопки улавливания времени, даже когда терминал находится в фоновом режиме Спасибо за вклад в комментарии. Клавиатура слушателя такая же, как и в вопросе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...