Link to home
Start Free TrialLog in
Avatar of Stuart_Johnson
Stuart_Johnson

asked on

Delphi DLLs

Ok.  Here is a good one.  We just D4 C/S.  Ran the upgrade patch (to SR 1). No worries.  Works well.  However.....

We have a DLL writen in D3 (pro).  We need this DLL to work with a program we are writing.  This DLL can not be used at all.  For some strange reason, D4 craps itself and comes up with an "Access violation error at address....".  Now, we know this DLL works perfectly.  If I recompile the program in D3, the DLL is called and the images are loaded from the library as normal.

Can anyone tell me a) Why it is doing this and b) How the hell do I fix it???

Thanks for any info.

Stuart
Avatar of Madshi
Madshi

All I can say: Reduce your DLL, until it can be build with D4. Then add the reduced parts again step by step and look which part is "guilty". Perhaps this way you'll find a workaround.
Probably takes a lot of time...     :-(

Madshi.
Avatar of Stuart_Johnson

ASKER

Hi Madshi,

We have already thought of that, but we would really prefer to leave it in D3 if possible.  Its just that to upgrade some of the components so they are D4 compatible is going to set us back money we really dont want to part with unless it is absolutely required.

Thanks for the advice though.

Stuart.
Once I have similar problem and that's was because I forgot write directive stdcall. Error was the same 'Access violation...'

AP
333 - Simple name, Half of the Beast is it :)

I have tried at as well.  Its no real concern now, just something that I would like to solve eventually.

Stu.
Yo,
does your DLL/EXE pass over object pointers ? D3 and D4 objects have an incompatible VMT, so this could be a reason.

Slash/d003303
Hi Slash,

One of the parameters I pass is a var TImage.  Bummer.  Anyway around this at all?  The DLL does a whole stack of work on an image.  It is essential that this Image is passed and returned.

I hate the way software companies cant make backward compatible software.  It really puts the wind up me.

Stu.
ASKER CERTIFIED SOLUTION
Avatar of d003303
d003303

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Hi Slash,

That sounds like a better idea.  We have already tried passing the bitmaps between the exe and the DLL and that didnt work either.

Can you give me some idea on how I would go about that?  I havent started "playing" with COM before :)

Thanks mate.

Stuart.
By the way, the internal implementation of objects has changed in the release cycles of *every* object oriented compiler I know. The only way around that is COM.
E.g. the Steinberg Cubase VST SDK for creating plugins uses a VC++ object as an interface. This only works with MS VC++ 5.0, no other version of C++.

Slash/d003303
Huh, posted at the same time :)
I try something out, the COM idea and something else that I don't know if it works. Hang on.

Slash/d003303
Haha :)  What a coincidence!!!!  Wouldnt see that often.

If you wouldnt mind sending me an example, I would appreciate it!

Stu.
Some quick questions to reduce the overhead.
- You pass a TImage in the var parameter. Does the DLL mainly modify the image ?
- Does it only accept bitmaps or also icons/metafiles ?
Ok, here is a bit of a better explaination of what it does.  We have an Image on a form.  When the form is shown, we call the DLL with the following proceedure:

  ExtractImage(var Image: TImage; Index, BitDepth: Integer);

We assume that the image is already there.  If any errors occur within the DLL,  we set the Image.Picture.Bitmap to NIL.

The Image is extracted from an Image Library (Made by a program called FastGraph).  The index is the name of the file, and the bit depth is, well, thats obvious. Basically, we make the filename up by :

FileName = String(Index)+String(BitDepth) (loosely)

Is that a good enough explaination.  I could send you some code if that would help, but what I have said is basically what we are trying to do.

Im almost off home (7pm here), so I wont get any more replies from you until tomorrow morning.  If you can help out, I would really appreciate it.

Have a great day(evening?)

Stuart.
OK,
here's a generic solution. The clue is to introduce a COM interfaced stream class as a transport medium. With that, you are able to transfer any data you like, not only bitmaps.
first unit is the new stream class, the others are a sample DLL and a sample EXE to ckeck it out.

// BOC
unit IDelphiStream;

(*******************************************************************************

             Delphi Stream implementation through a COM interface
              useful to transfer stream contents between modules
                    written in different Delphi versions

*******************************************************************************)

interface

uses Classes;

type

  IDelphiStreamInterface = interface(IUnknown)
    ['{FE01F910-3810-11d2-B4FD-0000E82D8A65}']
    function GetPosition: Longint; stdcall;
    procedure SetPosition(Pos: Longint); stdcall;
    function GetSize: Longint; stdcall;
    procedure SetSize(NewSize: Longint); stdcall;
    function Read(var Buffer; Count: Longint): Longint; stdcall;
    function Write(const Buffer; Count: Longint): Longint; stdcall;
    function Seek(Offset: Longint; Origin: Word): Longint; stdcall;
    procedure ReadBuffer(var Buffer; Count: Longint); stdcall;
    procedure WriteBuffer(const Buffer; Count: Longint); stdcall;
    function CopyFrom(Source: IDelphiStreamInterface; Count: Longint): Longint; stdcall;
    property Position: Longint read GetPosition write SetPosition;
    property Size: Longint read GetSize write SetSize;
  end;

  TStreamInterface = class(TInterfacedObject, IDelphiStreamInterface)
  private
    FStreamBase : TStream;
  public
    constructor Create(StreamBase: TStream);
    destructor Destroy; override;
{    // IUnknown implementation, move to public visibility
    function QueryInterface(const IID: TGUID; out Obj): Integer; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;}
    // IDelphiStreamInterface implementation
    function GetPosition: Longint; stdcall;
    procedure SetPosition(Pos: Longint); stdcall;
    function GetSize: Longint; stdcall;
    procedure SetSize(NewSize: Longint); stdcall;
    function Read(var Buffer; Count: Longint): Longint; stdcall;
    function Write(const Buffer; Count: Longint): Longint; stdcall;
    function Seek(Offset: Longint; Origin: Word): Longint; stdcall;
    procedure ReadBuffer(var Buffer; Count: Longint); stdcall;
    procedure WriteBuffer(const Buffer; Count: Longint); stdcall;
    function CopyFrom(Source: IDelphiStreamInterface; Count: Longint): Longint; stdcall;
  end;

implementation

constructor TStreamInterface.Create(StreamBase: TStream);
begin
  inherited Create;
  FStreamBase := StreamBase;
end;

destructor TStreamInterface.Destroy;
begin
  FStreamBase.Free;
  inherited Destroy;
end;

function TStreamInterface.GetPosition: Longint;
begin
  Result := FStreamBase.Position;
end;

procedure TStreamInterface.SetPosition(Pos: Longint);
begin
  FStreamBase.Position := Pos;
end;

function TStreamInterface.GetSize: Longint;
begin
  Result := FStreamBase.Size;
end;

procedure TStreamInterface.SetSize(NewSize: Longint);
begin
  FStreamBase.Size := NewSize;
end;

function TStreamInterface.Read(var Buffer; Count: Longint): Longint;
begin
  Result := FStreamBase.Read(Buffer, Count);
end;

function TStreamInterface.Write(const Buffer; Count: Longint): Longint;
begin
  Result := FStreamBase.Write(Buffer, Count);
end;

function TStreamInterface.Seek(Offset: Longint; Origin: Word): Longint;
begin
  Result := FStreamBase.Seek(Offset, Origin);
end;

procedure TStreamInterface.ReadBuffer(var Buffer; Count: Longint);
begin
  FStreamBase.ReadBuffer(Buffer, Count);
end;

procedure TStreamInterface.WriteBuffer(const Buffer; Count: Longint);
begin
  FStreamBase.WriteBuffer(Buffer, Count);
end;

function TStreamInterface.CopyFrom(Source: IDelphiStreamInterface; Count: Longint): Longint;
const
  MaxBufSize = $F000;
var
  BufSize, N: Integer;
  Buffer: PChar;
begin
  if Count = 0 then
  begin
    Source.Position := 0;
    Count := Source.Size;
  end;
  Result := Count;
  if Count > MaxBufSize then BufSize := MaxBufSize else BufSize := Count;
  GetMem(Buffer, BufSize);
  try
    while Count <> 0 do
    begin
      if Count > BufSize then N := BufSize else N := Count;
      Source.ReadBuffer(Buffer^, N);
      FStreamBase.WriteBuffer(Buffer^, N);
      Dec(Count, N);
    end;
  finally
    FreeMem(Buffer, BufSize);
  end;
end;

end.

// DLL project file
library thedll;

uses
  SysUtils,
  Classes,
  ExtCtrls,
  IDelphiStream in 'idelphistream.pas';

procedure ExtractImage2(var Image: TImage; Index, BitDepth: Integer);
begin
  // just used a static filename here
  Image.Picture.LoadFromFile('c:\temp\img' + IntToStr(Index) + '.bmp');
end;

procedure ExtractImage(Stream: IDelphiStreamInterface; Index, BitDepth: Integer);
var Image        : TImage;
    StreamBuffer : TMemoryStream;
    StreamWrapper: IDelphiStreamInterface;
begin
  Image := TImage.Create(nil);
  StreamBuffer := TMemoryStream.Create;
  StreamWrapper := TStreamInterface.Create(StreamBuffer);
  StreamWrapper._AddRef;
  Stream._AddRef;
  try
    ExtractImage2(Image, Index, BitDepth);
    if Image.Picture.Bitmap <> nil then
     begin
       Image.Picture.Bitmap.SaveToStream(StreamBuffer);
       StreamWrapper.Seek(0, soFromBeginning);
       Stream.CopyFrom(StreamWrapper, StreamWrapper.Size);
     end;
  finally
    Image.Free;
    // this also frees StreamBuffer
    Stream._Release;
    StreamWrapper._Release;
  end;
end;

exports
  ExtractImage index 1;

begin
end.

// EXE project file
program app;

uses
  Forms,
  _main in '_main.pas' {Form1},
  idelphistream in 'idelphistream.pas';

{$R *.RES}

begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

// form code
unit _main;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, StdCtrls, Spin, IDelphiStream;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Button1: TButton;
    SpinEdit1: TSpinEdit;
    Label1: TLabel;
    Image1: TImage;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

procedure ExtractImage(Stream: IDelphiStreamInterface; Index, BitDepth: Integer);

implementation

{$R *.DFM}

procedure ExtractImage; external 'thedll.dll' index 1;

procedure ExtractImage2(var Image: TImage; Index, BitDepth: Integer);
var StreamBuffer : TMemoryStream;
    StreamWrapper: IDelphiStreamInterface;
begin
  StreamBuffer := TMemoryStream.Create;
  StreamWrapper := TStreamInterface.Create(StreamBuffer);
  StreamWrapper._AddRef;
  try
    ExtractImage(StreamWrapper, Index, BitDepth);
    StreamWrapper.Seek(0, soFromBeginning);
    Image.Picture.Bitmap.LoadFromStream(StreamBuffer);
  finally
    // this also frees StreamBuffer
    StreamWrapper._Release;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ExtractImage2(Image1, SpinEdit1.Value, 0);
end;

end.

// form object
object Form1: TForm1
  Left = 200
  Top = 108
  Width = 783
  Height = 551
  Caption = 'Form1'
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  PixelsPerInch = 96
  TextHeight = 13
  object Image1: TImage
    Left = 0
    Top = 41
    Width = 775
    Height = 483
    Align = alClient
  end
  object Panel1: TPanel
    Left = 0
    Top = 0
    Width = 775
    Height = 41
    Align = alTop
    TabOrder = 0
    object Label1: TLabel
      Left = 8
      Top = 12
      Width = 72
      Height = 13
      Caption = 'Image Number:'
    end
    object Button1: TButton
      Left = 128
      Top = 6
      Width = 75
      Height = 25
      Caption = '&Load'
      TabOrder = 0
      OnClick = Button1Click
    end
    object SpinEdit1: TSpinEdit
      Left = 84
      Top = 8
      Width = 41
      Height = 22
      MaxValue = 4
      MinValue = 1
      TabOrder = 1
      Value = 1
    end
  end
end

// EOC

Questions->ask.

Good luck,
Slash/d003303
GOD!!!!  So much code for such a simple task!!!  It used to be SOOO easy!  

Alrighty then, I'll give this a shot and see what happens.  We have moved the code from the DLL into the main program which solved the problem, but I would like to get this going again from the DLL.

Thanks for you help, I'll get back to you today with a grade.

Stuart.
Stuart,
it is so much code because of the sample app. You just need the stream unit and proxy/stub code in your calls. Not that much, and you can compile it with every version of Delphi > 3.

Slash/d003303