Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

How to extract exported function names from .DLL

2,863 views
Skip to first unread message

Loren Scott [Vista Software]

unread,
Jan 6, 2000, 3:00:00 AM1/6/00
to
Hi Gang,

I know how to check if an exported function (by name) exists in a .DLL by
using GetProcAddress, but isn't there also a way to get the list of exported
function names contained in the .DLL? Simple old DOS utilities like LIB.EXE
or LIBRA.EXE could extract this information. So, how would I do this in
Delphi?

Best regards,

Loren Scott
President/CEO
Vista Software
http://www.VistaSoftware.com

Luiz Carlos Buiatte

unread,
Jan 7, 2000, 3:00:00 AM1/7/00
to
You can use TDUMP.EXE (command prompt). Then look at the exports section.

Luiz Carlos.

buiatte.vcf

Loren Scott [Vista Software]

unread,
Jan 7, 2000, 3:00:00 AM1/7/00
to
Hi Luiz,

I know that much. I meant from Delphi code.

Peter Below

unread,
Jan 7, 2000, 3:00:00 AM1/7/00
to
> I know how to check if an exported function (by name) exists in a .DLL by
> using GetProcAddress, but isn't there also a way to get the list of exported
> function names contained in the .DLL? Simple old DOS utilities like LIB.EXE
> or LIBRA.EXE could extract this information. So, how would I do this in
> Delphi?

You have to dissect the PE file format used for Win32 executables. There is a
system DLL (imagehlp.dll) on NT (it is distributable but not part of the
default Win95 installation) that can help you somewhat here, D5 comes with an
import unit for it (imagehlp.pas). The function ImageDirectoryEntryToData can
give you a pointer to the start of the IMAGE_EXPORT_DIRECTORY section of the
file. Further decoding is left to you, however. It is not totally
straightforward since the entries are not straight pointers but RVAs (offsets
relative to the image base address), but imagehlp has functions to convert
between RVAs and pointers.

An excellent description of the PE file innards can be found in Matt Pietreks
classic "Windows 95 System programming secrets".

Peter Below (TeamB) 10011...@compuserve.com)
No e-mail responses, please, unless explicitly requested!

Sent using Virtual Access 5.00 - download your freeware copy now
http://www.atlantic-coast.com/downloads/vasetup.exe

Loren Scott [Vista Software]

unread,
Jan 7, 2000, 3:00:00 AM1/7/00
to
Hi Peter,

So, as far as you are aware, there is not a Windows API function or Delphi
call that provides direct access the function names within the .DLL?

Loren Scott [Vista Software]

unread,
Jan 7, 2000, 3:00:00 AM1/7/00
to
Hi Gang,

I'm still looking for a solution on this one, if anyone has any experience
in this area.

Ralph Friedman (TeamB)

unread,
Jan 8, 2000, 3:00:00 AM1/8/00
to
In message <856hje$sa...@bornews.borland.com>, Loren Scott [Vista Software]
stated:

> I'm still looking for a solution on this one, if anyone has any experience
> in this area.
>
Loren,

I think that the advice that Peter gave (roll your own) is the way that
you'll have to go.

===
Regards
Ralph (TeamB)
===


Peter Below

unread,
Jan 8, 2000, 3:00:00 AM1/8/00
to
In article <855npc$s2...@bornews.borland.com>, Loren Scott [Vista Software]
wrote:

> So, as far as you are aware, there is not a Windows API function or Delphi
> call that provides direct access the function names within the .DLL?
>

No, but as it happens i found a message that may help you in my archive:

<quote>
From: "Dmitry Streblechenko" <dmitrys@-NoSpam-.asu.edu>
Newsgroups: borland.public.delphi.winapi
Subject: Re: DLL accessing routines
Date: Wed, 7 Apr 1999 15:05:46 -0700

Eduardo wrote in message <01be8108$a184a9a0$ba1e28c8@eduardo>...
>How can I get a list of routines stored into a DLL library?
>

I am reposting a code snippet I posted 2 couple month ago.

Dmitry

----------------------------------------------------------------------------
----

Hey guys, it is actually much easier than it sounds; I wrote the function
below a long time ago and didn't
actually use it
extensively, so don't look too closely :) I intended to use that function
for exactly the same purpose as Frank,
worked just fine.
MapDebugInformation and UnmapDebugInformation are available for both NT and
95, and you can
redisribute imagehlp.dll with 95

---------------------------------------------------

uses Windows, Classes, ImageHlp;

....

procedure GetFunctionNamesFromDLL(DLLName:string; List:TStrings);
type chararr=array[0..$FFFFFF] of char;
var h:THandle;
i,fc:integer;
st:string;
arr:pointer;
ImageDebugInformation:PImageDebugInformation;
begin
List.Clear;
DLLName:=ExpandFileName(DLLName);
if not FileExists(DLLName) then Exit;
h:=CreateFile(PChar(DLLName), GENERIC_READ,
FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, 0);
if h=-1 then Exit;
ImageDebugInformation:=MapDebugInformation(h, PChar(DLLName), nil,0);
if ImageDebugInformation=nil then Exit;
arr:=ImageDebugInformation^.ExportedNames;
fc:=0;
for i:=0 to ImageDebugInformation^.ExportedNamesSize-1 do begin
if chararr(arr^)[i]=#0 then begin
st:=PChar(@chararr(arr^)[fc]);
if length(st)>0 then List.Add(st);
if (i>0) and (chararr(arr^)[i-1]=#0) then Break;
fc:=i+1;
end;
end;
UnmapDebugInformation(ImageDebugInformation);
CloseHandle(h);
end;


</quote>

Loren Scott [Vista Software]

unread,
Jan 8, 2000, 3:00:00 AM1/8/00
to
YEAH!! That GetFunctionNamesFromDLL function Dmitry wrote looks like
exactly what I was hoping for. Thanks much, Peter! I appreciate the
detective work very much.

Loren Scott [Vista Software]

unread,
Jan 8, 2000, 3:00:00 AM1/8/00
to
Groan... I just tried it. It compiles fine and opens my .DLL, but the
ImageDebugInformation^.ExportedNames return is nil on all .DLLs I try it
with.

I am currently testing this on a PC with Windows 2000, so I wonder if that
has anything to do with it.

I just sent Dmitry an e-mail to see if he might have any ideas. I think
this is ultimately going to do the trick for me, though. Thanks much for
your assistance.

Manuel Algora

unread,
Jan 8, 2000, 3:00:00 AM1/8/00
to
On Thu, 6 Jan 2000 23:10:20 -0800, "Loren Scott [Vista Software]"
<lor...@vistasoftware.com> wrote:

>I know how to check if an exported function (by name) exists in a .DLL by
>using GetProcAddress, but isn't there also a way to get the list of exported
>function names contained in the .DLL?

There's a command line tool by Borland, IMPDEF.EXE, which does just
this. It comes both with TASM and Borland C++, but I can't see it
among the binaries of Delphi 4.

It creates a definition file from a DLL. The definition file is just a
text file containing the exported identifiers, along with their
ordinal number.

Manuel Algora
m...@encomix.es

philippe_ranger

unread,
Jan 8, 2000, 3:00:00 AM1/8/00
to
<<Loren:

I just sent Dmitry an e-mail to see if he might have any ideas. I think
this is ultimately going to do the trick for me, though. Thanks much for
your assistance.
>>

Please keep us posted.

PhR

Loren Scott [Vista Software]

unread,
Jan 8, 2000, 3:00:00 AM1/8/00
to
Hi Philippe,

>> Please keep us posted. <<

Well, I haven't heard back from Dmitry yet, but I did try this same code on
a Win95 PC with the same non-results. So, hopefully Dmitry (or someone
else) will have some idea what might be going wrong.

Peter Below

unread,
Jan 9, 2000, 3:00:00 AM1/9/00
to
In article <8590h2$7m...@bornews.borland.com>, Loren Scott [Vista Software]
wrote:

> Well, I haven't heard back from Dmitry yet, but I did try this same code on
> a Win95 PC with the same non-results. So, hopefully Dmitry (or someone
> else) will have some idea what might be going wrong.

I think the problem is that Dmitrys code relies on debug info (in MS format)
that is simply not present in most executables. The code does not try to read
the exported names table directly. Ok, since it's weekend and i don't have
anything better to do i take a stab at the problem.

<Several cups of tea later...>

unit DLLTools;

interface

Uses Windows, Classes, Sysutils, imagehlp ;

type
TDLLExportCallback = function (const name: String; ordinal: Integer;
address: Pointer): Boolean of Object;
{ Note: address is a RVA here, not a usable virtual address! }
DLLToolsError = Class( Exception );

Procedure ListDLLExports( const filename: String; callback:
TDLLExportCallback );
Procedure DumpExportDirectory( Const ExportDirectory: TImageExportDirectory;
lines: TStrings; const Image: LoadedImage );
Function RVAToPchar( rva: DWORD; const Image: LoadedImage ): PChar;
Function RVAToPointer( rva: DWORD; const Image: LoadedImage ): Pointer;

implementation

resourcestring
eDLLNotFound =
'ListDLLExports: DLL %s does not exist!';

{+----------------------------------------------------------------------
| Procedure EnumExports
|
| Parameters :
| ExportDirectory: IMAGE_EXPORT_DIRECTORY record to enumerate
| image : LOADED_IMAGE record for the DLL the export directory belongs
| to.
| callback : callback function to hand the found exports to, must not be
Nil
| Description:
| The export directory of a PE image contains three RVAs that point at
tables
| which describe the exported functions. The first is an array of RVAs
that
| refer to the exported function names, these we translate to PChars to
| get the exported name. The second array is an array of Word that
contains
| the export ordinal for the matching entry in the names array. The
ordinal
| is biased, that is we have to add the ExportDirectory.Base value to it
to
| get the actual export ordinal. The biased ordinal serves as index for
the
| third array, which is an array of RVAs that give the position of the
| function code in the image. We don't translate these RVAs since the DLL
| is not relocated since we load it via MapAndLoad. The function array is
| usually much larger than the names array, since the ordinals for the
| exported functions do not have to be in sequence, there can be (and
| frequently are) gaps in the sequence, for which the matching entries in
the
| function RVA array are garbage.
| Error Conditions: none
| Created: 9.1.2000 by P. Below
+----------------------------------------------------------------------}
Procedure EnumExports( const ExportDirectory : TImageExportDirectory ;
const image : LoadedImage ;
callback : TDLLExportCallback ) ;
Type
TDWordArray = Array [0..$FFFFF] of DWORD;
Var
i: Cardinal;
pNameRVAs, pFunctionRVas: ^TDWordArray;
pOrdinals: ^TWordArray;
name: String;
address: Pointer;
ordinal: Word;
Begin { EnumExports }
pNameRVAs :=
RVAToPointer( DWORD(ExportDirectory.AddressOfNames), image );
pFunctionRVAs :=
RVAToPointer( DWORD(ExportDirectory.AddressOfFunctions), image );
pOrdinals :=
RVAToPointer( DWORD(ExportDirectory.AddressOfNameOrdinals), image );
For i:= 0 to Pred( ExportDirectory.NumberOfNames ) Do Begin
name := RVAToPChar( pNameRVAs^[i], image );
ordinal := pOrdinals^[i];
address := Pointer( pFunctionRVAs^[ ordinal ] );
If not callback( name, ordinal+ExportDirectory.Base, address ) Then
Exit;
End; { For }
End; { EnumExports }

{+----------------------------------------------------------------------
| Procedure ListDLLExports
|
| Parameters :
| filename : full pathname of DLL to examine
| callback : callback to hand the found exports to, must not be Nil
| Description:
| Loads the passed DLL using the LoadImage function, finds the exported
| names table and reads it. Each found entry is handed to the callback
| for further processing, until no more entries remain or the callback
| returns false. Note that the address passed to the callback for a
exported
| function is an RVA, so not identical to the address the function would
| have in a properly loaded and relocated DLL!
| Error Conditions:
| Exceptions are raised if
| - the passed DLL does not exist or could not be loaded
| - no callback was passed (only if assertions are on)
| - an API function failed
| Created: 9.1.2000 by P. Below
+----------------------------------------------------------------------}
Procedure ListDLLExports( const filename : String ; callback :
TDLLExportCallback ) ;
Var
imageinfo: LoadedImage;
pExportDirectory: PImageExportDirectory;
dirsize: Cardinal;
Begin { ListDLLExports }
Assert( Assigned( callback ));
If not FileExists( filename ) Then
raise DLLToolsError.CreateFmt( eDLLnotFound, [filename] );

If MapAndLoad( PChar( filename ), nil, @imageinfo, true, true ) Then
try
pExportDirectory :=
ImageDirectoryEntryToData(
imageinfo.MappedAddress, false,
IMAGE_DIRECTORY_ENTRY_EXPORT, dirsize );

If pExportDirectory = Nil Then
RaiseLastWin32Error
Else
EnumExports( pExportDirectory^, imageinfo, callback );
finally
UnMapAndLoad( @imageinfo );
end
Else
RaiseLastWin32Error;
End; { ListDLLExports }

{+----------------------------------------------------------------------
| Procedure DumpExportDirectory
|
| Parameters :
| ExportDirectory: a IMAGE_EXPORT_DIRECTORY record
| lines : a TStrings descendend to put the info into, must not be Nil
| Description:
| Dumps the fields of the passed structure to the passed strings
descendent
| as strings.
| Error Conditions:
| will raise an exception if lines is Nil and assertions are enabled.
| Created: 9.1.2000 by P. Below
+----------------------------------------------------------------------}
Procedure DumpExportDirectory( Const ExportDirectory : TImageExportDirectory;
lines : TStrings; const Image: LoadedImage ) ;
Begin { DumpExportDirectory }
Assert( Assigned( lines ));

lines.add( 'Dump of IMAGE_EXPORT_DIRECTORY' );
lines.add( format('Characteristics: %d',
[ExportDirectory.Characteristics]));
lines.add( format('TimeDateStamp: %d',
[ExportDirectory.TimeDateStamp]));
lines.add( format('Version: %d.%d',
[ExportDirectory.MajorVersion,
ExportDirectory.MinorVersion]));
lines.add( format('Name (RVA): %x',
[ExportDirectory.Name]));
lines.add( format('Name (translated): %s',
[RVAToPchar( ExportDirectory.name, Image )]));
lines.add( format('Base: %d',
[ExportDirectory.Base]));
lines.add( format('NumberOfFunctions: %d',
[ExportDirectory.NumberOfFunctions]));
lines.add( format('NumberOfNames: %d',
[ExportDirectory.NumberOfNames]));
lines.add( format('AddressOfFunctions (RVA): %p',
[Pointer(ExportDirectory.AddressOfFunctions)]));
lines.add( format('AddressOfNames (RVA): %p',
[Pointer(ExportDirectory.AddressOfNames)]));
lines.add( format('AddressOfNameOrdinals (RVA): %p',
[Pointer(ExportDirectory.AddressOfNameOrdinals)]));
End; { DumpExportDirectory }

{+----------------------------------------------------------------------
| Function RVAToPointer
|
| Parameters :
| rva : a relative virtual address to translate
| Image : LOADED_IMAGE structure for the image the RVA relates to
| Returns : translated address
| Description:
| Uses the ImageRVAToVA function to translate the RVA to a virtual
| address.
| Error Conditions:
| Will raise an exception if the translation failed
| Created: 9.1.2000 by P. Below
+----------------------------------------------------------------------}
Function RVAToPointer( rva : DWORD ; const Image : LoadedImage ) : Pointer;
var
pDummy: PImageSectionHeader;
Begin { RVAToPchar }
pDummy := nil;
Result :=
ImageRvaToVa( Image.FileHeader, Image.MappedAddress, rva,
pDummy );
If Result = Nil Then
RaiseLastWin32Error;
End; { RVAToPointer }

{+----------------------------------------------------------------------
| Function RVAToPchar
|
| Parameters :
| rva : a relative virtual address to translate
| Image : LOADED_IMAGE structure for the image the RVA relates to
| Returns : translated address
| Description:
| Uses the RVAToPointer function to translate the RVA to a virtual
| address. Note that we do not check that the address does indeed point
| to a zero-terminated string!
| Error Conditions:
| Will raise an exception if the translation failed
| Created: 9.1.2000 by P. Below
+----------------------------------------------------------------------}
Function RVAToPchar( rva : DWORD ; const Image : LoadedImage ) : PChar ;
Begin { RVAToPchar }
Result := RVAToPointer( rva, image );
End; { RVAToPchar }

end {DLLTools}.

The ListDLLExports function is used like this:

Drop a TListview and a button on a new form, name the listview 'Listview',
set its viewstyle to vsReport, add three columns to it. Drop a TOpenDialog on
the form, name it 'OpenDialog', set its Filter property to show DLLs, its
InitialDir to your Windows\system directory. Add a OnClick handler for the
button, modify unit as follows, compile and run, click on button, select a
DLL.

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ComCtrls;

type
TDLLExportCallback = function (const name: String; ordinal: Integer;
address: Pointer): Boolean of Object;
TForm1 = class(TForm)
ListView: TListView;
Button1: TButton;
OpenDialog: TOpenDialog;
procedure Button1Click(Sender: TObject);
private
function ListExport(const name: String; ordinal: Integer;
address: Pointer): Boolean;
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

uses DLLTools;
{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
begin
If opendialog.execute then begin
listview.items.clear;
ListDLLExports( opendialog.filename, listexport );
end;
end;

function tform1.ListExport( const name: String; ordinal: Integer; address:
Pointer ): Boolean;
var
listentry: TLIstItem;
begin
Result := true;
listentry:= listview.Items.Add;
listentry.Caption := Format('%p',[address] );
listentry.Subitems.Add( format('%d',[ordinal] ));
listentry.Subitems.Add( name );
end;

end.

Loren Scott [Vista Software]

unread,
Jan 9, 2000, 3:00:00 AM1/9/00
to
Hi Peter,

WOW!! Nice cups of tea you have there! <g> Thanks much for the huge
effort. I'll try it out right now!

Best regards,

Loren Scott
President/CEO
Vista Software
http://www.VistaSoftware.com

"Peter Below (TeamB)" <10011...@compuXXserve.com> wrote in message
news:VA.0000458...@antispam.compuserve.com...

Loren Scott [Vista Software]

unread,
Jan 9, 2000, 3:00:00 AM1/9/00
to
Hi Rudy,

Oops! Sorry about that. I usually remove all quotes from my ng replies. I
guess in my excitement I forgot. Sorry about that.

Loren Scott [Vista Software]

unread,
Jan 9, 2000, 3:00:00 AM1/9/00
to
Hi Peter,

You Da Man!!!! It works beautifully, even on my Windows 2000 system.
Outstanding work! If you will be at the next BorCon (in San Diego), the
drinks are on me.

Thanks again,

Rudy Velthuis

unread,
Jan 9, 2000, 3:00:00 AM1/9/00
to
Loren Scott [Vista Software] wrote...

>Hi Peter,
>
>WOW!! Nice cups of tea you have there! <g> Thanks much for the huge
>effort. I'll try it out right now!

Certainly. But please, please, next time don't quote 315 lines to add 3
of your own.

Thanks.
--
Rudy Velthuis

Rudy Velthuis

unread,
Jan 9, 2000, 3:00:00 AM1/9/00
to
Loren Scott [Vista Software] wrote...
>Hi Rudy,
>
>Oops! Sorry about that. I usually remove all quotes from my ng replies. I
>guess in my excitement I forgot. Sorry about that.

I thought so. The danger of auto-quoting newsreaders. Be glad Philippe
didn't spot it yet <g,d&r>.
--
Rudy Velthuis

philippe_ranger

unread,
Jan 9, 2000, 3:00:00 AM1/9/00
to
<<Rudy:

. Be glad Philippe didn't spot it yet
>>

??? I did, of course. Er, I mean, you did.

Your alter ego.

Peter Below

unread,
Jan 10, 2000, 3:00:00 AM1/10/00
to
In article <85as47$17...@bornews.borland.com>, Loren Scott [Vista
Software] wrote:
> It works beautifully, even on my Windows 2000 system.
>

Nice to know.

Steve Turner

unread,
Jan 11, 2000, 3:00:00 AM1/11/00
to
In article <856hje$sa...@bornews.borland.com>, Loren Scott [Vista
Software] <lor...@vistasoftware.com> writes
>Hi Gang,

>
>I'm still looking for a solution on this one, if anyone has any experience
>in this area.
>
>Best regards,
>
>Loren Scott

Loren,

Here's my contribution - hopefully it will help you.

Listed below is a (Delphi 1) experimental unit that I worked on some
time ago. It contains a class called TDLLInfo, which allows a (16- or
32-bit) DLL's exported functions and imported modules/functions to be
identified. Note that it's a Delphi-1 unit - I haven't had the time to
try it out by compiling it under any of the 32-bit Delphi versions.
Maybe you could?

The majority of the code which accesses 32-bit DLL's was gleaned off the
internet from Gerald Nunn's GExperts web site at:
http://www.gexperts.com. The name of the original module was GX_UPE.PAS.

The code which accesses 16-bit DLL's was written by myself after
examining various technical documents, the origins of which I cannot
recall.

Here is the unit (remember that it is EXPERIMENTAL):

{
File: DLL_INFO.PAS

Contains a class called TDLLInfo, which allows a DLL's exported
functions and
imported modules/functions to be identified.

The class supports two different DLL file formats - the 16-bit Windows
Executable (WE) and the 32-bit Windows Portable Executable (PE).

Currently the imported function names can only be identified with 32-bit
DLL's.
I am not sure whether 16-bit DLL's are supposed to carry the imported
function
names, but on the ones that I tested the class with, only the module
names
were present.

The majority of the code which accesses 32-bit DLL's was gleaned off the
internet from Gerald Nunn's GExperts web site at:

http://www.gexperts.com

The name of the original module was GX_UPE.PAS. It was modified by me to
both
allow it to be compiled using 16-bit Delphi, and to allow it to access
16-bit
Windows Executable (WE) DLL's also.

The original GX_UPE.PAS module contained very little comments, so proper
explanations of various routine which I copied may not be forthcoming in
this
module either.

For 16-bit Windows Executables, the exported functions can appear in two
file
areas - the "Resident Name Table" and the "Non-resident Name Table". The
first
entry in the Non-resident Name Table is always the module description
string,
therefore it is not a real function.

Whilst this class has been designed to access Windows DLL's, it should
operate
perfectly well on Windows EXE's also!

Example of Useage
=================

The following procedure fills two memo boxes on a form with the exports
and
imports for the DLL called C:\XYZ.DLL.

procedure TForm1.Button1Click(Sender: TObject);
var
Count: Integer;
DLLInfo: TDLLInfo;
const
DLLName = 'c:\xyz.dll';
begin
DLLInfo := TDLLInfo.Create(DLLName);

try
with DLLInfo do
Memo1.Lines.Add('Export Base: ' + '$' + IntToHex(ExportBase,
16));

with DLLInfo.ExportList do
if (FunctionCount > 0) then
for Count := 0 to FunctionCount - 1 do
with Functions[Count] do
Memo1.Lines.Add(Name + #9 + '$' +
IntToHex(Ordinal, 16)
+ #9 + '$' + IntToHex(Address, 16));

with DLLInfo.ImportList do
if (FunctionCount > 0) then
for Count := 0 to FunctionCount - 1 do
with Functions[Count] do
Memo2.Lines.Add(ModuleName + #9 + Name + #9
+ '$' + IntToHex(Ordinal, 16));
finally
DLLInfo.Free;
end;

end;


S.P.Turner 24th June 1998
}

unit Dll_info;

interface

uses
Classes;

type
{ The original GX_UPE.PAS module was written for 32-bit Delphi,
where a
"SmallInt" is 16-bits and an "Integer" is 32-bits. These two type
definitions allow the data sizes to be correctly honoured in the
16-bit world (a 32-bit "SmallInt" equals a 16-bit "Integer", and a
32-bit
"Integer" equals a 16-bit "LongInt") }
TSmallInt = Integer;
TInteger = Longint;

{ DOS executable header record. This is included at the start of all
Windows executable files also }
TDosHeader = packed record
e_magic: TSmallInt; { Magic number - $5A4D }
e_cblp: TSmallInt; { Bytes on last page of file }
e_cp: TSmallInt; { Pages in file }
e_crcl: TSmallInt; { Relocations }
e_cparhdr: TSmallInt; { Size of header in paragraphs }
e_minalloc: TSmallInt; { Minimum extra paragraphs needed }
e_maxalloc: TSmallInt; { Maximum extra paragraphs needed }
e_ss: TSmallInt; { Initial (relative) SS value }
e_sp: TSmallInt; { Initial SP value }
e_csum: TSmallInt; { Checksum }
e_ip: TSmallInt; { Initial IP value }
e_cs: TSmallInt; { Initial (relative) CS value }
e_lfarclc: TSmallInt; { File address of relocation table }
e_ovno: TSmallInt; { Overlay number }
e_res: Array[1..4] of TSmallInt; { Reserved words }
e_oemid: TSmallInt; { OEM identifier (for e_oeminfo) }
e_oeminfo: TSmallInt; { OEM information; e_oemid specific }
e_res2: Array[1..10] of TSmallInt; { Reserved words }
e_lfanew: TInteger; { File address of new exe header (used
by
Windows) }
end;

{ 16-bit Windows Executable (WE) header record }
TWinHeader = packed record
Sig: TSmallInt; { Signature word ("NE") }
LinkerVer: Byte; { Linker version number }
LinkerRev: Byte; { Linker revision number }
EntryTableOffset: TSmallInt; { Offset to the entry table
(relative
to the beginning of the
header) }
EntryTableLength: TSmallInt; { Length of the entry table
(bytes) }
Reserved1: Array[0..3] of Byte; { Reserved }
Flags: TSmallInt; { Flags that describe the
contents of
the executable file }
AutoDataSegNo: TSmallint; { Automatic data segment number
}
LocalHeapSize: TSmallInt; { Initial size of the local heap
(bytes) }
StackSize: TSmallInt; { Initial size of the stack
(bytes) }

CSIPValue: Array[0..1] of TSmallInt; { Segment:Offset value
of CS:IP
(Offset stored first)
}
SSSPValue: Array[0..1] of TSmallInt; { Segment:Offset value
of SS:SP
(Offset stored first)
}

NumSegTableEntries: TSmallInt; { The number of entries in
the
segment table }
NumModRefTableEntries: TSmallInt; { The number of entries in
the
module-reference table }
NonResidentTableSize: TSmallInt; { The size of the
nonresident-name
table (bytes) }
SegTableOffset: TSmallInt; { Offset to the segment table
(relative
to the beginning of the
header) }
ResTableOffset: TSmallInt; { Offset to the resource table
(relative
to the beginning of the
header) }
ResNameTableOffset: TSmallInt; { Offset to the resident-name
table
(relative to the beginning of
the
header) }
ModRefTableOffset: TSmallInt; { Offset to the module-reference
table
(relative to the beginning of
the
header) }
ImpNameTableOffset: TSmallInt; { Offset to the imported-name
table
(relative to the beginning of
the
header) }
NonResNameTableOffset: TSmallInt;{ Offset to the nonresident-
name table
(relative to the beginning of
the
header) }
Reserved2: Array[0..1] of Byte; { Reserved }
NumMovEntryPoints: TSmallInt; { The number of movable entry
points }

LogSectorShift: TSmallInt; { Shift count that is used to
align the
logical sector }
NumResSegments: TSmallInt; { The number of resource
segments }
TargetOS: Byte; { The target operating system }
ExtraEXEInfo: Byte; { Specifies additional
information
about the executable file }
FastLoadOffset: TSmallInt; { The offset, in sectors, to the
beginning of the fast-load
area }
FastLoadLength: TSmallInt; { The length, in sectors, of the
fast-load area }
Reserved3: Array[0..1] of Byte; { Reserved }
ExpectedWinVer: TSmallInt; { Specifies the expected version
number
for Windows }
end;

{ 32-bit Windows Portable Executable (PE) header record (taken from
the original GX_UPE.PAS module, therefore not documented very
well) }
TPEImgHeader = packed record
Machine: TSmallInt;
NumberofSections: TSmallInt;
TimeDateStamp: TInteger;
PointerToSymboltable: TInteger;
NumberofSymbols: TInteger;
SizeOfOptionalHeader: TSmallInt;
Characteristics: TSmallInt;
end;

{ Miscellaneous 32-bit Windows Portable Executable (PE) records
(taken from
the original GX_UPE.PAS module, therefore not documented very
well) }
TImage_Data_Directory = packed record
VirtualAddress: TInteger;
Size: TInteger;
end;

TPEOptionalHeader = packed record
Magic: TSmallInt;
MajorLinkerVersion: Byte;
MinorLinkerVersion: Byte;
SizeOfCode: TInteger;
SizeofInitializedData: TInteger;
SizeofUninitializedData: TInteger;
AddressofEntryPoint: TInteger;
BaseofCode: TInteger;
BaseofData: TInteger;
ImageBase: TInteger;
SectionAlignment: TInteger;
FileAlignment: TInteger;
MajorOperatingSystemVersion: TSmallInt;
MinorOperatingSystemVersion: TSmallInt;
MajorImageVersion: TSmallInt;
MinorImageVersion: TSmallInt;
MajorSubsystemVersion: TSmallInt;
MinorSubsystemVersion: TSmallInt;
Reserved1: TInteger;
SizeofImage: TInteger;
SizeofHeaders: TInteger;
CheckSum: TInteger;
Subsystem: TSmallInt;
DLLCharacteristics: TSmallInt;
SizeofStackReserve: TInteger;
SizeofStackCommit: TInteger;
SizeofHeapReserve: TInteger;
SizeofHeapCommit: TInteger;
LoaderFlags: TInteger;
NumberofRVAandSizes: TInteger;
DataDirectories: Array[0..15] of TImage_Data_Directory;
end;

TImageSectionHeader = packed record
Name: Array[0..7] of Char;
VirtualSize: TInteger;
VirtualAddress: TInteger;
SizeofRawData: TInteger;
PointerToRawData: TInteger;
PointerToRelocations: TInteger;
PointerToLineNumbers: TInteger;
NumberofRelocations: TSmallInt;
NumberofLineNumbers: TSmallInt;
Characteristics: TInteger;
End;

TPEImportDescriptors = packed record
Characteristics: TInteger;
TimeDateStamp: TInteger;
ForwarderChain: TInteger;
Name: TInteger;
FirstThunk: TInteger;
end;

{ TImportRec definition - used by TImportList }
TImportRec = record
ModuleName: String; { The name of the module in which the
imported
function resides }
Name: String; { The name of the imported function (only
reported
for 32-bit Windows Portable Executable
files) }
Ordinal: TInteger; { The ordinal number of the imported
function (only
reported for 32-bit Windows Portable
Executable
files) }
end;

{ TImportList - a class which is used to store a list of DLL
imported
functions. The protected function Add is used to store a new
function in
the list (by way of a TImportRec record). The public property
Functions
(implemented as an array) allows access to all the functions
stored in the
list (again, by way of TImportRec records). The number of
functions
stored is indicated by FunctionCount }
TImportList = class(TObject)
private
ImportList: TStringList;

function GetFunctionCount: Integer;
function GetFunction(Index: Integer): TImportRec;
protected
procedure Add(const ImportRec: TImportRec);
procedure Clear;
public
constructor Create;
destructor Destroy; override;

property FunctionCount: Integer read GetFunctionCount;
property Functions[Index: Integer]: TImportRec
read GetFunction; default;
end;

{ TExportRec definition - used by TExportList }
TExportRec = record
Name: String; { The name of the exported function }
Ordinal: TInteger; { The ordinal of the exported function }
Address: TInteger; { The export address of the exported
function (only
reported for 32-bit Windows Portable
Executable
files) }
end;

{ TExportList - a class which is used to store a list of DLL
exported
functions. The protected function Add is used to store a new
function in
the list (by way of a TExportRec record). The public property
Functions
(implemented as an array) allows access to all the functions
stored in the
list (again, by way of TExportRec records). The number of
functions
stored is indicated by FunctionCount }
TExportList = class(TObject)
private
ExportList: TStringList;

function GetFunctionCount: Integer;
function GetFunction(Index: Integer): TExportRec;
protected
procedure Add(const ExportRec: TExportRec);
procedure Clear;
public
constructor Create;
destructor Destroy; override;

property FunctionCount: Integer read GetFunctionCount;
property Functions[Index: Integer]: TExportRec
read GetFunction; default;
end;

{ TDLLInfo - class which allows a DLL's exported functions and
imported
modules/functions to be identified }
TDLLInfo = class(TObject)
private
FFileName: String;

FStream: TFileStream;
FSectionStart: TInteger;

FIsMSDos: Boolean;
FIsPE: Boolean;
FIsWE: Boolean;

FDosHeader: TDOSHeader;
FWinHeader: TWinHeader;

FPEHeader: TPEImgHeader;
FPEOptionalHeader: TPEOptionalHeader;

FExportList: TExportList;
FImportList: TImportList;
FExportBase: TInteger;

procedure ReadFileFormat;
procedure ReadExportList;
procedure ReadImportList;
function GetEnclosingSectionHeader(rva: TInteger;
var SectionHeader: TImageSectionHeader): Boolean;
procedure GetImportFunctions(Import: TPEImportDescriptors;
Delta: TInteger; ModuleName: String);
public
constructor Create(FName: String);
destructor Destroy; override;

{ The DLL file name, as passed to the Create constructor }
property FileName: String read FFileName;

{ Three boolean properties which depict the file's composition:

IsMSDos - True for all DOS, Win-16, and Win-32 executable
files
IsPE - True for 32-bit Windows Portable Executable (PE) files
IsWE - True for 16-bit Windows Executable (WE) files }
property IsMSDos: Boolean read FIsMSDos;
property IsPE: Boolean read FIsPE;
property IsWE: Boolean read FIsWE;

{ The list of imported modules/functions }
property ImportList: TImportList read FImportList;

{ The list of exported functions }
property ExportList: TExportList read FExportList;

{ The export base. This value appears to need adding to the
ordinal
of each function exported by the DLL (for 32-bit Windows
Portable
Executables only - not used for 16-bit Windows Executables) }
property ExportBase: TInteger read FExportBase;
end;

implementation

uses
SysUtils;

type
{ TImportRecObj definition - used by TImportList. TImportList's
string list
can only store TObject-descended types, so in order to store
TImportRec
records in the list we must create TImportRecObj objects, store
the
records inside the objects, and then add these new objects to the
string
list }
TImportRecObj = class(TObject)
ImportRec: TImportRec;
end;

{ TExportRecObj definition - used by TExportList. TExportList's
string list
can only store TObject-descended types, so in order to store
TExportRec
records in the list we must create TExportRecObj objects, store
the
records inside the objects, and then add these new objects to the
string
list }
TExportRecObj = class(TObject)
ExportRec: TExportRec;
end;

{ Miscellaneous 32-bit Windows Portable Executable (PE) records
(taken from
the original GX_UPE.PAS module, therefore not documented very
well) }
TPEExportImage = packed record
Characteristics: TInteger;
TimeDateStamp: TInteger;
MajorVersion: TSmallInt;
MinorVersion: TSmallInt;
Name: TInteger;
Base: TInteger;
NumberofFunctions: TInteger;
NumberofNames: TInteger;
AddressOfFunctions: TInteger;
AddressofNames: TInteger;
AddressofNameOrdinals: TInteger;
End;

TThunkData = packed record
AddressOfData: TInteger;
end;

TImportFunction = packed record
Ordinal: TSmallInt;
Name: Array[0..255] of Char;
end;

{*************************}
{ TImportList }
{*************************}

{*************************}
{ Create }
{*************************}

{ Overridden constructor }

constructor TImportList.Create;
begin
{ Always call inherited constructor }
inherited Create;

{ Create the string list and tell it to allow duplicates }
ImportList := TStringList.Create;
ImportList.Duplicates := dupAccept;
end;

{*************************}
{ Destroy }
{*************************}

{ Overridden destructor }

destructor TImportList.Destroy;
var
p: Integer;
begin
{ Delete all associated TImportRecObj objects before deleting the
list
itself }
if (ImportList.Count > 0) then
for p := 0 to ImportList.Count - 1 do
TImportRecObj(ImportList.Objects[p]).Free;

ImportList.Free;

{ Always call inherited destructor }
inherited Destroy;
end;

{*************************}
{ Add }
{*************************}

{ Adds a new function contained in the given ImportRec record to the
list }

procedure TImportList.Add(const ImportRec: TImportRec);
var
ImportRecObj: TImportRecObj;
begin
{ The string list can only store TObject-descended types, so to
store the
ImportRec record in the list we must create a new TImportRecObj
object,
store the record inside the object, and then add this new object
to the
string list }
ImportRecObj := TImportRecObj.Create;
ImportRecObj.ImportRec := ImportRec;
ImportList.AddObject(ImportRec.Name, ImportRecObj);
end;

{*************************}
{ Clear }
{*************************}

{ Removes all import functions currently stored in the list }

procedure TImportList.Clear;
var
p: Integer;
begin
{ Delete all associated TImportRecObj objects before clearing the
list }
if (ImportList.Count > 0) then
for p := 0 to ImportList.Count - 1 do
TImportRecObj(ImportList.Objects[p]).Free;

ImportList.Clear;
end;

{*************************}
{ GetFunctionCount }
{*************************}

{ Property helper routine, which returns the number of import functions
stored in the list }

function TImportList.GetFunctionCount: Integer;
begin
Result := ImportList.Count;
end;

{*************************}
{ GetFunction }
{*************************}

{ Property helper routine, which returns a TImportRec record containing
information about a particular import function stored in the list }

function TImportList.GetFunction(Index: Integer): TImportRec;
begin
Result := TImportRecObj(ImportList.Objects[Index]).ImportRec;
end;

{*************************}
{ TExportList }
{*************************}

{*************************}
{ Create }
{*************************}

{ Overridden constructor }

constructor TExportList.Create;
begin
{ Always call inherited constructor }
inherited Create;

{ Create the string list and tell it to allow duplicates }
ExportList := TStringList.Create;
ExportList.Duplicates := dupAccept;
end;

{*************************}
{ Destroy }
{*************************}

{ Overridden destructor }

destructor TExportList.Destroy;
var
p: Integer;
begin
{ Delete all associated TExportRecObj objects before deleting the
list
itself }
if (ExportList.Count > 0) then
for p := 0 to ExportList.Count - 1 do
TExportRecObj(ExportList.Objects[p]).Free;

ExportList.Free;

{ Always call inherited destructor }
inherited Destroy;
end;

{*************************}
{ Add }
{*************************}

{ Adds a new function contained in the given ExportRec record to the
list }

procedure TExportList.Add(const ExportRec: TExportRec);
var
ExportRecObj: TExportRecObj;
begin
{ The string list can only store TObject-descended types, so to
store the
ExportRec record in the list we must create a new TExportRecObj
object,
store the record inside the object, and then add this new object
to the
string list }
ExportRecObj := TExportRecObj.Create;
ExportRecObj.ExportRec := ExportRec;
ExportList.AddObject(ExportRec.Name, ExportRecObj);
end;

{*************************}
{ Clear }
{*************************}

{ Removes all export functions currently stored in the list }

procedure TExportList.Clear;
var
p: Integer;
begin
{ Delete all associated TExportRecObj objects before clearing the
list }
if (ExportList.Count > 0) then
for p := 0 to ExportList.Count - 1 do
TExportRecObj(ExportList.Objects[p]).Free;

ExportList.Clear;
end;

{*************************}
{ GetFunctionCount }
{*************************}

{ Property helper routine, which returns the number of export functions
stored in the list }

function TExportList.GetFunctionCount: Integer;
begin
Result := ExportList.Count;
end;

{*************************}
{ GetFunction }
{*************************}

{ Property helper routine, which returns a TExportRec record containing
information about a particular export function stored in the list }

function TExportList.GetFunction(Index: Integer): TExportRec;
begin
Result := TExportRecObj(ExportList.Objects[Index]).ExportRec;
end;

{*************************}
{ TDLLInfo }
{*************************}

{*************************}
{ Create }
{*************************}

{ Overridden constructor }

constructor TDLLInfo.Create(FName: String);
begin
{ Always call inherited constructor }
inherited Create;

{ Create the exports and imports lists }
FExportList := TExportList.Create;
FImportList := TImportList.Create;

{ Store the given DLL file name }
FFileName := FName;

{ Obtain the file's composition (this function also populates the
exports
and imports lists) }
ReadFileFormat;
end;

{*************************}
{ Destroy }
{*************************}

{ Overridden destructor }

destructor TDLLInfo.Destroy;
begin
{ Destroy the exports and imports lists }
FImportList.Free;
FExportList.Free;

{ Always call inherited destructor }
inherited Destroy;
end;

{*************************}
{ ReadFileFormat }
{*************************}

{ Obtains the composition of the DLL file, and populates the exports and
imports lists }

procedure TDLLInfo.ReadFileFormat;
var
PE: Array[0..3] of Char;
Begin
FStream := TFileStream.Create(Filename, fmOpenRead +
fmShareDenyNone);

try
{ Read the DOS header record from the start of the file and
check that
it is a DOS executable }
FStream.Read(FDosHeader, SizeOf(FDosHeader));

if (FDosHeader.e_magic <> $5A4D) then
begin
{ The file is not a DOS executable, so we cannot continue }
Exit;
end; { end if }

FIsMSDos := True;

{ Check that the file is a Windows executable }
FStream.Position := FDosHeader.e_lfanew;
FStream.Read(PE, SizeOf(PE));

if ((PE[0] = 'P') and (PE[1] = 'E') and (PE[2] = #0) and
(PE[3] = #0)) then
begin
{ The file is a 32-bit Windows Portable Executable (PE) file
}
FIsWE := True;
FIsPE := True;
end
else if ((PE[0] = 'N') and (PE[1] = 'E')) then
begin
{ The file is a 16-bit Windows Executable (WE) file }
FIsWE := True;
end
else
begin
{ The file is not a Windows executable, so we cannot
continue }
Exit;
end; { end if }

{ Populate the exports and imports lists. This has to be done
differently depending upon whether the file is a 32-bit or
16-bit
DLL }
if FIsPE then
begin
FStream.Read(FPEHeader, SizeOf(FPEHeader));
FStream.Read(FPEOptionalHeader, Sizeof(FPEOptionalHeader));
FSectionStart := FStream.Position;
ReadExportList;
ReadImportList;
end
else
begin
FStream.Position := FDosHeader.e_lfanew;
FStream.Read(FWinHeader, SizeOf(FWinHeader));
ReadExportList;
ReadImportList;
end; { end if }

finally
FStream.Free;
end; { end try }

end;

{*************************}
{ ReadExportList }
{*************************}

{ Obtains the exported functions from the DLL and stores them in the
exports
list }

procedure TDLLInfo.ReadExportList;

{*************************}
{ ReadWEExportList }
{*************************}

{ Local routine which obtains the exported functions from the 16-bit
Windows Executable (WE) DLL and stores them in the exports list }

procedure ReadWEExportList;
var
Len: Byte;
Ordinal: TSmallInt;
Buf: Array[0..254] of Char;
ExportRec: TExportRec;
begin
{ Read the entries from the resident-name table one-by-one and
add them
to the exports list }
FStream.Position := FDosHeader.e_lfanew +
FWinHeader.ResNameTableOffset;

repeat
FStream.Read(Len, SizeOf(Len));

if (Len <> 0) then
begin
FillChar(Buf, SizeOf(Buf), #0);
FStream.Read(Buf, Len);
FStream.Read(Ordinal, SizeOf(Ordinal));
ExportRec.Name := StrPas(Buf);
ExportRec.Ordinal := Ordinal;
ExportRec.Address := 0; { Not used }
FExportList.Add(ExportRec);
end; { end if }

until (Len = 0);

{ Read the entries from the nonresident-name table one-by-one
and add
them to the exports list also. The first entry in this table
is always
the module description string, therefore it is not a real
function.
Add it to the exports list anyway }
FStream.Position := FWinHeader.NonResNameTableOffset;

repeat
FStream.Read(Len, SizeOf(Len));

if (Len <> 0) then
begin
FillChar(Buf, SizeOf(Buf), #0);
FStream.Read(Buf, Len);
FStream.Read(Ordinal, SizeOf(Ordinal));
ExportRec.Name := StrPas(Buf);
ExportRec.Ordinal := Ordinal;
ExportRec.Address := 0; { Not used }
FExportList.Add(ExportRec);
end; { end if }

until (Len = 0);

end;

{*************************}
{ ReadPEExportList }
{*************************}

{ Local routine which obtains the exported functions from the 32-bit
Windows Portable Executable (PE) DLL and stores them in the
exports
list. This routine has been taken from the original GX_UPE.PAS
module,
and so it is not commented very well }

procedure ReadPEExportList;
var
SectionHeader: TImageSectionHeader;
delta: TInteger;
ExportInfo: TPEExportImage;
ExportAddress, NameAddr: TInteger;
Ordinal: Word;
Name: String;
Buf: Array[0..255] of char;
i, j: TInteger;
ExportRec: TExportRec;
begin
if not GetEnclosingSectionHeader(
FPEOptionalHeader.DataDirectories[0].VirtualAddress,
SectionHeader)
then
Exit;

delta := SectionHeader.VirtualAddress - SectionHeader.PointerToR
awData;
FStream.Position := FPEOptionalHeader.DataDirectories[0].Virtual
Address
- delta;
FStream.Read(ExportInfo, sizeof(ExportInfo));

if (ExportInfo.Characteristics <> 0) then
Exit;

{ Store the export base }
FExportBase := ExportInfo.Base;

for i := 0 to ExportInfo.NumberofNames - 1 do
begin
{ Reposition for each function in turn. Get the file
position of
the function name and read the name from the file }
FStream.Position := (ExportInfo.AddressofNames - delta)
+ (i * SizeOf(NameAddr));
FStream.Read(NameAddr, SizeOf(NameAddr));
FStream.Position := NameAddr - delta;
FStream.Read(Buf, SizeOf(Buf));
Name := StrPas(Buf);

{ Read the export address of the function from the file }
FStream.Position := (ExportInfo.AddressOfFunctions - delta)
+ (i * SizeOf(ExportAddress));
FStream.Read(ExportAddress, SizeOf(ExportAddress));

{ Read the ordinal of the function from the file }
FStream.Position := (ExportInfo.AddressofNameOrdinals -
delta)
+ (i * SizeOf(Ordinal));
FStream.Read(Ordinal, SizeOf(Ordinal));

{ Add the function to the exports list }
ExportRec.Name := Name;
ExportRec.Ordinal := Ordinal;
ExportRec.Address := ExportAddress;
FExportList.Add(ExportRec);
end; { end for }

end;

begin
FExportList.Clear;

{ The exported functions have to be obtained differently depending
upon
whether the file is a 32-bit or 16-bit DLL }
if FIsPE then
ReadPEExportList
else
ReadWEExportList;

end;

{*************************}
{ ReadImportList }
{*************************}

{ Obtains the imported modules/functions from the DLL and stores them in
the imports list }

procedure TDLLInfo.ReadImportList;

{*************************}
{ ReadWEImportList }
{*************************}

{ Local routine which obtains the imported modules/functions from
the
16-bit Windows Executable (WE) DLL and stores them in the imports
list }

procedure ReadWEImportList;
var
Len: Byte;
Offset: TSmallInt;
Buf: Array[0..254] of Char;
Count: TSmallInt;
ModRefTable: ^TSmallInt;
ModRefTableSize: TSmallInt;
ImportRec: TImportRec;
begin
{ Allocate memory for the module-reference table to be read in }
ModRefTableSize := SizeOf(ModRefTable^)
* FWinHeader.NumModRefTableEntries;
GetMem(ModRefTable, ModRefTableSize);

try
{ Read the module-reference table from the DLL }
FStream.Position := FDosHeader.e_lfanew
+ FWinHeader.ModRefTableOffset;
FStream.Read(ModRefTable^, ModRefTableSize);

{ Read the entries from the imported-name table one-by-one
and add
them to the exports list }
for Count := 1 to FWinHeader.NumModRefTableEntries do
begin
FStream.Position := FDosHeader.e_lfanew
+ FWinHeader.ImpNameTableOffset + ModRefTable^;
FStream.Read(Len, SizeOf(Len));
FillChar(Buf, SizeOf(Buf), #0);
FStream.Read(Buf, Len);

ImportRec.ModuleName := StrPas(Buf);
ImportRec.Name := ''; { Not used }
ImportRec.Ordinal := 0; { Not used }
FImportList.Add(ImportRec);

Inc(ModRefTable, 1);
end; { end for }

Dec(ModRefTable, FWinHeader.NumModRefTableEntries);
finally
{ Free the memory allocated for the module-reference table }
FreeMem(ModRefTable, ModRefTableSize);
end; { end try }

end;

{*************************}
{ GetName }
{*************************}

{ Local routine taken from the original GX_UPE.PAS module, which
reads a
module name from the DLL file and returns it }

function GetName(L: TInteger): String;
var
SPos: TInteger;
Count: TInteger;
Buf: Array[0..254] of Char;
begin
SPos := FStream.Position;
FStream.Position := L;
FStream.Read(Buf, SizeOf(Buf));
Result := StrPas(Buf);
FStream.Position := SPos;
end;

{*************************}
{ ReadPEImportList }
{*************************}

{ Local routine which obtains the imported modules/functions from
the
32-bit Windows Portable Executable (PE) DLL and stores them in the
imports list. This routine has been taken from the original
GX_UPE.PAS
module, and so it is not commented very well }

procedure ReadPEImportList;
var
Import: TPEImportDescriptors;
delta, p: TInteger;
SectionHeader: TImageSectionHeader;
Name: String;
begin
if not GetEnclosingSectionHeader(
FPEOptionalHeader.DataDirectories[1].VirtualAddress,
SectionHeader)
then
Exit;

delta := SectionHeader.VirtualAddress - SectionHeader.PointerToR
awData;
FStream.Position := FPEOptionalHeader.DataDirectories[1].Virtual
Address
- delta;

FStream.Read(Import, Sizeof(Import));

while ((Import.Name <> 0) and (FStream.Position < FStream.Size))
do
begin
Name := GetName(Import.Name - delta);

GetImportFunctions(Import, Delta, Name);
FStream.Read(Import, Sizeof(Import));
end; { end while }

end;

begin
FImportList.Clear;

{ The imported modules/functions have to be obtained differently
depending
upon whether the file is a 32-bit or 16-bit DLL }
if FIsPE then
ReadPEImportList
else
ReadWEImportList;

end;

{*************************}
{ GetImportFunctions }
{*************************}

{ Obtains the imported functions for the current imported module, from
the
32-bit Windows Portable Executable (PE) DLL and stores them in the
imports list. This routine has been taken from the original GX_UPE.PAS
module, and so it is not commented very well }

procedure TDLLInfo.GetImportFunctions(Import: TPEImportDescriptors;
Delta: TInteger; ModuleName: String);
var
Thunk: TInteger;
SPos, p: TInteger;
ThunkData: TThunkData;
st: String;
ImpF: TImportFunction;
ImportRec: TImportRec;
Begin
SPos := FStream.Position;

try
if (Import.Characteristics = 0) then
Thunk := Import.FirstThunk - delta
else
Thunk := Import.Characteristics - delta;

FStream.Position := Thunk;
FStream.Read(ThunkData, Sizeof(ThunkData));

while ((FStream.Position < FStream.Size)
and (ThunkData.AddressofData <> 0)) do
begin
p := FStream.Position;

if (ThunkData.AddressofData < 0) then
begin
{ Imported by Ordinal }
FStream.Position := Thunk;
ImportRec.ModuleName := ModuleName;
ImportRec.Name := '';
ImportRec.Ordinal := Word(ThunkData.AddressofData);
FImportList.Add(ImportRec);
end
else
begin
FStream.Position := ThunkData.AddressofData - delta;
FStream.Read(ImpF, Sizeof(ImpF));
ImportRec.ModuleName := ModuleName;
ImportRec.Name := StrPas(ImpF.Name);
ImportRec.Ordinal := ImpF.Ordinal;
FImportList.Add(ImportRec);
end; { end if }

FStream.Position := p;
FStream.Read(ThunkData, Sizeof(ThunkData));
end; { end while }

finally
FStream.Position := SPos;
end; { end try }

end;

{***************************}
{ GetEnclosingSectionHeader }
{***************************}

{ Obtains a section header from the 32-bit Windows Portable Executable
(PE)
DLL. This routine has been taken from the original GX_UPE.PAS module,
and
so it is not commented very well }

function TDLLInfo.GetEnclosingSectionHeader(rva: TInteger;
var SectionHeader: TImageSectionHeader): Boolean;
var
i: Integer;
begin
Result := False;

for i := 0 to FPEOptionalHeader.NumberofRVAAndSizes - 1 do
begin
FStream.Position := FSectionStart + Sizeof(SectionHeader) * i;
FStream.Read(SectionHeader, Sizeof(SectionHeader));

if ((rva >= SectionHeader.VirtualAddress) and (rva <=
SectionHeader.VirtualAddress + SectionHeader.VirtualSize))
then
begin
Result := True;
Exit;
end; { end if }

end; { end for }

end;

end.

Let me know whether you find it useful.
--
Steve Turner
Leeds, England
(Remove NOMORESPAM from return address to reply via e-mail)

Ralph Friedman (TeamB)

unread,
Jan 11, 2000, 3:00:00 AM1/11/00
to
In message <N1YLZDAF...@dmg-hq.demon.co.uk>, Steve Turner stated:
Steve,

I know that you are "within the letter of the law here", but 39k is a
bit extreme. This would have been better posted in b.p.Attachments with
a reference to it here.

Steve Turner

unread,
Jan 11, 2000, 3:00:00 AM1/11/00
to
In article <VA.00000af...@della.garlin>, Ralph Friedman (TeamB)
<ralphf...@email.com> writes

>
>I know that you are "within the letter of the law here", but 39k is a
>bit extreme. This would have been better posted in b.p.Attachments with
>a reference to it here.
>
Okay, point taken. The ng guidelines are however a bit vague regarding
what is deemed too-large a post. FWIW I did hesitate before I sent the
entire unit due to its "substantial" size. I thought about zipping it,
but that would have violated the guidelines regarding the posting of
attachments :-)

BTW I was not aware of b.p.attachments - I didn't see it in either the
www.borland.com/newsgroups list or the guidelines.

philippe_ranger

unread,
Jan 11, 2000, 3:00:00 AM1/11/00
to
<<Ralph:

I know that you are "within the letter of the law here", but 39k is a
bit extreme.
>>

Let's just pray no one auto-quotes Steve's message in reply!!

PhR

philippe_ranger

unread,
Jan 11, 2000, 3:00:00 AM1/11/00
to
<<Steve:

Okay, point taken. The ng guidelines are however a bit vague regarding
what is deemed too-large a post. FWIW I did hesitate before I sent the
entire unit due to its "substantial" size.
>>

The problem is that many, many people auto-quote and don't cut when
replying. 39K is already pretty bad (equivalent of having everyone download
39 simple messages from you). 39K repeated once or twice in reply is
disastrous -- over 100K's wasted dowload for everybody.

You have to think that the longer the code, the fewer people will actually
read it. So b.p.attachments is a most reasonable solution here.

<<
I thought about zipping it, but that would have violated the guidelines
regarding the posting of
attachments :-)
>>

Definitely.

<<
BTW I was not aware of b.p.attachments - I didn't see it in either the
www.borland.com/newsgroups list or the guidelines.
--
>>

Very good points.

PhR

Ralph Friedman (TeamB)

unread,
Jan 11, 2000, 3:00:00 AM1/11/00
to
In message <85fea3$47...@bornews.borland.com>, stated:

> Let's just pray no one auto-quotes Steve's message in reply!!
>
Philippe,

Gotta be grateful for the small things in life<g>

Ralph Friedman (TeamB)

unread,
Jan 11, 2000, 3:00:00 AM1/11/00
to
In message <XmCCZCAm...@dmg-hq.demon.co.uk>, Steve Turner stated:

> BTW I was not aware of b.p.attachments - I didn't see it in either the
> www.borland.com/newsgroups list or the guidelines.
>
and you point is taken. It's been addressed, we'll see what happens.

Loren Scott [Vista Software]

unread,
Jan 11, 2000, 3:00:00 AM1/11/00
to
Hi Steve,

Thanks for the sizeable contribution. <g>

Peter's code is already working out perfectly for my needs. But, if I get a
chance, I'll take a closer look at your solution as well.

Dr John Stockton

unread,
Jan 11, 2000, 3:00:00 AM1/11/00
to
JRS: In article <VA.00000af...@della.garlin> of Tue, 11 Jan 2000
11:08:21 in news:borland.public.delphi.objectpascal, Ralph Friedman

(TeamB) <ralphf...@email.com> wrote:
>In message <N1YLZDAF...@dmg-hq.demon.co.uk>, Steve Turner stated:
>Steve,
>
>I know that you are "within the letter of the law here", but 39k is a
>bit extreme. This would have been better posted in b.p.Attachments with
>a reference to it here.

B.P.Attachments, AIUI, has 3-day retention. A more permanent approach
would be to put it on a Web site, and a Demon TAM account gets a 20MB
allocation. Upload by FTP, info presumably findable on www.demon.net.
Upload *something* for index.htm to start a site.

It should then be accessible from a URL given here.

No-one's reported difficulty with Line 3 of my Sig below ... just be
careful with Bin/Txt modes of transfer. The following programs/units
there, at least, work with D3 "DCC32 -cc" console mode, and some may
even be of use : cvt_rome.pas, eratost*.pas, mjd_date.pas, dateprox.pas,
queens_8.pas, longcalc.pas, jad_9514.pas, paschal.pas, powers.pas,
series.pas, wr-roman.pas, pastri.pas, fpatan2.pas, arccot.pas,
invcos.pas, countdwn.pas, permute.pas, permutes.pas

--
© John Stockton, Surrey, UK. j...@merlyn.demon.co.uk Turnpike v4.00 MIME. ©
Web <URL: http://www.merlyn.demon.co.uk/> - FAQqish topics, acronyms & links.
PAS, EXE in <URL: http://www.merlyn.demon.co.uk/programs/> - see 00index.txt.
Do not Mail News to me. Before a reply, quote with ">" or "> " (SoRFC1036)

0 new messages