【问题标题】:InvalidOperationException while saving a bitmap and using graphics.copyFromScreen parallel-y保存位图和使用 graphics.copyFromScreen 并行-y 时出现 InvalidOperationException
【发布时间】:2017-07-27 09:25:33
【问题描述】:

我正在编写一个应用程序来使用CopyFromScreen 方法捕获屏幕,并且还想保存我捕获的图像以通过我的本地网络发送。 因此,我尝试将捕获的屏幕存储在一个位图上,并将另一个位图(即先前捕获的屏幕)保存在两个线程上。

但是,这会抛出一个InvalidOperationException,它表示对象当前正在其他地方使用。 System.Drawing.dll 引发异常。
我尝试过锁定,并且正在使用单独的位图来保存和捕获屏幕。我该如何阻止这种情况发生?相关代码:

Bitmap ScreenCapture(Rectangle rctBounds)
{
    Bitmap resultImage = new Bitmap(rctBounds.Width, rctBounds.Height);

    using (Graphics grImage = Graphics.FromImage(resultImage))
    {
        try
        {
            grImage.CopyFromScreen(rctBounds.Location, Point.Empty, rctBounds.Size);
        }
        catch (System.InvalidOperationException)
        {
            return null;
        }
    }
    return resultImage;
}

void ImageEncode(Bitmap bmpSharedImage)
{
    // other encoding tasks
    pictureBox1.Image = bmpSharedImage;
    try
    {
        Bitmap temp = (Bitmap)bmpSharedImage.Clone();
        temp.Save("peace.jpeg");
    }
    catch (System.InvalidOperationException)
    {
        return;
    }
}

private void button1_Click(object sender, EventArgs e)
{
    timer1.Interval = 30;
    timer1.Start();
}

Bitmap newImage = null;

private async void timer1_Tick(object sender, EventArgs e)
{
    //take new screenshot while encoding the old screenshot 

    Task tskCaptureTask = Task.Run(() =>
        {
            newImage = ScreenCapture(_rctDisplayBounds);
        });
    Task tskEncodeTask = Task.Run(() =>
            {
                try
                {
                    ImageEncode((Bitmap)_bmpThreadSharedImage.Clone());
                }
                catch (InvalidOperationException err)
                {
                    System.Diagnostics.Debug.Write(err.Source);
                }
            });

    await Task.WhenAll(tskCaptureTask, tskEncodeTask);

    _bmpThreadSharedImage = newImage;
}

【问题讨论】:

  • 它究竟在哪里确定正在使用的东西?
  • 我假设它是 _bmpThreadSharedImage,您没有包含在上面的代码中导致问题?
  • @BugFinder 异常未处理消息出现在 Program.cs 中的 Application.Run(new Form1()) 行,CopyFromScreenBitmap.Save 方法被突出显示
  • 克隆图像,然后运行任务..(注意您不需要重新克隆它)
  • @BugFinder 那是立即抛出同样的异常

标签: c# multithreading graphics bitmap


【解决方案1】:

我通过创建一个带有单个按钮的简单 winforms 项目,简而言之重现了您的问题。

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        Task.Run(() => SomeTask());
    }

    public void SomeTask() //this will result in 'Invalid operation exception.'
    {
        var myhandle = System.Drawing.Graphics.FromHwnd(Handle);
        myhandle.DrawLine(new Pen(Color.Red), 0, 0, 100, 100);
    }
}

要解决此问题,您需要执行以下操作:

public partial class Form1 : Form
{
    private Thread myUIthred;

    public Form1()
    {
        myUIthred = Thread.CurrentThread;
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        Task.Run(() => SomeTask());
    }

    public void SomeTask() // Works Great.
    {
        if (Thread.CurrentThread != myUIthred) //Tell the UI thread to invoke me if its not him who is running me.
        {
            BeginInvoke(new Action(SomeTask));
            return;
        }
        var myhandle = System.Drawing.Graphics.FromHwnd(Handle);
        myhandle.DrawLine(new Pen(Color.Red), 0, 0, 100, 100);
    }

}

问题是(正如 Spektre 暗示的那样)尝试从非 UI 线程调用 UI 方法的结果。 “BeginInvoke”实际上是“this.BeginInvoke”,“this”是由 UI 线程创建的表单,因此一切正常。

【讨论】:

    【解决方案2】:

    我没有在 C# 中编码,所以我在这里可能是错的,但我假设您使用的是 Windows...

    WndProc 函数之外访问任何可视组件(如GDI 位图 或窗口...)都是不安全的。因此,如果您使用 GDI 位图(带有设备上下文的位图)或在任何线程内从窗口渲染/访问任何可视组件,那么就会出现问题。之后,在您的应用中对 WinAPI 的任何调用都可能引发异常(甚至与图形无关)

    因此,请尝试将任何此类代码移动到您的 WndProc 函数中。如果您无权访问它,请使用窗口中的任何事件(例如 OnTimerOnIdle)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多