Solved

Delphi DLLs

Posted on 1998-08-18
16
414 Views
Last Modified: 2010-08-05
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
0
Comment
Question by:Stuart_Johnson
16 Comments
 
LVL 20

Expert Comment

by:Madshi
ID: 1337225
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.
0
 
LVL 6

Author Comment

by:Stuart_Johnson
ID: 1337226
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.
0
 
LVL 2

Expert Comment

by:333
ID: 1337227
Once I have similar problem and that's was because I forgot write directive stdcall. Error was the same 'Access violation...'

AP
0
Courses: Start Training Online With Pros, Today

Brush up on the basics or master the advanced techniques required to earn essential industry certifications, with Courses. Enroll in a course and start learning today. Training topics range from Android App Dev to the Xen Virtualization Platform.

 
LVL 6

Author Comment

by:Stuart_Johnson
ID: 1337228
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.
0
 
LVL 4

Expert Comment

by:d003303
ID: 1337229
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
0
 
LVL 6

Author Comment

by:Stuart_Johnson
ID: 1337230
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.
0
 
LVL 4

Accepted Solution

by:
d003303 earned 200 total points
ID: 1337231
Yo,
two possibilities:
you will have to create seperate instances of TImage on both sides, EXE and DLL, and only pass the bitmap handle as a parameter.
The second one is to introduce a COM interface to the TImage in the DLL. COM interfaces are always binary compatible. Implement a TImage derivant in your EXE that supports this interface and pass it to the DLL. The only method that will NOT work then is Assign.

Hope this helps,
Slash/d003303
0
 
LVL 6

Author Comment

by:Stuart_Johnson
ID: 1337232
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.
0
 
LVL 4

Expert Comment

by:d003303
ID: 1337233
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
0
 
LVL 4

Expert Comment

by:d003303
ID: 1337234
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
0
 
LVL 6

Author Comment

by:Stuart_Johnson
ID: 1337235
Haha :)  What a coincidence!!!!  Wouldnt see that often.

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

Stu.
0
 
LVL 4

Expert Comment

by:d003303
ID: 1337236
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 ?
0
 
LVL 6

Author Comment

by:Stuart_Johnson
ID: 1337237
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.
0
 
LVL 4

Expert Comment

by:d003303
ID: 1337238
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
0
 
LVL 6

Author Comment

by:Stuart_Johnson
ID: 1337239
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.
0
 
LVL 4

Expert Comment

by:d003303
ID: 1337240
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
0

Featured Post

Live: Real-Time Solutions, Start Here

Receive instant 1:1 support from technology experts, using our real-time conversation and whiteboard interface. Your first 5 minutes are always free.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Introduction The parallel port is a very commonly known port, it was widely used to connect a printer to the PC, if you look at the back of your computer, for those who don't have newer computers, there will be a port with 25 pins and a small print…
Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have to…
This tutorial gives a high-level tour of the interface of Marketo (a marketing automation tool to help businesses track and engage prospective customers and drive them to purchase). You will see the main areas including Marketing Activities, Design …
With Secure Portal Encryption, the recipient is sent a link to their email address directing them to the email laundry delivery page. From there, the recipient will be required to enter a user name and password to enter the page. Once the recipient …

786 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question