2014-11-23 3 views
3

나는 델파이에 약간의 문제가있다.Jpeg TThread에서 base64로 저장

저는 스크린 샷을 만들기위한 두 가지 간단한 함수를 작성하고 jpeg로 변환하고 base64 스트림으로 디코딩했습니다. 그리고 메인 스트림 프로그램에서 잘 만들면 잘 작동합니다. 하지만 TThread 클래스를 만들고 Execute에서이 함수를 시작하면 Windows가 멈추고 컴퓨터를 재부팅 할 수 있습니다.

여러 시도를하면 프로 시저를 통해 PC가 응답하지 않는다는 것을 발견했습니다. JpegImg.SaveToStream(Input); 그리고 비트 맵을 jpeg로 변환하지 않으면 작동이 좋아서 이미지 문자열을 얻습니다.

도와주세요. 여기

코드

procedure TEvReader.ScreenShot(DestBitmap : TBitmap) ; 
var DC : HDC; 
begin DC := GetDC (GetDesktopWindow) ; 
    try 
    DestBitmap.Width := GetDeviceCaps (DC, HORZRES) ; 
    DestBitmap.Height := GetDeviceCaps (DC, VERTRES) ; 
    BitBlt(DestBitmap.Canvas.Handle, 0, 0, DestBitmap.Width, DestBitmap.Height, DC, 0, 0, SRCCOPY) ; 
    finally 
    ReleaseDC (GetDesktopWindow, DC) ; 
    end; 
end; 


function TEvReader.Base64FromBitmap(Bitmap: TBitmap): string; 
var 
    Input: TBytesStream; 
    Output: TStringStream; 
    JpegImg:TJPEGImage; 
begin 
    Input := TBytesStream.Create; 
    try 
    JpegImg:=TJPEGImage.Create; 
    JpegImg.Assign(Bitmap); 


    JpegImg.SaveToStream(Input); {here a problem.When i replace "JpegImg" to "Bitmap" all works good } 
    Input.Position := 0; 
    Output := TStringStream.Create('', TEncoding.ASCII); 
    try 
     Soap.EncdDecd.EncodeStream(Input, Output); 
     Result := Output.DataString; 
    finally 
     Output.Free; 
    end; 
    finally 
    Input.Free; 
    end; 
end; 


procedure TOutThread.Execute; 
var 

bmp:TBitmap; 
strrr:String; 
begin 

    bmp:=TBitmap.Create; 
    mObj.ScreenShot(bmp); 

    strrr := mObj.Base64FromBitmap(bmp); 

    Form2.Memo4.Text := strrr; 

end; 
+2

I 추측하지만,이 도움이 될 수 있습니다 :

는 다음 코드를 사용하여 시도 http://qc.embarcadero.com/wc/qcmain.aspx?d=55871을. 비트 맵은 스레드로부터 안전하지 않습니다. 캔버스를 잠 그거나 잠금 해제해야합니다. – kobik

+0

@kobik 마치 여러 스레드에서 동일한 비트 맵 객체에 액세스하는 것처럼 들리지는 않습니다. 아니면 VCL 비트 맵을 사용하는 일부 미친 구현이 있습니까? –

+1

@DavidHeffernan TJPEGImage가 엉망입니다. 그래픽의 GDI 개체 캐싱 메커니즘으로 인해 때때로 지워지는 Bitmap.Canvas DC와 관련된 문제가 있습니다. –

답변

3

TJPEGImage는 스레드로부터 안전하지 않습니다. http://qc.embarcadero.com/wc/qcmain.aspx?d=55871에 언급 된 스레드 안전 도면과 관련된 문제는 Delphi XE6에서 다소 고정되어 있지만 (Canvas 속성을 노출시킴으로써 자물쇠를 채워야 함), 아마도 도움이되지 않을 것입니다.

메인 스레드와 TJPEGImage 처리를 동기화해야합니다.

또한 코드에서 JpgImg 및 Bmp 객체를 사용한 적이 없기 때문에 메모리 누수가 발생했습니다.

procedure TEvReader.ScreenShot(DestBitmap: TBitmap); 
var 
    DC: HDC; 
begin 
    DC := GetDC(GetDesktopWindow); 
    DestBitmap.Canvas.Lock; 
    try 
    DestBitmap.Width := GetDeviceCaps(DC, HORZRES); 
    DestBitmap.Height := GetDeviceCaps(DC, VERTRES); 
    BitBlt(DestBitmap.Canvas.Handle, 0, 0, DestBitmap.Width, DestBitmap.Height, DC, 0, 0, SRCCOPY); 
    finally 
    DestBitmap.Canvas.Unlock; 
    ReleaseDC(GetDesktopWindow, DC); 
    end; 
end; 

function TEvReader.Base64FromBitmap(Bitmap: TBitmap): string; 
var 
    Input: TBytesStream; 
    Output: TStringStream; 
    JpegImg: TJPEGImage; 
begin 
    Input := TBytesStream.Create; 
    try 
    JpegImg := TJPEGImage.Create; 
    try 
     TThread.Synchronize(nil, 
     procedure 
     begin 
      JpegImg.Assign(Bitmap); 
      JpegImg.SaveToStream(Input); 
     end); 
    finally 
     JpegImg.Free; 
    end; 
    Input.Position := 0; 
    Output := TStringStream.Create('', TEncoding.ASCII); 
    try 
     Soap.EncdDecd.EncodeStream(Input, Output); 
     Result := Output.DataString; 
    finally 
     Output.Free; 
    end; 
    finally 
    Input.Free; 
    end; 
end; 

procedure TOutThread.Execute; 
var 
    mObj: TEvReader; 
    bmp: TBitmap; 
    strrr: string; 
begin 
    mObj := TEvReader.Create; 
    bmp := TBitmap.Create; 
    try 
    mObj.ScreenShot(bmp); 
    strrr := mObj.Base64FromBitmap(bmp); 
    finally 
    bmp.Free; 
    mObj.Free; 
    end; 

    Synchronize(nil, 
    procedure 
    begin 
     Form2.Memo4.Text := strrr; 
    end); 
end; 
+0

제 3 자 jpeg lib가 있어야합니다. –

+0

@DavidHeffernan 직접 TJPEGImage를 패치하는 데 사용되었지만 스레드에서 JPEG를 그릴 필요가 있으며 패치 할 때 문제가되지 않습니다 (물론 Embarcadero가 모든 소스 파일을 포함하는 것을 잊지 않는 한) 재 컴파일에 필요합니다. 정기적으로 일어나는 일). –

+4

가장 안전한 방법은 [here] (http://stackoverflow.com/a/14804378/937125)에서 설명한대로 GDI + /'CreateCompatibleDC'와'CreateBitmap'을 사용하는 것입니다.'JpegImg.Assign' 부품. 'ScreenShot'에서'DestBitmap.Canvas.Handle'의 잠금/잠금 해제가 필수적입니다. +1 – kobik