Главная страница | назад





Article #20073: How to avoid "Random" Exceptions being raised when using TJPEGImage or the JPEG unit.

How to avoid "Random" Exceptions being raised when using TJPEGImage or the JPEG unit.

Question:
Sometimes when I try to load a JPEG into a TImage, I get an exception message even though I wrap the LoadFromFile call in a try...except. How can I avoid such exceptions, and why do they happen?

Answer:
When loading a JPEG from a file, sometimes it will load fine, but contain a corrupt JPEG image. When an attempt is made to draw the image, an exception will be raised. Quite often, the programmer may try to suppress errors with a procedure such as:

procedure TForm1.btnOpenDefaultClick(Sender: TObject);
begin
  if OpenPictureDialog1.Execute then
  try
    Image1.Picture.LoadFromFile(OpenPictureDialog1.FileName);
  except end;
end;
But, unexpectedly, JPEG's sometimes display an exception message and it appears that the creator of the JPEG unit hard-coded a ShowMessage to display the exception. The problem is that the LoadFromFile procedure executes fine, and the actual drawing of the JPEG to the TImage is what is causing the problem. The drawing of the image is implicitly called after the image has loaded from the file.

The way around this problem is to explicitly control when any possible errors happen. You can capture any errors that happen in the loading of the image, and/or in the drawing of the image.

Below is an example of how to do this. The project consists of a unit (called MainFrm.pas). It has a TImage and an OpenPictureDialog placed on the form. Two buttons are also placed on it, one named btnOpenCatch and another called btnOpenDefault. The btnOpenCatchClick procedure demonstrates how each exception can be caught and displayed the the user. Alternatively, the exception could be "eaten" with an empty except section. The btnOpenDefaultClick displays the default way of opening an image and trying to eat an exception, which may still display a JPEG error. Also notice the addition of the JPEG unit to the uses clause.

To create a corrupt JPEG that displays this error, open a JPEG image in a hex editor and change the first few bytes or the last few bytes.


unit MainFrm;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtDlgs, ExtCtrls, StdCtrls, JPEG;

type
  TForm1 = class(TForm)
    btnOpenCatch: TButton;
    Image1: TImage;
    OpenPictureDialog1: TOpenPictureDialog;
    btnOpenDefault: TButton;
    procedure btnOpenCatchClick(Sender: TObject);
    procedure btnOpenDefaultClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.btnOpenCatchClick(Sender: TObject);
var
  Picture: TPicture;
  Bitmap: TBitmap;
begin
  if OpenPictureDialog1.Execute then
  begin
    Picture := TPicture.Create;
    try
      // Try to load the picture from a file.
      // I'm wrapping it in a try..except to capture
      // any exceptions that happen in loading the image from
      // a stream.
      try
        Picture.LoadFromFile(OpenPictureDialog1.FileName);
      except
        on E: EInvalidGraphic do
        begin
          ShowMessage('Capturing Load Error: ' + E.Message);
          Exit; // Exit the procedure
        end;
      end;
      Bitmap := TBitmap.Create;
      try
        try
          // Assign the Graphic to the Bitmap; any drawing errors/exceptions
          // will happen here, and will be captured and shown to the user
          Bitmap.Assign(Picture.Graphic);
        except
          // Eat invalid graphic messages (which may be raised
          // when a jpeg is being drawn)
          on E: EInvalidGraphic do
            begin
              ShowMessage('Capturing Assign (Drawing) Error: ' + E.Message);
              Exit; // Exit the procedure
            end;
        end;
        // Now draw the picture on the screen's TImage
        Image1.Picture.Bitmap.Assign(Bitmap);
      finally
        Bitmap.Free;
      end;
    finally
      Picture.Free;
    end;
  end; // OpenPictureDialog1.Execute
end;

procedure TForm1.btnOpenDefaultClick(Sender: TObject);
begin
  if OpenPictureDialog1.Execute then
  try
    Image1.Picture.LoadFromFile(OpenPictureDialog1.FileName);
  except end;
end;

end.

Another thing to notice is that the OpenPictureDialog does not contain this fix. It will display some JPEG error messages when you click on a corrupt JPEG and it attempts to display a preview image.

Last Modified: 03-JAN-00