Jpeg保存到TThread中的base64

问题描述:

我在Delphi中遇到了一些问题。Jpeg保存到TThread中的base64

我写使截图两个简单的功能,将其转换为JPEG和解码为base64,流。 它的作品不错,如果我在主流程序上制作。但是,如果我创建一个TThread类并启动此功能执行,Windows冻结,我只能重新启动我的电脑。

通过多次尝试,我发现挂起电脑通过程序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

我猜测,但是这可能会有所帮助:http://qc.embarcadero.com/wc/qcmain.aspx?d=55871。位图不是线程安全的。您需要锁定/解锁其画布。 – kobik 2014-11-23 12:53:59

+0

@kobik听起来好像他从多个线程访问相同的位图对象。还是有一些疯狂的实施与VCL位图搞砸了? – 2014-11-23 16:51:12

+1

@DavidHeffernan TJPEGImage被搞砸了,有问题,其Bitmap.Canvas DC是在graphics.pas – 2014-11-23 20:10:17

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

必须有一个适当的第三方JPEG LIB – 2014-11-23 20:12:15

+0

@DavidHeffernan我用来修补TJPEGImage自己,但我只需要绘制JPEG的线程,并且部分是没有问题的修补(除非,当然,英巴卡迪诺忘了,包括所有的源文件需要重新编译,这是经常发生的事情)。 – 2014-11-23 20:17:38

+4

最安全的方法是使用GDI + /'CreateCompatibleDC'和'CreateBitmap'作为描述这里(http://*.com/a/14804378/937125),我也不能确定同步'JpegImg.Assign'部分。但在“ScreenShot”中锁定/解锁“DestBitmap.Canvas.Handle”是必不可少的。 +1 – kobik 2014-11-23 21:01:11