How to check a DLL if a function exists?

后端 未结 2 1041
醉梦人生
醉梦人生 2020-12-10 07:58

I\'m working on something which dynamically loads specially formulated DLL\'s. I need to be able to check the DLL and make sure all the expected functions exist before I con

相关标签:
2条回答
  • 2020-12-10 08:33

    If you are in control of the DLLs and you don't want to load them in order to check capability, then you could use the version resource to indicate capability. This would require the host app to have knowledge of what was the minimum supported version for each optional DLL feature. You can read the version resource cheaply without loading the DLL.

    It is perfectly possible, and rather simple, to obtain the list of functions exported by a DLL with loading it into your process with LoadLibrary. The dbghelp.dll system library provides services to do that. However, I suspect that is overkill for your situation.

    If it is not a problem to load and unload the DLL then GetProcAddress is probably the preferred solution. If there is some reason why you need to avoid loading the DLL in order to check capability, use the version resource to infer capability. If you need to do this with legacy DLLs that do not have a meaningful version resource then use dbghelp.dll to find the exported functions.


    For the sake of completeness, here is some code to read all the exported symbols from a DLL, without loading it with LoadLibrary.

    type
      PIMAGE_NT_HEADERS = ^IMAGE_NT_HEADERS;
      PIMAGE_EXPORT_DIRECTORY = ^IMAGE_EXPORT_DIRECTORY;
    
    function ImageNtHeader(Base: Pointer): PIMAGE_NT_HEADERS; stdcall; external 'dbghelp.dll';
    function ImageRvaToVa(NtHeaders: Pointer; Base: Pointer; Rva: ULONG; LastRvaSection: Pointer): Pointer; stdcall; external 'dbghelp.dll';
    
    procedure ImageExportedFunctionNames(const ImageName: string; NamesList: TStrings);
    var
      i: Integer;
      FileHandle: THandle;
      ImageHandle: THandle;
      ImagePointer: Pointer;
      Header: PIMAGE_NT_HEADERS;
      ExportTable: PIMAGE_EXPORT_DIRECTORY;
      NamesPointer: Pointer;
      Names: PAnsiChar;
      NamesDataLeft: Integer;
    begin
      //NOTE: our policy in this procedure is to exit upon any failure and return an empty list
    
      NamesList.Clear;
    
      FileHandle := CreateFile(
        PChar(ImageName),
        GENERIC_READ,
        FILE_SHARE_READ,
        nil,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        0
      );
      if FileHandle=INVALID_HANDLE_VALUE then begin
        exit;
      end;
      Try
        ImageHandle := CreateFileMapping(FileHandle, nil, PAGE_READONLY, 0, 0, nil);
        if ImageHandle=0 then begin
          exit;
        end;
        Try
          ImagePointer := MapViewOfFile(ImageHandle, FILE_MAP_READ, 0, 0, 0);
          if not Assigned(ImagePointer) then begin
            exit;
          end;
    
          Try
            Header := ImageNtHeader(ImagePointer);
            if not Assigned(Header) then begin
              exit;
            end;
            if Header.Signature<>$00004550 then begin // "PE\0\0" as a DWORD.
              exit;
            end;
    
            ExportTable := ImageRvaToVa(Header, ImagePointer, Header.OptionalHeader.DataDirectory[0].VirtualAddress, nil);
            if not Assigned(ExportTable) then begin
              exit;
            end;
    
            NamesPointer := ImageRvaToVa(Header, ImagePointer, Cardinal(ExportTable.AddressOfNames), nil);
            if not Assigned(NamesPointer) then begin
              exit;
            end;
            Names := ImageRvaToVa(Header, ImagePointer, Cardinal(NamesPointer^), nil);
            if not Assigned(Names) then begin
              exit;
            end;
    
            NamesDataLeft := Header.OptionalHeader.DataDirectory[0].Size;
            for i := 0 to ExportTable.NumberOfNames-1 do begin
              NamesList.Add(Names);
              // Locate the next name
              while (Names^<>chr(0)) and (NamesDataLeft>0) do begin
                inc(Names);
                dec(NamesDataLeft);
              end;
              inc(Names);
            end;
          Finally
            UnmapViewOfFile(ImagePointer); // Ignore error as there is not much we could do.
          End;
        Finally
          CloseHandle(ImageHandle);
        End;
      Finally
        CloseHandle(FileHandle);
      End;
    end;
    
    0 讨论(0)
  • 2020-12-10 08:33

    You have to use LoadLibrary, and then use GetProcAddress for each function you want to check existence for. There's really no other reasonable choice (unless there are specific reasons you need to avoid`LoadLibrary). Since your intent seems to be just to check to see if the functions are present and nothing more, LoadLibrary and GetProcAddress are the simplest means to do so; you can do all of the work in very few lines of code, and error checking is extremely simple and straightforward.

    0 讨论(0)
提交回复
热议问题