2017-07-27 25 views
6

我正在編寫一個應用程序來使用CopyFromScreen方法捕獲屏幕,並且還想保存捕獲的圖像以通過本地網絡發送。 因此,我試圖將捕獲的屏幕存儲在一個位圖上,並在兩個線程上保存另一個位圖,這是之前捕獲的屏幕。InvalidOperationException保存位圖並使用graphics.copyFromScreen並行-y

但是,這是拋出一個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; 
} 

img1img2

+1

它究竟在哪裏決定使用什麼東西? – BugFinder

+0

我假設它是'_bmpThreadSharedImage'你沒有包含在上面導致問題的代碼中? – DavidG

+0

@BugFinder異常未處理的消息出現在Program.cs中的Application.Run(new Form1())行,並且「CopyFromScreen」和「Bitmap.Save」方法突出顯示 – Priyank

回答

4

我通過一個簡單的按鈕創建一個簡單的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線程創建的表單,因此全部工作。

1

我不C#代碼所以我可能是錯在這裏,但我假設你使用的是Windows ...

訪問任何可視化組件(如GDI位圖或窗口...)在WndProc功能外不安全。因此,如果您在任何線程內使用GDI位圖(位圖與設備上下文)或渲染/訪問窗口中的任何可視組件,那麼存在您的問題。在此之後,以任何調用WinAPI的在您的應用程序可以拋出一個異常(甚至無關的圖形)

所以嘗試移動任何這樣的代碼到你的WndProc功能。如果您無法訪問它,請使用任何窗口事件(如OnTimerOnIdle)。