Jer*_*dge 5 delphi dll loadlibrary delphi-xe2
我正在研究动态加载特殊配方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, nil, PAGE_READONLY, 0, 0, nil);
if ImageHandle = 0 then Exit;
try
ImagePointer:= MapViewOfFile(ImageHandle, FILE_MAP_READ, 0, 0, 0);
if not Assigned(ImagePointer) then Exit;
try
Header:= ImageNtHeader(ImagePointer);
if not Assigned(Header) then Exit;
if Header.Signature <> $00004550 then Exit; // "PE\0\0" as a DWORD.
ExportTable:= ImageRvaToVa(Header, ImagePointer,
Header.OptionalHeader.DataDirectory[0].VirtualAddress, nil);
if not Assigned(ExportTable) then Exit;
NamesPointer:= ImageRvaToVa(Header, ImagePointer,
Cardinal(ExportTable.AddressOfNames), nil);
if not Assigned(NamesPointer) then Exit;
Names:= ImageRvaToVa(Header, ImagePointer, 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;
Run Code Online (Sandbox Code Playgroud)
Ken*_*ite 10
您必须使用LoadLibrary,然后使用GetProcAddress您要检查存在的每个功能.实际上没有其他合理的选择(除非有特殊原因需要避免`LoadLibrary).因为你的意图似乎只是检查功能是否存在而且仅此而已,LoadLibrary并且GetProcAddress是最简单的方法; 你可以用很少的代码行完成所有的工作,错误检查非常简单直接.
如果您控制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),
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;
Run Code Online (Sandbox Code Playgroud)