Lazarus GDI+ problem. Access Violation lazarus / 39-Graphics ---------------------------------------------------------------------- Nick00 · 2023-04-12 06:47 I am porting an application from Delphi to Lazarus. There was a problem with the following piece of code: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i uses GDIPAPI, GDIPOBJ;...var ist:IStream;    ms:TMemoryStream;    img:TGPImage;begin    ...    //загрузка из БД blob поля в поток    (DS.FieldByName('IMAGE') as TBlobField).SaveToStream(ms);    //создание TGPImage    ms.Position:=0;    ist:=TStreamAdapter.Create(ms);    img:=TGPImage.Create(ist); // на этой строке возникает исключение    ...end;The problem is throwing an exception External: Access Violation... Write of address 00000000. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i TGPImage constructor:constructor TGPImage.Create(stream: IStream;  useEmbeddedColorManagement: BOOL = FALSE);begin  nativeImage := nil;  if (useEmbeddedColorManagement) then    lastResult := GdipLoadImageFromStreamICM(stream, nativeImage)  else lastResult := GdipLoadImageFromStream(stream, nativeImage);end;The modules GDIPAPI, GDIPOBJ are identical for Delphi and Lazarus, except for the line Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$IFDEF FPC} {$MODE Delphi} {$ENDIF}in modules for Lazarus. For an ms stream, ms.Size in Delphi = ms.Size in Lazarus for a single database entry. Interestingly, if write Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i ms.Position:=1;then an exception is not thrown, well, in img, of course, garbage. Who has any ideas how to deal with the problem? ---------------------------------------------------------------------- marcov · 2023-04-12 10:33 If you save the memory stream to disc after the ms.position:=0, is the result a valid image or does it have some bytes prepended? ---------------------------------------------------------------------- Nick00 · 2023-04-13 02:21 The IMAGE field contains an image in emf format. If i do ms.SaveToFile, the valid image is saved to the file. ---------------------------------------------------------------------- Nick00 · 2023-05-02 03:12 I found out that the problem occurs when rendering the emf / wmf format. JPEG Rendered without problems. Attached is a test project. https://drive.google.com/file/d/1Ee301- ej4iW25z7isH8eW37r6ksZe3P5/view?usp=share_link ---------------------------------------------------------------------- Jurassic Pork · 2023-05-02 05:43 hello, for GDI+ you can try the GDIPlus-Library-for-Delphi-and-Lazarus. There is a package for Lazarus. Exemple : Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i implementationuses  GDIPlus; {$R *.lfm} { TForm1 } procedure TForm1.PaintBox1Paint(Sender: TObject);var img:IGPImage;    g: IGPGraphics;begin  img := TGPImage.Create('image.emf');  g:=TGPGraphics.Create(PaintBox1.Canvas.Handle);  g.SetPageUnit (UnitPixel);  g.DrawImage(img,0,0,PaintBox1.Width,PaintBox1.Height); end;       Friendly, J.P ---------------------------------------------------------------------- Nick00 · 2023-05-02 06:47 Quote from: Jurassic Pork on May 02, 2023, 07:43:00 amhello, for GDI+ you can try the GDIPlus-Library-for-Delphi-and-Lazarus. There is a package for Lazarus. Exemple : Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i implementationuses  GDIPlus; {$R *.lfm} { TForm1 } procedure TForm1.PaintBox1Paint(Sender: TObject);var img:IGPImage;    g: IGPGraphics;begin  img := TGPImage.Create('image.emf');  g:=TGPGraphics.Create(PaintBox1.Canvas.Handle);  g.SetPageUnit (UnitPixel);  g.DrawImage(img,0,0,PaintBox1.Width,PaintBox1.Height); end;       Friendly, J.P            Thanks for the answer. Your code works with my version of the library too. However, in both versions of the library, an error occurs if the image is loaded from a stream. Loading from a file doesn't work for me as the images are stored in the database in binary form. ---------------------------------------------------------------------- loaded · 2023-05-02 16:01 Quote from: Nick00 on May 02, 2023, 08:47:06 amThanks for the answer. Your code works with my version of the library too. However, in both versions of the library, an error occurs if the image is loaded from a stream. Loading from a file doesn't work for me as the images are stored in the database in binary form. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i uses GdiPlus,Activex,Comobj;  function MemoryStreamToIStream(const AStream: TMemoryStream): IStream;var  Output: IStream;  Data: Pointer;  Size: Longint;  test:Qword;begin  Data := AStream.Memory;  Size := AStream.Size;  OleCheck(CreateStreamOnHGlobal(0, True, Output));  try    OleCheck(Output.Write(Data, Size, nil));    OleCheck(Output.Seek(0, STREAM_SEEK_SET, test));    Result := Output;  except    Output := nil;    raise;  end;end;  procedure TForm1.Button1Click(Sender: TObject);var    img:IGPImage;    g: IGPGraphics;    Ms:TMemoryStream;    Stream : IStream;begin   Ms:=TMemoryStream.Create;  Ms.LoadFromFile('C:\Users\mali.aydin\Desktop\E.emf');  Ms.Position:=0;   //In the example, I have uploaded a file to the Memory Stream object.  //If you want, you can directly load the stream object that you read from the database into the memory stream object.     Stream:=MemoryStreamToIStream(ms);   img := TGPImage.Create(stream);  g:=TGPGraphics.Create(Image1.Canvas.Handle);  g.SetPageUnit (UnitPixel);  g.DrawImage(img,0,0,Image1.Width,Image1.Height);end; ---------------------------------------------------------------------- Nick00 · 2023-05-03 02:56 Thanks, it works. I wonder why the TMemoryStream->IStream conversion does not work through the TStreamAdapter... ---------------------------------------------------------------------- loaded · 2023-05-03 04:58 Quote from: Nick00 on May 03, 2023, 04:56:18 amThanks, it works. I wonder why the TMemoryStream->IStream conversion does not work through the TStreamAdapter... First, we can save the streams to the file and check their content. If the contents are equal, the source of the problem is likely to be in the position information of the stream. ---------------------------------------------------------------------- marcov · 2023-05-03 07:17 Some fields written with tdbimage also have a small record with info before the image. Another thing, is this 32 or 64-bit? It might be simply GDI headers being untested for 64-bit ? ---------------------------------------------------------------------- Jurassic Pork · 2023-05-03 07:35 Hello, Quote from: marcov on May 03, 2023, 09:17:25 amSome fields written with tdbimage also have a small record with info before the image. The small record is the Extension, have a look to this post to disable it. Friendly, J.P ---------------------------------------------------------------------- Nick00 · 2023-05-04 02:36 Saving the stream to a file works. Before saving to a file, I specify a zero position and before transferring the stream to the TSreamAdapter, the position is zero. Initially, the project was written in Delphi 7 and my version with TSreamAdapter works there without any problems. On Lazarus (32-bit application) I get an exception. The images are stored in the database and they were loaded there a long time ago through the IBExpert utility. I wonder why the identical code for creating TGPImage from a blob works in Delphi, but does not work in Lazarus with the same GDI+ library. ---------------------------------------------------------------------- loaded · 2023-05-04 05:14 Quote from: Nick00 on May 04, 2023, 04:36:13 amSaving the stream to a file works. Before saving to a file, I specify a zero position and before transferring the stream to the TSreamAdapter, the position is zero. Initially, the project was written in Delphi 7 and my version with TSreamAdapter works there without any problems. On Lazarus (32-bit application) I get an exception. The images are stored in the database and they were loaded there a long time ago through the IBExpert utility. I wonder why the identical code for creating TGPImage from a blob works in Delphi, but does not work in Lazarus with the same GDI+ library. Because of your insistence on the use of TStreamAdapter, I immediately gave it a try. I didn't encounter any problems either. The result is very successful  Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i uses GdiPlus,Activex,Comobj; procedure TForm1.Button1Click(Sender: TObject);var    img:IGPImage;    g: IGPGraphics;    Ms:TMemoryStream;    Stream : IStream;    Adapter:TStreamAdapter;begin  Ms:=TMemoryStream.Create;  Ms.LoadFromFile('C:\Users\mali.aydin\Desktop\Emf\emf.emf');  Ms.Position:=0;   Adapter:=TStreamAdapter.Create(ms);  Stream := Adapter as IStream;   img := TGPImage.Create(stream);  g:=TGPGraphics.Create(Image1.Canvas.Handle);  g.SetPageUnit (UnitPixel);  g.DrawImage(img,0,0,Image1.Width,Image1.Height);end; ---------------------------------------------------------------------- Jurassic Pork · 2023-05-04 05:50 Hello, Quote from: Nick00 on May 04, 2023, 04:36:13 amI wonder why the identical code for creating TGPImage from a blob works in Delphi, but does not work in Lazarus with the same GDI+ library. What is your database ? are you using the same database for delphi and lazarus ? Can you compare what you read from blob database in delphi and lazarus (bytes) ? Friendly, J.P ---------------------------------------------------------------------- Nick00 · 2023-05-04 06:51 Quote from: loaded on May 04, 2023, 07:14:49 amQuote from: Nick00 on May 04, 2023, 04:36:13 amSaving the stream to a file works. Before saving to a file, I specify a zero position and before transferring the stream to the TSreamAdapter, the position is zero. Initially, the project was written in Delphi 7 and my version with TSreamAdapter works there without any problems. On Lazarus (32-bit application) I get an exception. The images are stored in the database and they were loaded there a long time ago through the IBExpert utility. I wonder why the identical code for creating TGPImage from a blob works in Delphi, but does not work in Lazarus with the same GDI+ library. Because of your insistence on the use of TStreamAdapter, I immediately gave it a try. I didn't encounter any problems either. The result is very successful  Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i uses GdiPlus,Activex,Comobj; procedure TForm1.Button1Click(Sender: TObject);var    img:IGPImage;    g: IGPGraphics;    Ms:TMemoryStream;    Stream : IStream;    Adapter:TStreamAdapter;begin  Ms:=TMemoryStream.Create;  Ms.LoadFromFile('C:\Users\mali.aydin\Desktop\Emf\emf.emf');  Ms.Position:=0;   Adapter:=TStreamAdapter.Create(ms);  Stream := Adapter as IStream;   img := TGPImage.Create(stream);  g:=TGPGraphics.Create(Image1.Canvas.Handle);  g.SetPageUnit (UnitPixel);  g.DrawImage(img,0,0,Image1.Width,Image1.Height);end; Your code doesn't work with my image. I thought that the problem is in my image, but the same code works without problems in Delphi 7 with my image ---------------------------------------------------------------------- loaded · 2023-05-04 06:53 Can you upload a sample image? ---------------------------------------------------------------------- Nick00 · 2023-05-04 06:56 Change the extension to emf. ---------------------------------------------------------------------- loaded · 2023-05-04 10:38 When I save the stream objects it creates in its two methods in a file, they both have the same content. When I reset the starting positions, the result is still the same. I think it's an interesting situation. There is probably information that the stream object created by the Adapter class has or does not have, other than the context data. In the future, I will investigate this issue in depth. I have different trials in mind, but that's all I've got at the moment. I would be glad if experts on the subject give their opinion. ---------------------------------------------------------------------- marcov · 2023-05-04 10:56 I also want to have a look in time. Can sb create a bugreport with image, code and link to gdiplus units? ---------------------------------------------------------------------- loaded · 2023-05-05 06:50 I don't know why, but some issues are stuck in my head. This is one of them, the opinion that I have reached from the research I have done; Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.ButtonAPClick(Sender: TObject);var    img:IGPImage;    g: IGPGraphics;    Ms:TMemoryStream;    Adapter:TStreamAdapter;    EmfStream :IStream;    test:Qword;begin  Ms:=TMemoryStream.Create;  Ms.LoadFromFile(efp);  Ms.Position:=0;   Adapter:=TStreamAdapter.Create(ms);   EmfStream:=Adapter as IStream; // Not the adapter class, the conversion metedo here is doing the wrong thing.   img := TGPImage.Create(EmfStream);  g:=TGPGraphics.Create(Image1.Canvas.Handle);  g.SetPageUnit (UnitPixel);  g.DrawImage(img,0,0,Image1.Width,Image1.Height);end;  If we make the above "EmfStream:=Adapter as IStream;" method ourselves. We can see that the problem is fixed. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.ButtonAPClick(Sender: TObject);    function AdapterStreamtoIStream(const AStream: TStream): IStream;    var      Output: IStream;      MemoryPointer: Pointer;      test:Qword;      begin        try          GetMem(MemoryPointer, AStream.Size);        try            AStream.ReadBuffer(MemoryPointer^, AStream.Size);        except            FreeMem(MemoryPointer);            raise;        end;        finally        end;         CreateStreamOnHGlobal(0, True, Output);        try          Output.Write(MemoryPointer, AStream.Size, nil);          Output.Seek(0, STREAM_SEEK_SET, test);        Result := Output;        except          Output := nil;        raise;        end;    end;var    img:IGPImage;    g: IGPGraphics;    Ms:TMemoryStream;    Adapter:TStreamAdapter;    EmfStream :IStream;  test:Qword;begin  Ms:=TMemoryStream.Create;  Ms.LoadFromFile(efp);  Ms.Position:=0;   Adapter:=TStreamAdapter.Create(ms);   EmfStream:=AdapterStreamtoIStream(Adapter.Stream);  //This code gives the impression that the adapter class is working correctly.   img := TGPImage.Create(EmfStream);  g:=TGPGraphics.Create(Image1.Canvas.Handle);  g.SetPageUnit (UnitPixel);  g.DrawImage(img,0,0,Image1.Width,Image1.Height);end;  This is my last post on the subject.