如何在.NET中编写屏幕录像机?

7

有没有办法在C#中制作屏幕录像?如果可以,有人知道我可以使用的任何教程或关于此主题的任何信息吗?


我敢打赌,你可以用与VB.NET屏幕录制器相同的方式来完成这个任务。 - John Saunders
1
@John Saunders - 你有想法如何完成这个任务或者有教程链接吗? - user
是的。就像VB.NET或任何其他.NET语言一样 - 它与C#无关。你可能已经知道了,但你听起来好像不知道。如果你实际上并不那么无知,那么我道歉。 - John Saunders
我认为你不需要在标签中使用“屏幕抓取”。屏幕抓取通常是指从另一个应用程序中提取一个或多个控件或屏幕中的文本。 - MusiGenesis
@John Saunders - 我知道它适用于任何 .NET 语言。 - user
4个回答

3
我将Lucas McCoy的VB.net代码转换成了C#代码。该代码可以记录5秒并将gif保存到桌面。注意:有时候当最后一个截图还在通过线程中止时,会出现错误,然后流尝试读取它。
代码非常缓慢。
using System;
using System.Collections.Generic;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;


namespace ConsoleApplication1
{

    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;
        }

    }

    class Program
    {
        static void Main(string[] args)
        {
            ScreenRecorder.Start();
            System.Threading.Thread.Sleep(5000);
            ScreenRecorder.Stop();
            ScreenRecorder.Save(Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\video.gif");
            ScreenRecorder.ClearRecording();
        }
    }
}

以下是我需要添加的参考文献:

输入图片描述


如果有人想使用代码来更快地获得工作解决方案,请在此处检查我的答案。这里 - Sebastian Xawery Wiśniowiecki

3

看一下这段VB.NET代码。

Public Class ScreenRecorder

Private Shared tempDir As String = My.Computer.FileSystem.SpecialDirectories.Temp & "snapshot"
Private Shared snap As New System.Threading.Thread(AddressOf Snapshot)
Private Shared _Bounds As System.Drawing.Rectangle = System.Windows.Forms.Screen.PrimaryScreen.Bounds

Public Shared Property Bounds() As System.Drawing.Rectangle
    Get
        Return _Bounds
    End Get
    Set(ByVal value As System.Drawing.Rectangle)
        _Bounds = value
    End Set
End Property

Private Shared Sub Snapshot()
    If Not My.Computer.FileSystem.DirectoryExists(tempDir) Then _
        My.Computer.FileSystem.CreateDirectory(tempDir)
    Dim Co As Integer = 0
    Do
        Co += 1
        System.Threading.Thread.Sleep(50)
        Dim X As New System.Drawing.Bitmap(_Bounds.Width, _Bounds.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb)
        Using G = System.Drawing.Graphics.FromImage(X)
            G.CopyFromScreen(_Bounds.Location, New System.Drawing.Point(), _Bounds.Size)
            Dim CurBounds As New System.Drawing.Rectangle(System.Windows.Forms.Cursor.Position - Bounds.Location, System.Windows.Forms.Cursor.Current.Size)
            Forms.Cursors.Default.Draw(G, CurBounds)
        End Using
        Dim FS As New IO.FileStream(tempDir & FormatString(Co.ToString, 5, "0"c) & ".png", IO.FileMode.OpenOrCreate)
        X.Save(FS, System.Drawing.Imaging.ImageFormat.Png)
        X.Dispose()
        FS.Close()
    Loop
End Sub

Public Shared Sub ClearRecording()
    If My.Computer.FileSystem.DirectoryExists(tempDir) Then _
    My.Computer.FileSystem.DeleteDirectory(tempDir, FileIO.DeleteDirectoryOption.DeleteAllContents)
    My.Computer.FileSystem.CreateDirectory(tempDir)
End Sub

Public Shared Sub Save(ByVal Output As String)
    Dim G As New Windows.Media.Imaging.GifBitmapEncoder

    Dim X As New List(Of IO.FileStream)
    For Each Fi As String In My.Computer.FileSystem.GetFiles(tempDir, FileIO.SearchOption.SearchTopLevelOnly, "*.png")
        Dim TempStream As New IO.FileStream(Fi, IO.FileMode.Open)
        Dim Frame = Imaging.BitmapFrame.Create(TempStream)
        X.Add(TempStream)
        G.Frames.Add(Frame)
    Next
    Dim FS As New IO.FileStream(Output, IO.FileMode.OpenOrCreate)
    G.Save(FS)
    FS.Close()

    For Each St As IO.FileStream In X
        St.Close()

    Next

End Sub

Public Shared Sub Start()
    snap = New System.Threading.Thread(AddressOf Snapshot)
    snap.Start()
End Sub

Public Shared Sub [Stop]()
    snap.Abort()
End Sub

Private Shared Function FormatString(ByVal S As String, ByVal places As Integer, ByVal character As Char) As String
    If S.Length >= places Then Return S
    For X As Integer = S.Length To places
        S = character & S
    Next
    Return S
End Function

End Class

问题在于引号中的正斜杠。我不知道有没有VB.NET等效的方法不会让编辑器混淆,所以这个问题就留给你了。 - John Saunders
1
我想我已经没有代码了。大约6年前,我为雇主编写了它。我肯定它仍然存在于他们的CVS存储库中,但是这些代码可能已经从我的驱动器中消失了。很抱歉。它遵循了相同的基本概念:将定期屏幕截图保存到磁盘。我记得当时尝试了几种不同的方法来优化它,但是不记得具体细节了。这期间写的代码太多了。 - Jason Jackson
1
好的,我会使用这个,并将其转换为C#(我更喜欢C#而不是VB)。希望一切顺利。谢谢。 - user
1
也许你可以在CodePlex上发布C#版本以备将来使用。祝好运! - Kredns
这段代码需要进行非常严重的清理,并且根本不应该被使用——它出现在其他问题中并引起了问题:打开流而不使用,方法FormatString试图以更慢和浪费的方式做与简单的String.Format相同的事情,只是格式为“D8”。直接写入用户的桌面。通过调用Thread.Abort停止线程,从而保证未受保护的流将保持打开状态。 - Panagiotis Kanavos
显示剩余2条评论

1

对于线程异常,您可以使用以下代码避免

    public static void Stop()
    {
        flag = false;
        snap.Join();
    }

将标志设置为静态私有布尔值(全局),并在while循环中使用它代替true值:
while(flag)

关于如何避免资源线程重叠的问题。我知道这不是主题,但我想分享这个信息,因为线程总是让调试变得棘手。

敬礼, Fawaz


0

我拿了Parox的代码并修复了一些错误,使其更适合C#语言编译器。

作为一名C#程序员,我可以说这段代码抛出错误并不奇怪。有些部分甚至可能导致堆栈溢出或杀死应用程序异常 - 但这就是我们在这个问答环节的领域 ;)

尽管如此,长时间录制可能会导致Counter溢出,并覆盖从录制开始的PNG文件以及其他问题,但在我看来已经足够好,可以附加在该线程中供其他人使用:

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;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接