Estoy trabajando en algo que carga dinámicamente DLL especialmente formulados. Necesito poder verificar la DLL y asegurarme de que todas las funciones esperadas existen antes de considerar usar esta DLL. Si le faltan algunas funciones determinadas, no debería intentar cargarlo. Sé que podría intentar llamar a una de las funciones y ver si hay una excepción, pero vería errores en el modo de depuración.¿Cómo verificar una DLL si existe una función?
¿Cómo debo consultar una DLL si existe una función? Me gustaría comprobarlo antes de Lo cargo (usando LoadLibrary
) pero supongo que está bien si tengo que cargarlo para realizar esta comprobación también.
ACTUALIZACIÓN
He aceptado la respuesta de David a continuación, y pensé que había puesto mi código final para mostrar todo el proceso. Lo convertí en una función que devolvía un Bool, si tenía éxito o no, limpiaba un poco el código, y añadía otra función en la parte inferior que usa esta para verificar cada nombre uno por uno.
Decidí usar este método en lugar de leer GetProcAddress
porque me ayudará en el futuro con otras cosas.
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;
Parece prometedor, y cuando regrese a mi escritorio, lo intentaré: D Esta puede ser la manera de hacerlo, acepté demasiado pronto ... –
Bueno, este es un código probado. Pero, ¿qué te impide usar LoadLibrary y GetProcAddress? Sabiendo que podría ayudar. –
Porque de hecho tendré que averiguar cómo enumerar estas funciones para este proyecto de todos modos :) –