我想用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中函数的个数与名称信息)
谢谢!

解决方案 »

  1.   

    推荐遍历一个固定的路径,装载该路径下所有DLL,然后调用一个固定的入口函数,一个参数为调用原因(加载或者卸载),另一个参数为接口,这个接口提供些什么功能就看插件的复杂度了。
      

  2.   

    lake_cx :
    谢谢,可是如果函数并不固定呢?因为我的程序要对每一个DLL里的函数操作,以获得Describe,where,content三个参数,通过调用固定的函数似乎没法完成。
      

  3.   

    dll插件没你那样做的,是插件来配合主程序,不是主程序来配合插件
      

  4.   

    其实这也不算插件,只是程序中的一个具体功能的实现,做成DLL只是为了日后扩展。不知道有没有什么方法能够满足上述要求?
    我觉得如果获取dll的导出函数列表,然后就可以循环了。不知道有没有什么更好的方法?
      

  5.   

    给你一个查看模块导入导出函数的代码,这个是声明部分
    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;
      

  6.   

    这个是实现部分
    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;
      

  7.   

    这个是dfm文件
    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
      

  8.   

    这个是dfm文件
    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
      

  9.   

    1, 插件可以通过固定目录或者配置文件来指定;
    2, 插件必须提供一些必须的接口函数,例如Register函数,Register必须提供一些必要的信息,比如插件名称,插件作者,插件版本等;
    3, 建议使用Interface去实现, 这样可以隐藏你的关键代码,只用提供接口;