Dynamic Dll Loading
Posted: Thu Feb 01, 2007 4:42 am
I have been experimenting with a way to dynamically load a DLL and its entrypoints on the fly with error checking instead of implicit linking. I didn't like how if I was to do this, I would have to write 100's of stubs that called LoadLibrary and GetProcAddress... so I came up with this
Note: requires some assembler understanding
Yes, I am translating a freeglut header
The idea is to have one generic function that loads the library if it is not loaded, find the proc address, and if it doesnt exist then throw a detailed exception.
so, using this example... calling glutInit would follow this flow:
glutInit -> CallTable + 0 -> DllFunc -> Real glutInit
Each entry in CallTable saves the registers, pushes the function name onto the stack, and calls DllFunc, if DllFunc falls through because the dll could not be loaded, it restores the registers and returns. If DllFunc succeeds, then the stack is restored to the state it was in when CallTable was called, and then it jumps to the function inside the dll.
DllFunc will only return if the dll/proc wasnt found, and CallTable will only return if DllFunc returned.
Hope this all makes sence, it seems to work really well and saves heaps of duplicated code.
Share your thoughts/suggestions/ideas... I would be interested to know what others think.
Note: requires some assembler understanding
Code: Select all
unit uFreeglut;
interface
uses Windows, SysUtils, OpenGL;
const
freeglut = 'freeglut.dll';
type
TglutInit = procedure(var pargc: Integer; var argv: PChar); cdecl;
TglutInitWindowPosition = procedure(x, y: Integer); cdecl;
var
glutInit: TglutInit;
glutInitWindowPosition: TglutInitWindowPosition;
implementation
procedure FailedToCall(Proc: PChar);
begin
raise Exception.Create('Unable to find the procedure ''' + Proc + ''' in the dll ''' + freeglut + '''');
end;
//Jumps to the procedure in the dll after dynamically loading it
procedure DllFunc(Proc: PChar); cdecl;
var
hDll: HINST;
P : Pointer;
begin
//Get/Load the Dll
hDll := GetModuleHandle(freeglut);
if hDll = 0 then hDll := LoadLibrary(freeglut);
P := GetProcAddress(hDll, Proc); //Get the proc address
if P = nil then FailedToCall(Proc) //Check if the proc was found
else
begin
asm
push P //Push the proc address onto the stack
add esp, 7*4 //Restore ESP to our saved registers
popad //Restore the registers
jmp dword ptr [esp-(15*4)] //Jump to the real function
end;
//We should never get here since we are jumping into the function
raise Exception.Create('Fatal Error');
end;
end;
{
Lookup Table
We use this instead of stubs because delphi likes to add asm to functions
with params that we are forced to clean up, this way the asm is pure and
we always know what state the stack is in.
pushad //Save the registers
push _XX //Push the name of the function onto the stack
call DllFunc //Call the generic dll handler
//only gets here if dll loading failed, DllFunc does not return.
popad //Restore the registers and the stack
ret //Return
Each entry is exactly 14 bytes long
}
procedure CallTable();
const
_000: PChar = 'glutInit';
_001: PChar = 'glutInitWindowPosition';
asm
pushad; push _000; call DllFunc; popad; ret;
pushad; push _001; call DllFunc; popad; ret;
end;
initialization
glutInit := Pointer(Integer(@CallTable) + (14*000));
glutInitWindowPosition := Pointer(Integer(@CallTable) + (14*001));
end.
The idea is to have one generic function that loads the library if it is not loaded, find the proc address, and if it doesnt exist then throw a detailed exception.
so, using this example... calling glutInit would follow this flow:
glutInit -> CallTable + 0 -> DllFunc -> Real glutInit
Each entry in CallTable saves the registers, pushes the function name onto the stack, and calls DllFunc, if DllFunc falls through because the dll could not be loaded, it restores the registers and returns. If DllFunc succeeds, then the stack is restored to the state it was in when CallTable was called, and then it jumps to the function inside the dll.
DllFunc will only return if the dll/proc wasnt found, and CallTable will only return if DllFunc returned.
Hope this all makes sence, it seems to work really well and saves heaps of duplicated code.
Share your thoughts/suggestions/ideas... I would be interested to know what others think.