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
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
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.
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
AP
ASKER
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.
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
does your DLL/EXE pass over object pointers ? D3 and D4 objects have an incompatible VMT, so this could be a reason.
Slash/d003303
ASKER
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.
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
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
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.
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
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
I try something out, the COM idea and something else that I don't know if it works. Hang on.
Slash/d003303
ASKER
Haha :) What a coincidence!!!! Wouldnt see that often.
If you wouldnt mind sending me an example, I would appreciate it!
Stu.
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 ?
- You pass a TImage in the var parameter. Does the DLL mainly modify the image ?
- Does it only accept bitmaps or also icons/metafiles ?
ASKER
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(BitDe pth) (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.
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(BitDe
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 -0000E82D8 A65}']
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(St reamBase: TStream);
begin
inherited Create;
FStreamBase := StreamBase;
end;
destructor TStreamInterface.Destroy;
begin
FStreamBase.Free;
inherited Destroy;
end;
function TStreamInterface.GetPositi on: Longint;
begin
Result := FStreamBase.Position;
end;
procedure TStreamInterface.SetPositi on(Pos: Longint);
begin
FStreamBase.Position := Pos;
end;
function TStreamInterface.GetSize: Longint;
begin
Result := FStreamBase.Size;
end;
procedure TStreamInterface.SetSize(N ewSize: Longint);
begin
FStreamBase.Size := NewSize;
end;
function TStreamInterface.Read(var Buffer; Count: Longint): Longint;
begin
Result := FStreamBase.Read(Buffer, Count);
end;
function TStreamInterface.Write(con st Buffer; Count: Longint): Longint;
begin
Result := FStreamBase.Write(Buffer, Count);
end;
function TStreamInterface.Seek(Offs et: Longint; Origin: Word): Longint;
begin
Result := FStreamBase.Seek(Offset, Origin);
end;
procedure TStreamInterface.ReadBuffe r(var Buffer; Count: Longint);
begin
FStreamBase.ReadBuffer(Buf fer, Count);
end;
procedure TStreamInterface.WriteBuff er(const Buffer; Count: Longint);
begin
FStreamBase.WriteBuffer(Bu ffer, 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(Bu ffer^, 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(St reamBuffer );
StreamWrapper._AddRef;
Stream._AddRef;
try
ExtractImage2(Image, Index, BitDepth);
if Image.Picture.Bitmap <> nil then
begin
Image.Picture.Bitmap.SaveT oStream(St reamBuffer );
StreamWrapper.Seek(0, soFromBeginning);
Stream.CopyFrom(StreamWrap per, 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(TFo rm1, 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(St reamBuffer );
StreamWrapper._AddRef;
try
ExtractImage(StreamWrapper , Index, BitDepth);
StreamWrapper.Seek(0, soFromBeginning);
Image.Picture.Bitmap.LoadF romStream( StreamBuff er);
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
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
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(St
begin
inherited Create;
FStreamBase := StreamBase;
end;
destructor TStreamInterface.Destroy;
begin
FStreamBase.Free;
inherited Destroy;
end;
function TStreamInterface.GetPositi
begin
Result := FStreamBase.Position;
end;
procedure TStreamInterface.SetPositi
begin
FStreamBase.Position := Pos;
end;
function TStreamInterface.GetSize: Longint;
begin
Result := FStreamBase.Size;
end;
procedure TStreamInterface.SetSize(N
begin
FStreamBase.Size := NewSize;
end;
function TStreamInterface.Read(var Buffer; Count: Longint): Longint;
begin
Result := FStreamBase.Read(Buffer, Count);
end;
function TStreamInterface.Write(con
begin
Result := FStreamBase.Write(Buffer, Count);
end;
function TStreamInterface.Seek(Offs
begin
Result := FStreamBase.Seek(Offset, Origin);
end;
procedure TStreamInterface.ReadBuffe
begin
FStreamBase.ReadBuffer(Buf
end;
procedure TStreamInterface.WriteBuff
begin
FStreamBase.WriteBuffer(Bu
end;
function TStreamInterface.CopyFrom(
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^,
FStreamBase.WriteBuffer(Bu
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
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(St
StreamWrapper._AddRef;
Stream._AddRef;
try
ExtractImage2(Image, Index, BitDepth);
if Image.Picture.Bitmap <> nil then
begin
Image.Picture.Bitmap.SaveT
StreamWrapper.Seek(0, soFromBeginning);
Stream.CopyFrom(StreamWrap
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(TFo
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(St
StreamWrapper._AddRef;
try
ExtractImage(StreamWrapper
StreamWrapper.Seek(0, soFromBeginning);
Image.Picture.Bitmap.LoadF
finally
// this also frees StreamBuffer
StreamWrapper._Release;
end;
end;
procedure TForm1.Button1Click(Sender
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
ASKER
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.
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
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
Probably takes a lot of time... :-(
Madshi.