2017-03-04 5 views
-1

[해결책] Ofir의 솔루션은 즉시 사용 가능합니다. 현재 24 f/s에서 5 분 동 안 실행 중입니다.Newb 오버 헤드 : 왜 집에서 만든 스크린 레코더가 손상 되었습니까? (C# 및 windows 폼)

화면에서 이미지를 저장하려고합니다.

기본적으로 내 문제는, 괜찮은 프레임 속도를 만들기 위해 Thread.sleep를 줄이면 프로그램이 충돌한다는 것입니다. 프로그램은 더 빠르게 thread.sleep을 0에 가깝게 추락 시키지만, 이런 종류의 것들을 결코 다룰 수 없기 때문에 문제를 찾을 수조차 없다. (만약 내가이 모든면에서 unity3d라면).

void ScreenCapture()//this is from a youtube tutorial 
    { 
     while (true) 
     { 
      Bitmap bm = new Bitmap((int)Math.Round(Screen.PrimaryScreen.Bounds.Width * 1.5), (int)Math.Round(Screen.PrimaryScreen.Bounds.Height * 1.5)); 
    //I don't know why I had to multipyly these by 1.5, but it made the screen fit properly. 
      Graphics g = Graphics.FromImage(bm); 
      g.CopyFromScreen(0, 0, 0, 0, bm.Size); 
      pictureBox.Image = bm; 
      Thread.Sleep(250); 
     } 
    } 

    private void button2_Click(object sender, EventArgs e) 
    { 
     Thread t = new Thread(ScreenCapture); 
     t.Start(); 
    } 

나는 감미로운 감미로운 과오를 너무 가지고있다. (나는 거의 약을 알고있는) -2147467259

내가 시도 - 캐치를 구축 :

유형 'System.ComponentModel.Win32Exception'의 처리되지 않은 예외는 System.Drawing.dll

오류 코드 발생 하지만 몇 가지 테스트 후에도 깨지면, 내가 그 로그를 얻을 수 있는지보십시오.

System.ArgumentException: Parameter is not valid. 
at System.Drawing.Bitmap..ctor(Int32 width, Int32 height, PixelFormat format) 
at System.Drawing.Bitmap..ctor(int32 width, Int32 height) 
at Screen_Recorder.Form1.ScreenCapture() C:Users\Jupiter\Desktop\visual studio experiments\Screen Recorder\ScreenRecorder\Form1.cs:line 32 

을 한 후 그 오류 무한히 반복됩니다

System.ComponentModel.Win32Exception(0x80004005): The operation completed successfully as System.Drawing.Graphics.CopyFromScreen(Int32 sourceX, Int32 sourceY, Int32 destinationX,Int32 destinationY, Size blockRegionSize) 
at Screen_Recorder.Form1.ScreenCapture() in C:Users\Jupiter\Desktop\visual studio experiments\Screen Recorder\ScreenRecorder\Form1.cs:line 35 

나는 한 다음 확인을 밝히는 클릭합니다.

어쨌든, 나는 잠자리에 들기 시작하고 나서 다시 그 안에 들어가 겠지만, 그 전에는 어떤 조언도 많이 들었을 것입니다!

답변

1

메모리 누수가 있습니다.

GraphicsBitmap은 일회용 개체이므로 사용 후에는 폐기해야합니다.
케이스에 Graphics을 넣을 수있는 곳은 using 블록이며 마지막 비트 맵은 루프 반복마다 폐기되어야합니다.
따라서 코드는 다음과 같이한다 :이 루프는 결코 (때문에 while (true)의) 종료하려고하지

private void ScreenCapture() 
{ 
    while (true) 
    { 
     var bm = new Bitmap((int)Math.Round(Screen.PrimaryScreen.Bounds.Width * 1.5), 
          (int)Math.Round(Screen.PrimaryScreen.Bounds.Height * 1.5)); 
     using (Graphics g = Graphics.FromImage(bm)) g.CopyFromScreen(0, 0, 0, 0, bm.Size); 

     // As per the comment by HansPassant - the following would cause 
     // a thread race with the UI thread. 
     //this.pictureBox1.Image?.Dispose(); 
     //this.pictureBox1.Image = bm; 

     // Instead we use beginInvoke to run this on the UI thread 
     Action action =() => 
      { 
       this.pictureBox1.Image?.Dispose(); 
       this.pictureBox1.Image = bm; 
      }; 

     this.BeginInvoke(action); 

     Thread.Sleep(250); 
    } 
} 

, 어쩌면 당신은 CancelationToken을 추가하는 것을 고려해야하며 Thread 대신 Task를 사용합니다.

그리고 마지막으로, 그림 상자 자체도 함께 마무리해야합니다.

+0

매우 일반적인 이유 중 하나 인 이유가 있지만 그 중 하나는 아닙니다. Image 속성은 스레드로부터 안전하지 않으며 UI 스레드가 이미지를 그리는 동시에 Dispose()를 호출하면 문제가 해결되지 않습니다. 실패 확률은 훨씬 낮지 만 제로가 아니기 때문에 디버깅이 훨씬 어려워집니다. –

+0

@HansPassant 나는 당신을 완전히 이해했는지 모르겠다. 그러나 이미지에 대한 참조를 저장하고 새 이미지를 설정 한 다음 이전 이미지를 처리하면 어떨까? 그게 문제를 해결할 수 있을까요? –

+0

아니요. Paint() 메서드가 실행 중이며 이전 참조를 사용하고 있기 때문에 여전히 스레드 레이스입니다. 코드가 UI 스레드에서 실행되도록 BeginInvoke()를 사용해야한다는 냉담한 사실이 있습니다. –