0

定期的にスクリーンショットを取得する簡単なアプリケーションを作成しました。

私が抱えている主な問題は、アプリケーションを移動するとフリーズすることです。

したがって、主な目標は、スクリーンショット、スレッドなどのこの影響を取り除くことです.

ここにすべてのコードを入れましたが、動作するので再現できます。

このコードの .NET プロファイリング情報を次に示します。

ここに画像の説明を入力

ここに画像の説明を入力

どうすれば修正できますか?

XAML

<Window x:Class="Screenshot.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded" >
    <Grid Height="Auto">
        <Image Name="Image1"/>
    </Grid>
</Window>

C#

public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();            
        }

        ScreenGrabber grabber;

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            grabber = new ScreenGrabber(5);
            grabber.Changed += new ChangedEventHandler(grabber_Changed);
        }

        void grabber_Changed(object sender, EventArgs e)
        {
            Image1.Dispatcher.Invoke(new Action(() => {
                BitmapSource bs = ((ScreenGrabber)sender).GetImage();
                Image1.Width = bs.Width;
                Image1.Height = bs.Height;
                Image1.Source = bs;
            } ));
        } 
    }

C# DLL

namespace MyScreenGrabber
{
    public delegate void ChangedEventHandler(object sender, EventArgs e);

    public class ScreenGrabber : Window
    {
        public event ChangedEventHandler Changed;

        protected virtual void OnChanged(EventArgs e)
        {
            if (Changed != null)
                Changed(this, e);
        }

        byte[] BitmapData { set; get; }

        int Interval { set; get; }

        DispatcherTimer Timer { set; get; }

        public ScreenGrabber(int interval)
        {
            Interval = interval;
            Timer = new DispatcherTimer();
            Timer.Interval = new TimeSpan(0, 0, Interval);
            Timer.Tick += new EventHandler(Timer_Tick);
            Timer.Start();
        }

        void Timer_Tick(object sender, EventArgs e)
        {
            WindowInteropHelper windowInteropHelper = windowInteropHelper = new WindowInteropHelper(this);
            Screen screen = Screen.FromHandle(windowInteropHelper.Handle);

            using (MemoryStream ms = new MemoryStream())
            {
                if (screen != null)
                {
                    using (Bitmap bitmap = new Bitmap(screen.Bounds.Size.Width, screen.Bounds.Size.Height))
                    {
                        using (Graphics g = Graphics.FromImage(bitmap))
                        {
                            g.CopyFromScreen(screen.Bounds.X, screen.Bounds.Y, 0, 0, screen.Bounds.Size, CopyPixelOperation.SourceCopy);
                        }
                        ImageCodecInfo myImageCodecInfo;
                        myImageCodecInfo = GetEncoderInfo("image/jpeg");
                        System.Drawing.Imaging.Encoder myEncoder;
                        myEncoder = System.Drawing.Imaging.Encoder.Quality;
                        EncoderParameters encoderParameters = new EncoderParameters();
                        EncoderParameter encoderParameter = new EncoderParameter(myEncoder, 25L);
                        encoderParameters.Param[0] = encoderParameter;
                        bitmap.Save(ms, myImageCodecInfo, encoderParameters);
                        BitmapData = ms.ToArray();
                        OnChanged(EventArgs.Empty);
                    }
                }
            }
        }

        static ImageCodecInfo GetEncoderInfo(String mimeType)
        {
            int j;
            ImageCodecInfo[] encoders;
            encoders = ImageCodecInfo.GetImageEncoders();
            for (j = 0; j < encoders.Length; ++j)
            {
                if (encoders[j].MimeType == mimeType)
                    return encoders[j];
            }
            return null;
        }

        public BitmapSource GetImage()
        {
            using (MemoryStream ms = new MemoryStream(this.BitmapData))
            {
                var decoder = BitmapDecoder.Create(ms, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
                return decoder.Frames[0];
            }
        }
    }
}

最適化されたコード:

namespace MyScreenGrabber
{
    public delegate void ChangedEventHandler(object sender, EventArgs e);

    public class ScreenGrabber : Window
    {
        public event ChangedEventHandler Changed;

        protected virtual void OnChanged(EventArgs e)
        {
            if (Changed != null)
                Changed(this, e);
        }

        byte[] BitmapData { set; get; }

        int Interval { set; get; }

        WindowInteropHelper windowInteropHelper;
        Screen screen;

        DispatcherTimer Timer { set; get; }

        BackgroundWorker worker = new BackgroundWorker();

        public ScreenGrabber(int interval)
        {
            Interval = interval;

            windowInteropHelper = windowInteropHelper = new WindowInteropHelper(this);
            screen = Screen.FromHandle(windowInteropHelper.Handle);

            isDone = true;

            Timer = new DispatcherTimer();
            Timer.Interval = new TimeSpan(0, 0, Interval);
            Timer.Tick += new EventHandler(Timer_Tick);
            Timer.Start();

            worker.DoWork += new DoWorkEventHandler(worker_DoWork);
            worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
        }

        void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            OnChanged(EventArgs.Empty);
            isDone = true;
        }

        bool isDone;

        void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            GetScreenshot();
        }

        void Timer_Tick(object sender, EventArgs e)
        {
            if (isDone)
            {
                isDone = false;
                worker.RunWorkerAsync();
            }
        }

        void GetScreenshot()
        {
            using (MemoryStream ms = new MemoryStream())
            {
                if (screen != null)
                {
                    using (Bitmap bitmap = new Bitmap(screen.Bounds.Size.Width, screen.Bounds.Size.Height))
                    {
                        using (Graphics g = Graphics.FromImage(bitmap))
                        {
                            g.CopyFromScreen(screen.Bounds.X, screen.Bounds.Y, 0, 0, screen.Bounds.Size, CopyPixelOperation.SourceCopy);
                        }
                        ImageCodecInfo myImageCodecInfo;
                        myImageCodecInfo = GetEncoderInfo("image/jpeg");
                        System.Drawing.Imaging.Encoder myEncoder;
                        myEncoder = System.Drawing.Imaging.Encoder.Quality;
                        EncoderParameters encoderParameters = new EncoderParameters();
                        EncoderParameter encoderParameter = new EncoderParameter(myEncoder, 25L);
                        encoderParameters.Param[0] = encoderParameter;
                        bitmap.Save(ms, myImageCodecInfo, encoderParameters);
                        BitmapData = ms.ToArray();
                    }
                }
            }
        }

        static ImageCodecInfo GetEncoderInfo(String mimeType)
        {
            int j;
            ImageCodecInfo[] encoders;
            encoders = ImageCodecInfo.GetImageEncoders();
            for (j = 0; j < encoders.Length; ++j)
            {
                if (encoders[j].MimeType == mimeType)
                    return encoders[j];
            }
            return null;
        }

        public BitmapSource GetImage()
        {
            using (MemoryStream ms = new MemoryStream(this.BitmapData))
            {
                var decoder = BitmapDecoder.Create(ms, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
                return decoder.Frames[0];
            }
        }
    }
}
4

1 に答える 1

2

バックグラウンド ワーカー プロセスを使用して、スクリーンショットを取得する関数を実行することで、これを回避できると思います。

バックグラウンド ワーカーは別のスレッドを使用し、メイン スレッドは引き続き UI をレンダリングするため、スタックすることはありません。

編集://バックグラウンドワーカーVSデリゲートに関することを明確にするかもしれないSOでこの質問を見つけました

幸運を!

于 2012-04-14T18:13:26.337 に答える