delphi – 如果函数存在,如何检查DLL?
我正在研究动态加载特殊配方DLL的东西.在考虑使用此DLL之前,我需要能够检查DLL并确保所有预期的功能都存在.如果它缺少某些功能,我不应该尝试加载它.我知道我可以尝试调用其中一个函数并查看是否存在异常,但我会在调试模式下看到错误.
如果函数存在,我该如何检查DLL?我想在加载它之前检查它(使用LoadLibrary),但我想如果我必须加载它来执行此检查也没关系. UPDATE 我在下面接受了David的回答,并且认为我会发布我的最终代码以显示整个过程.我把它变成了一个函数返回一个Bool,无论它是否成功,清理了一下代码,并在底部添加了另一个函数,使用这个函数逐个检查每个名字. 我决定使用这种方法而不是阅读GetProcAddress,因为它将来会帮助我解决其他问题. 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'; function ExportedFunctionNames(const ImageName: string; NamesList: TStrings): Bool; var i: Integer; FileHandle: THandle; ImageHandle: THandle; ImagePointer: Pointer; Header: PIMAGE_NT_HEADERS; ExportTable: PIMAGE_EXPORT_DIRECTORY; NamesPointer: Pointer; Names: PAnsiChar; NamesDataLeft: Integer; begin Result:= False; NamesList.Clear; FileHandle:= CreateFile(PChar(ImageName),GENERIC_READ,FILE_SHARE_READ,nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0); if FileHandle = INVALID_HANDLE_VALUE then Exit; try ImageHandle:= CreateFileMapping(FileHandle,PAGE_READONLY,nil); if ImageHandle = 0 then Exit; try ImagePointer:= MapViewOfFile(ImageHandle,FILE_MAP_READ,0); if not Assigned(ImagePointer) then Exit; try Header:= ImageNtHeader(ImagePointer); if not Assigned(Header) then Exit; if Header.Signature <> $00004550 then Exit; // "PE " as a DWORD. ExportTable:= ImageRvaToVa(Header,ImagePointer,Header.OptionalHeader.DataDirectory[0].VirtualAddress,nil); if not Assigned(ExportTable) then Exit; NamesPointer:= ImageRvaToVa(Header,Cardinal(ExportTable.AddressOfNames),nil); if not Assigned(NamesPointer) then Exit; Names:= ImageRvaToVa(Header,Cardinal(NamesPointer^),nil); if not Assigned(Names) then Exit; NamesDataLeft:= Header.OptionalHeader.DataDirectory[0].Size; for i:= 0 to ExportTable.NumberOfNames - 1 do begin NamesList.Add(Names); while (Names^ <> chr(0)) and (NamesDataLeft > 0) do begin Inc(Names); Dec(NamesDataLeft); end; Inc(Names); end; Result:= True; finally UnmapViewOfFile(ImagePointer); end; finally CloseHandle(ImageHandle); end; finally CloseHandle(FileHandle); end; end; function IsMyDLL(const Filename: String): Bool; var H: THandle; L: TStringList; function InList(const Func: String): Bool; begin Result:= L.IndexOf(Func) >= 0; end; begin Result:= False; L:= TStringList.Create; try if ExportedFunctionNames(Filename,L) then begin Result:=//Names of functions which need to exist InList('GetName') and InList('GetDescription') and InList('GetVersion') and InList('Start') and InList('Stop'); end; finally L.Free; end; end; 解决方法
如果您控制DLL并且您不想加载它们以检查功能,那么您可以使用版本资源来指示功能.这将要求主机应用程序知道每个可选DLL功能的最低支持版本.您可以廉价地读取版本资源而无需加载DLL.
获取DLL导出的函数列表并使用LoadLibrary将其加载到进程中是完全可能的,而且非常简单. dbghelp.dll系统库提供了执行此操作的服务.但是,我怀疑这对你的情况来说太过分了. 如果加载和卸载DLL不是问题,那么GetProcAddress可能是首选的解决方案.如果有一些原因需要避免加载DLL以检查功能,请使用版本资源来推断功能.如果需要对没有有意义版本资源的旧DLL执行此操作,请使用dbghelp.dll查找导出的函数. 为了完整起见,这里有一些代码可以从DLL中读取所有导出的符号,而无需使用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),0 ); if FileHandle=INVALID_HANDLE_VALUE then begin exit; end; Try ImageHandle := CreateFileMapping(FileHandle,nil); if ImageHandle=0 then begin exit; end; Try ImagePointer := MapViewOfFile(ImageHandle,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 " as a DWORD. exit; end; ExportTable := ImageRvaToVa(Header,nil); if not Assigned(ExportTable) then begin exit; end; NamesPointer := ImageRvaToVa(Header,nil); if not Assigned(NamesPointer) then begin exit; end; Names := ImageRvaToVa(Header,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; (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |