我想用Dll做软件的插件。
思路是这样的:
Dll中有很多参数及返回值都相同的函数,如:
function Cool(app: boolean; var Describe, Where, Content: PChar): boolean; stdcall;
function Great(app: boolean; var Describe, Where, Content: PChar): boolean; stdcall;
当然它们的具体实现不同。
我想在主程序中用循环调用所有类似的函数,请问有什么好方法吗?(假设主程序没有Dll中函数的个数与名称信息)
谢谢!
思路是这样的:
Dll中有很多参数及返回值都相同的函数,如:
function Cool(app: boolean; var Describe, Where, Content: PChar): boolean; stdcall;
function Great(app: boolean; var Describe, Where, Content: PChar): boolean; stdcall;
当然它们的具体实现不同。
我想在主程序中用循环调用所有类似的函数,请问有什么好方法吗?(假设主程序没有Dll中函数的个数与名称信息)
谢谢!
谢谢,可是如果函数并不固定呢?因为我的程序要对每一个DLL里的函数操作,以获得Describe,where,content三个参数,通过调用固定的函数似乎没法完成。
我觉得如果获取dll的导出函数列表,然后就可以循环了。不知道有没有什么更好的方法?
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ComCtrls, ExtCtrls, StdCtrls, ShellApi;const
IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16; IMAGE_SIZEOF_SHORT_NAME = 8; IMAGE_DOS_SIGNATURE = $5A4D;
IMAGE_OS2_SIGNATURE = $454E;
IMAGE_OS2_SIGNATURE_LE = $454C;
IMAGE_VXD_SIGNATURE = $454C;
IMAGE_NT_SIGNATURE = $4550; IMAGE_DIRECTORY_ENTRY_EXPORT = 0; // Export Directory
IMAGE_DIRECTORY_ENTRY_IMPORT = 1; // Import Directory
IMAGE_DIRECTORY_ENTRY_RESOURCE = 2; // Resource Directory
IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3; // Exception Directory
IMAGE_DIRECTORY_ENTRY_SECURITY = 4; // Security Directory
IMAGE_DIRECTORY_ENTRY_BASERELOC = 5; // Base Relocation Table
IMAGE_DIRECTORY_ENTRY_DEBUG = 6; // Debug Directory
//IMAGE_DIRECTORY_ENTRY_COPYRIGHT = 7; // (X86 usage)
IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = 7; // Architecture Specific Data
IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8; // RVA of GP
IMAGE_DIRECTORY_ENTRY_TLS = 9; // TLS Directory
IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10; // Load Configuration Directory
IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11; // Bound Import Directory in headers
IMAGE_DIRECTORY_ENTRY_IAT = 12; // Import Address Table
IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13; // Delay Load Import Descriptors
IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14; // COM Runtime descriptortype
TImageDosHeader = packed record
e_magic: Word; // Magic number
e_cblp: Word; // Bytes on last page of file
e_cp: Word; // Pages in file
e_crlc: Word; // Relocations
e_cparhdr: Word; // Size of header in paragraphs
e_minalloc: Word; // Minimum extra paragraphs needed
e_maxalloc: Word; // Maximum extra paragraphs needed
e_ss: Word; // Initial (relative) SS value
e_sp: Word; // Initial SP value
e_csum: Word; // Checksum
e_ip: Word; // Initial IP value
e_cs: Word; // Initial (relative) CS value
e_lfarlc: Word; // File address of relocation table
e_ovno: Word; // Overlay number
e_res: array[0..3] of Word; // Reserved words
e_oemid: Word; // OEM identifier (for e_oeminfo)
e_oeminfo: Word; // OEM information; e_oemid specific
e_res2: array[0..9] of Word; // Reserved words
e_lfanew: Word; // File address of new exe header
end;
PImageDosHeader = ^TImageDosHeader; TImageFileHeader = packed record
Machine: Word;
NumberOfSections: Word;
TimeDateStamp: DWORD;
PointerToSymbolTable: DWORD;
NumberOfSymbols: DWORD;
SizeOfOptionalHeader: Word;
Characteristics: Word;
end;
PImageFileHeader = ^TImageFileHeader; TImageDataDirectory = packed record
VirtualAddress: DWORD;
Size: DWORD;
end;
PImageDataDirectory = ^TImageDataDirectory; TImageSectionHeader = packed record
Name: array [0..IMAGE_SIZEOF_SHORT_NAME - 1] of Char;
PhysicalAddress: DWORD; //or VirtualSize
VirtualAddress: DWORD;
SizeOfRawData: DWORD;
PointerToRawData: DWORD;
PointerToRelocations: DWORD;
PointerToLinenumbers: DWORD;
NumberOfRelocations: Word;
NumberOfLinenumbers: Word;
Characteristics: DWORD;
end;
PImageSectionHeader = ^TImageSectionHeader; TImageOptionalHeader32 = packed record
// Standard fields.
Magic: Word;
MajorLinkerVersion: Byte;
MinorLinkerVersion: Byte;
SizeOfCode: DWORD;
SizeOfInitializedData: DWORD;
SizeOfUninitializedData: DWORD;
AddressOfEntryPoint: DWORD;
BaseOfCode: DWORD;
BaseOfData: DWORD;
// NT additional fields.
ImageBase: DWORD;
SectionAlignment: DWORD;
FileAlignment: DWORD;
MajorOperatingSystemVersion: WORD;
MinorOperatingSystemVersion: WORD;
MajorImageVersion: WORD;
MinorImageVersion: WORD;
MajorSubsystemVersion: WORD;
MinorSubsystemVersion: WORD;
Win32VersionValue: DWORD;
SizeOfImage: DWORD;
SizeOfHeaders: DWORD;
CheckSum: DWORD;
Subsystem: WORD;
DllCharacteristics: WORD;
SizeOfStackReserve: DWORD;
SizeOfStackCommit: DWORD;
SizeOfHeapReserve: DWORD;
SizeOfHeapCommit: DWORD;
LoaderFlags: DWORD;
NumberOfRvaAndSizes: DWORD;
DataDirectory: array[0..IMAGE_NUMBEROF_DIRECTORY_ENTRIES - 1] of TImageDataDirectory;
end;
PImageOptionalHeader32 = ^TImageOptionalHeader32; TImageNtHeaders = packed record
Signature: DWORD;
FileHeader: TImageFileHeader;
OptionalHeader: TImageOptionalHeader32;
end;
PImageNtHeaders = ^TImageNtHeaders; TImageImportByName = packed record
Hint: Word;
Name: array[0..MAX_PATH] of Char;
end;
PImageImportByName = ^TImageImportByName; TImageThunkData32 = packed record
case Integer of
0: (ForwarderString: PByte);
1: (Func: PDWORD);
2: (Ordinal: DWORD);
3: (AddressOfData: PImageImportByName);
end;
PImageThunkData32 = ^TImageThunkData32; TImageImportDescriptor = packed record
OriginalFirstThunk: DWORD; // 0 for terminating null import descriptor
TimeDateStamp: DWORD; // 0 if not bound,
// -1 if bound, and real date\time stamp
// in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
// O.W. date/time stamp of DLL bound to (Old BIND)
ForwarderChain: DWORD; // -1 if no forwarders
Name: DWORD;
FirstThunk: DWORD; // RVA to IAT (if bound this IAT has actual addresses)
end;
PImageImportDescriptor = ^TImageImportDescriptor; TImageExportDirectory = packed record
Characteristics: DWORD;
TimeDateStamp: DWORD;
MajorVersion: Word;
MinorVersion: Word;
Name: DWORD;
Base: DWORD;
NumberOfFunctions: DWORD;
NumberOfNames: DWORD;
AddressOfFunctions: DWORD; // RVA from base of image
AddressOfNames: DWORD; // RVA from base of image
AddressOfNameOrdinals: DWORD; // RVA from base of image
end;
PImageExportDirectory = ^TImageExportDirectory; TfrmMain = class(TForm)
Panel1: TPanel;
lvImport: TListView;
Splitter2: TSplitter;
lvExport: TListView;
labModule: TLabel;
procedure FormCreate(Sender: TObject);
procedure lvImportColumnClick(Sender: TObject; Column: TListColumn);
procedure lvImportCompare(Sender: TObject; Item1, Item2: TListItem;
Data: Integer; var Compare: Integer);
procedure lvExportCompare(Sender: TObject; Item1, Item2: TListItem;
Data: Integer; var Compare: Integer);
procedure lvExportColumnClick(Sender: TObject; Column: TListColumn);
private
FDosHeader: TImageDosHeader;
FNtHeader: TImageNtHeaders;
FImageSections: array of TImageSectionHeader;
procedure WMDropFile(var Message: TMessage); message WM_DROPFILES;
function AddressToOffset(VirtualAddress: DWORD): DWORD;
function IsPeFile(Stream: TStream): Boolean;
procedure ParsePeFile(Stream: TStream);
procedure ParseImportTable(Stream: TStream; DataDirectory: TImageDataDirectory);
procedure ParseExportTable(Stream: TStream; DataDirectory: TImageDataDirectory);
public
{ Public declarations }
end;
procedure TfrmMain.FormCreate(Sender: TObject);
begin
DragAcceptFiles(Handle, True);
end; procedure TfrmMain.lvImportColumnClick(Sender: TObject;
Column: TListColumn);
begin
Column.Tag := Column.Tag * -1;
lvImport.CustomSort(nil, Column.Index);
end;procedure TfrmMain.lvExportColumnClick(Sender: TObject;
Column: TListColumn);
begin
Column.Tag := Column.Tag * -1;
lvExport.CustomSort(nil, Column.Index);
end;procedure TfrmMain.lvImportCompare(Sender: TObject; Item1,
Item2: TListItem; Data: Integer; var Compare: Integer);
begin
if lvImport.Columns[Data].Tag = 0 then lvImport.Columns[Data].Tag := 1;
if Data = 0 then
Compare := StrComp(PChar(Item1.Caption), PChar(Item2.Caption))
else
Compare := StrComp(PChar(Item1.SubItems[Data - 1]), PChar(Item2.SubItems[Data - 1]));
Compare := Compare * lvImport.Columns[Data].Tag;
end;procedure TfrmMain.lvExportCompare(Sender: TObject; Item1,
Item2: TListItem; Data: Integer; var Compare: Integer);
begin
if lvExport.Columns[Data].Tag = 0 then lvExport.Columns[Data].Tag := 1;
if Data = 0 then
Compare := StrComp(PChar(Item1.Caption), PChar(Item2.Caption))
else
Compare := StrComp(PChar(Item1.SubItems[Data - 1]), PChar(Item2.SubItems[Data - 1]));
Compare := Compare * lvExport.Columns[Data].Tag;
end;procedure TfrmMain.WMDropFile(var Message: TMessage);
var
Count: Integer;
FileName: array[0..MAX_PATH] of Char;
Stream: TStream;
begin
Count := DragQueryFile(Message.WParam, MAXDWORD, nil, 0);
if Count > 1 then
begin
MessageBox(Handle, '不支持多个文件。', PChar(Caption), MB_ICONINFORMATION);
Exit;
end;
lvImport.Items.Clear;
lvExport.Items.Clear;
labModule.Caption := '';
DragQueryFile(Message.WParam, 0, FileName, MAX_PATH);
if not FileExists(FileName) then Exit;
Stream := TFileStream.Create(FileName, fmShareDenyNone);
try
if not IsPeFile(Stream) then
begin
labModule.Caption := ' 不是有效的PE文件';
Exit;
end;
Stream.Position := 0;
ParsePeFile(Stream);
lvImport.CustomSort(nil, 1);
lvExport.CustomSort(nil, 0);
labModule.Caption := ' ' + StrPas(FileName);
finally
Stream.Free;
end;
end;function TfrmMain.IsPeFile(Stream: TStream): Boolean;
var
DosHeader: TImageDosHeader;
NtHeader: TImageNtHeaders;
begin
Result := False;
if Stream.Size < SizeOf(DosHeader) then Exit;
Stream.Read(DosHeader, SizeOf(DosHeader));
if DosHeader.e_magic <> IMAGE_DOS_SIGNATURE then Exit;
Stream.Seek(DosHeader.e_lfanew, soBeginning);
Stream.Read(NtHeader, SizeOf(NtHeader));
if NtHeader.Signature <> IMAGE_NT_SIGNATURE then Exit;
Result := True;
end;function TfrmMain.AddressToOffset(VirtualAddress: DWORD): DWORD;
var
i: Integer;
begin
Result := 0;
for i := Low(FImageSections) to High(FImageSections) do
begin
if (VirtualAddress >= FImageSections[i].VirtualAddress)
and (VirtualAddress < FImageSections[i].VirtualAddress
+ FImageSections[i].SizeOfRawData) then
begin
Result := VirtualAddress + FImageSections[i].PointerToRawData
- FImageSections[i].VirtualAddress;
end;
end;
end;procedure TfrmMain.ParsePeFile(Stream: TStream);
begin
Stream.Read(FDosHeader, SizeOf(FDosHeader));
Stream.Seek(FDosHeader.e_lfanew, soBeginning);
Stream.Read(FNtHeader, SizeOf(FNtHeader));
SetLength(FImageSections, FNtHeader.FileHeader.NumberOfSections);
Stream.Read(FImageSections[0], FNtHeader.FileHeader.NumberOfSections * SizeOf(TImageSectionHeader));
Stream.Position := 0;
ParseExportTable(Stream, FNtHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]);
Stream.Position := 0;
ParseImportTable(Stream, FNtHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]);
end;procedure TfrmMain.ParseImportTable(Stream: TStream; DataDirectory: TImageDataDirectory);
var
i: Integer;
Item: TListItem;
szDll: array[0..MAX_PATH] of Char;
ImportDesc: PImageImportDescriptor;
Thunks: array[0..1024] of TImageThunkData32;
ImportByName: TImageImportByName;
begin
if DataDirectory.Size = 0 then Exit;
lvImport.Items.BeginUpdate;
ImportDesc := GetMemory(DataDirectory.Size);
try
Stream.Seek(AddressToOffset(DataDirectory.VirtualAddress), soBeginning);
Stream.Read(ImportDesc^, DataDirectory.Size);
while ImportDesc.Name <> 0 do
begin
Stream.Seek(AddressToOffset(ImportDesc^.Name), soBeginning);
Stream.Read(szDll, MAX_PATH);
if ImportDesc^.OriginalFirstThunk <> 0 then
Stream.Seek(AddressToOffset(ImportDesc^.OriginalFirstThunk), soBeginning)
else
Stream.Seek(AddressToOffset(ImportDesc^.FirstThunk), soBeginning);
Stream.Read(Thunks[0], SizeOf(Thunks));
for i := Low(Thunks) to High(Thunks) do
begin
if Thunks[i].AddressOfData = nil then Break;
Stream.Seek(AddressToOffset(DWORD(Thunks[i].AddressOfData)), soBeginning);
Item := lvImport.Items.Add;
Item.Caption := Format('%0.4d', [i + 1]);
Item.SubItems.Add(szDll);
Item.SubItems.Add(Format('0x%0.8X', [DWORD(Thunks[i].Func)]));
if Stream.Position = 0 then
begin
Item.SubItems.Add('(使用序号)');
Item.SubItems.Add(Format('%0.4d', [Thunks[i].Ordinal and $FF]));
end else
begin
Stream.Read(ImportByName, SizeOf(ImportByName));
Item.SubItems.Add(ImportByName.Name);
Item.SubItems.Add(Format('%0.4d', [ImportByName.Hint]));
end;
end;
Inc(ImportDesc);
end;
finally
FreeMemory(ImportDesc);
lvImport.Items.EndUpdate;
end;
end;procedure TfrmMain.ParseExportTable(Stream: TStream; DataDirectory: TImageDataDirectory);
var
i: Integer;
Item: TListItem;
szProcName: array[0..MAX_PATH] of Char;
Names, Functions: array of DWORD;
Ordinals: array of Word;
ExportDirec: TImageExportDirectory;
begin
if DataDirectory.Size = 0 then Exit;
lvExport.Items.BeginUpdate;
try
Stream.Seek(AddressToOffset(DataDirectory.VirtualAddress), soBeginning);
Stream.Read(ExportDirec, SizeOf(ExportDirec));
SetLength(Functions, ExportDirec.NumberOfFunctions);
SetLength(Names, ExportDirec.NumberOfNames);
SetLength(Ordinals, ExportDirec.NumberOfNames);
Stream.Seek(AddressToOffset(ExportDirec.AddressOfFunctions), soBeginning);
Stream.Read(Functions[0], ExportDirec.NumberOfFunctions * SizeOf(DWORD));
Stream.Seek(AddressToOffset(ExportDirec.AddressOfNames), soBeginning);
Stream.Read(Names[0], ExportDirec.NumberOfNames * SizeOf(DWORD));
Stream.Seek(AddressToOffset(ExportDirec.AddressOfNameOrdinals), soBeginning);
Stream.Read(Ordinals[0], ExportDirec.NumberOfNames * SizeOf(Word));
for i := Low(Names) to High(Names) do
begin
Item := lvExport.Items.Add;
Item.Caption := Format('%0.4d', [Ordinals[i] + ExportDirec.Base]);
Item.SubItems.Add(Format('0x%0.8X', [Functions[Ordinals[i]]]));
Stream.Seek(AddressToOffset(Names[i]), soBeginning);
Stream.Read(szProcName, MAX_PATH);
Item.SubItems.Add(szProcName);
end;
finally
lvExport.Items.EndUpdate;
end;
end;
object frmMain: TfrmMain
Left = 192
Top = 114
Width = 696
Height = 480
Caption = '导入导出函数查看'
Color = clBtnFace
Constraints.MinHeight = 300
Constraints.MinWidth = 400
Font.Charset = GB2312_CHARSET
Font.Color = clWindowText
Font.Height = -12
Font.Name = '宋体'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
PixelsPerInch = 96
TextHeight = 12
object Panel1: TPanel
Left = 0
Top = 0
Width = 688
Height = 446
Align = alClient
BevelOuter = bvNone
Caption = 'Panel1'
TabOrder = 0
object Splitter2: TSplitter
Left = 0
Top = 169
Width = 688
Height = 3
Cursor = crVSplit
Align = alTop
end
object labModule: TLabel
Left = 0
Top = 434
Width = 688
Height = 12
Align = alBottom
end
object lvImport: TListView
Left = 0
Top = 172
Width = 688
Height = 262
Align = alClient
Columns = <
item
Caption = '序号'
Width = 60
end
item
Caption = '导入模块'
Width = 150
end
item
Caption = '导入地址'
Width = 80
end
item
Caption = '导入名称'
Width = 300
end
item
Caption = '导出序号'
Width = 60
end>
ReadOnly = True
RowSelect = True
SortType = stText
TabOrder = 0
ViewStyle = vsReport
OnColumnClick = lvImportColumnClick
OnCompare = lvImportCompare
end
object lvExport: TListView
Left = 0
Top = 0
Width = 688
Height = 169
Align = alTop
Columns = <
item
Caption = '导出序号'
Width = 60
end
item
Caption = '导出地址'
Width = 80
end
item
Caption = '导出名称'
Width = 500
end>
ReadOnly = True
RowSelect = True
SortType = stText
TabOrder = 1
ViewStyle = vsReport
OnColumnClick = lvExportColumnClick
OnCompare = lvExportCompare
end
end
end
object frmMain: TfrmMain
Left = 192
Top = 114
Width = 696
Height = 480
Caption = '导入导出函数查看'
Color = clBtnFace
Constraints.MinHeight = 300
Constraints.MinWidth = 400
Font.Charset = GB2312_CHARSET
Font.Color = clWindowText
Font.Height = -12
Font.Name = '宋体'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
PixelsPerInch = 96
TextHeight = 12
object Panel1: TPanel
Left = 0
Top = 0
Width = 688
Height = 446
Align = alClient
BevelOuter = bvNone
Caption = 'Panel1'
TabOrder = 0
object Splitter2: TSplitter
Left = 0
Top = 169
Width = 688
Height = 3
Cursor = crVSplit
Align = alTop
end
object labModule: TLabel
Left = 0
Top = 434
Width = 688
Height = 12
Align = alBottom
end
object lvImport: TListView
Left = 0
Top = 172
Width = 688
Height = 262
Align = alClient
Columns = <
item
Caption = '序号'
Width = 60
end
item
Caption = '导入模块'
Width = 150
end
item
Caption = '导入地址'
Width = 80
end
item
Caption = '导入名称'
Width = 300
end
item
Caption = '导出序号'
Width = 60
end>
ReadOnly = True
RowSelect = True
SortType = stText
TabOrder = 0
ViewStyle = vsReport
OnColumnClick = lvImportColumnClick
OnCompare = lvImportCompare
end
object lvExport: TListView
Left = 0
Top = 0
Width = 688
Height = 169
Align = alTop
Columns = <
item
Caption = '导出序号'
Width = 60
end
item
Caption = '导出地址'
Width = 80
end
item
Caption = '导出名称'
Width = 500
end>
ReadOnly = True
RowSelect = True
SortType = stText
TabOrder = 1
ViewStyle = vsReport
OnColumnClick = lvExportColumnClick
OnCompare = lvExportCompare
end
end
end
2, 插件必须提供一些必须的接口函数,例如Register函数,Register必须提供一些必要的信息,比如插件名称,插件作者,插件版本等;
3, 建议使用Interface去实现, 这样可以隐藏你的关键代码,只用提供接口;