c#局域网屏幕广播二三事(1)-屏幕抓取

最近项目中需要,使用C#做了一个简版的教学软件,过程中碰到过一些坑,也终于爬出来,现在随着项目的收尾,想要通过博客的方式来梳理和记录一下相关东西。


如下为对屏幕抓取封装的Capture,其中包含了鼠标的绘制以及缩放功能。


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.IO;

namespace screen_cast
{

    class ScreenCaptureEventArgs:EventArgs
    {
        public int width, height;
        public byte[] data;
    }

    public class TimerHelper
    {
        public TimerHelper(double interval)
        {
            TimerHandler = new System.Timers.Timer(interval);
            TimerHandler.AutoReset = true;
            TimerHandler.Enabled = false;

        }
        ~TimerHelper()
        {
            TimerHandler.Enabled = false;
            TimerHandler.Dispose();
        }

        public double Interval { get { return TimerHandler.Interval; } set { TimerHandler.Interval = value; } }
        public bool Enabled { get { return TimerHandler.Enabled; } set { TimerHandler.Enabled = value; } }

        public void DelegateFunc(System.Timers.ElapsedEventHandler ElapsedHandler)
        {
            TimerHandler.Elapsed += ElapsedHandler;
        }

        private System.Timers.Timer TimerHandler;
    }

    class ScreenCapture
    {
        #region Extern Funcs
        [StructLayout(LayoutKind.Sequential)]
        struct POINTAPI
        {
            public int x;
            public int y;
        }

        [StructLayout(LayoutKind.Sequential)]
        struct CURSORINFO
        {
            public Int32 cbSize;
            public Int32 flags;
            public IntPtr hCursor;
            public POINTAPI ptScreenPos;
        }

        [DllImport("user32.dll")]
        static extern bool GetCursorInfo(out CURSORINFO pci);

        [DllImport("user32.dll")]
        static extern bool DrawIcon(IntPtr hDC, int X, int Y, IntPtr hIcon);

        const Int32 CURSOR_SHOWING = 0x00000001;
        #endregion

        #region Variables
        private ImageCodecInfo JpegEncoder = null;
        private EncoderParameters EncoderParams = null;
        private TimerHelper ScreenTimer = new TimerHelper(100);
        private int DstWidth, DstHeight;
        private Boolean ScaleEnabled = false;
        public EventHandler<ScreenCaptureEventArgs> OnScreenDataEventHandler;
        #endregion
        public ScreenCapture(long Quality = 70L)
        {
            ScreenTimer.DelegateFunc(OnTimer);

            JpegEncoder = GetEncoder(ImageFormat.Jpeg);
            EncoderParams = new EncoderParameters(1);
            EncoderParameter EncoderParam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, Quality);
            EncoderParams.Param[0] = EncoderParam;
        }

        ~ScreenCapture()
        {

        }

        #region Native Functions
        private ImageCodecInfo GetEncoder(ImageFormat format)
        {
            ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();

            foreach (ImageCodecInfo codec in codecs)
            {
                if (codec.FormatID == format.Guid)
                    return codec;
            }

            return null;
        }


        private void OnTimer(object sender, System.Timers.ElapsedEventArgs e)
        {
            DoCapture();
        }

        public Bitmap CaptureScreenFun()
        {
            Bitmap result = null;

            try
            {
                result = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format24bppRgb);
                Graphics g = Graphics.FromImage(result);
                {
                    g.CopyFromScreen(0, 0, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);

                    CURSORINFO pci;
                    pci.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(CURSORINFO));

                    if (GetCursorInfo(out pci))
                    {
                        if (pci.flags == CURSOR_SHOWING)
                        {
                            DrawIcon(g.GetHdc(), pci.ptScreenPos.x, pci.ptScreenPos.y, pci.hCursor);
                            g.ReleaseHdc();
                        }
                    }

                    g.Dispose();
                    g = null;
                }
            }
            catch
            {
                result = null;
            }

            if (ScaleEnabled == true && result != null &&
                (this.DstWidth != result.Width || this.DstHeight != result.Height))
            {
                return KiResizeImage(result, this.DstWidth, this.DstHeight);
            }


            return result;
        }

        private Bitmap KiResizeImage(Bitmap bmp, int newW, int newH)
        {
            try
            {
                Bitmap b = new Bitmap(newW, newH);
                Graphics g = Graphics.FromImage(b);

                g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;

                g.DrawImage(bmp, new Rectangle(0, 0, newW, newH), new Rectangle(0, 0, bmp.Width, bmp.Height), GraphicsUnit.Pixel);
                g.Dispose();
                g = null;

                return b;
            }
            catch
            {
                return null;
            }
        }

        private void DoCapture()
        {
            Bitmap Bmp = CaptureScreenFun();
            if (Bmp == null)
                return;

            MemoryStream MSBmp = new MemoryStream();

            Bmp.Save(MSBmp, JpegEncoder, EncoderParams);

            Byte[] ArrBmp = MSBmp.ToArray();
            MSBmp.Dispose();
            MSBmp.Close();
            MSBmp = null;

            GC.Collect();
            OnScreenDataEventHandler(this, new ScreenCaptureEventArgs() {width = Bmp.Width,height = Bmp.Height,data = ArrBmp });
        }
        #endregion

        #region Interface
        public void StartCapture(double Interval, int dstWidth = 0, int dstHeight = 0, bool bScale = false)
        {
            this.ScaleEnabled = bScale;
            this.DstWidth = dstWidth;
            this.DstHeight = dstHeight;
            ScreenTimer.Interval = Interval;
            ScreenTimer.Enabled = true;
            DoCapture();
        }

        public void UpdateQuality(long Quality)
        {
            EncoderParameter EncoderParam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, Quality);
            EncoderParams.Param[0] = EncoderParam;
        }

        public void UpdateInterval(double Interval)
        {
            ScreenTimer.Interval = Interval;
        }

        public void UpdateScale(bool bEnable)
        {
            this.ScaleEnabled = bEnable;
        }

        public void StopCapture()
        {
            ScreenTimer.Enabled = false;
        }
        #endregion
    }
}


主界面仅仅添加一个Panel,一个PictureBox,一个Button,两个TextEdit,如下图:



其中对Capture类的使用如下:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using System.IO;

namespace screen_cast
{
    public partial class MainFrame : Form
    {
        private ScreenCapture _ObjCapture = new ScreenCapture();
        private Boolean _bRunning = false;

        public MainFrame()
        {
            InitializeComponent();

            this.text_fps.Text = "20";
            this.text_quality.Text = "80";
            this._ObjCapture.OnScreenDataEventHandler += new EventHandler<ScreenCaptureEventArgs>(this.OnScreenData);
            this.picture_screen.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
        }

        private void btn_start_Click(object sender, EventArgs e)
        {
            if (this._bRunning == false)
            {
                int Fps = int.Parse(this.text_fps.Text);
                int Quality = int.Parse(this.text_quality.Text);

                this._ObjCapture.UpdateQuality(Quality);
                this._ObjCapture.StartCapture(1000L / Fps);

                this._bRunning = true;
                this.btn_start.Text = "Stop";
            }
            else
            {
                this._ObjCapture.StopCapture();

                this._bRunning = false;
                this.btn_start.Text = "Start";
            }

        }

        private void OnScreenData(Object obj,ScreenCaptureEventArgs evt)
        {
            try
            {
                MemoryStream imgStream = new MemoryStream(evt.data, 0, evt.data.Length);
                Bitmap map = (Bitmap)Image.FromStream(imgStream);
                if (map != null)
                    this.picture_screen.BackgroundImage = map;
            }
            catch
            {

            }
        }
    }
}

运行效果如下图:




源码传送门:

C#屏幕广播源码V1.0

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值