User talk:ConradPino/Christian Wimmer/Dynamic Function Invocation Update/Archive
This is a talk page archive.
Contents
Design Offer JediAPILib.inc Initial Proposal
{$IFDEF DELPHI2010_UP} {$IFDEF DYNAMIC_LINK} {$UNDEF DYNAMIC_LINK} {$DEFINE DELAYED_LINKING} {$ENDIF DYNAMIC_LINK} {$ENDIF}
Archived here for Christian Wimmer – Conrad T. Pino 22:14, 1 February 2010 (UTC)
DELPHI2010_UP | DELAYED_LINKING | DYNAMIC_LINK | Link Method |
---|---|---|---|
False | False | False | Static |
False | False | True | Dynamic |
False | True | False | Compile Error |
False | True, State Error! | True | Dynamic |
True | False | False | Static |
True | False, Defined | True, Undefined | Delayed |
True | True | False | Delayed |
True | True, Defined | True, Undefined | Delayed |
Add Comments Here
Invocation Analysis Case Optimized With Arguments
Optimized with arguments example with instructions generated included as comments:
var _TestFunctionReal: TJwaDynamicProcedureRecord; function TestFunctionReal( const Index: Integer; const Value: PChar ): Integer; stdcall; asm // push stack frame here // push ebp // mov ebp,esp POP EBP // pop ebp JMP [_TestFunctionReal.ProcedureAddress] // jmp dword ptr [_TestFunctionReal] end; // pop stack frame here // pop ebp // ret 8
Error handling function and initialization:
function _TestFunctionRealError( const Index: Integer; const Value: PChar ): Integer; stdcall; begin Result := 0; GetProcedureAddressError( _TestFunctionReal ); end; initialization GetProcedureAddress( _TestFunctionReal, @_TestFunctionRealError, LibReal, 'TestFunctionReal' );
Conrad T. Pino 11:43, 2 February 2010 (UTC)
Invocation Analysis Case Optimized Without Arguments
Optimized without arguments example with instructions generated included as comments:
var _TestFunction: TJwaDynamicProcedureRecord; function TestFunction: Integer; stdcall; asm // stack frame not pushed JMP [_TestFunction.ProcedureAddress] // jmp dword ptr [_TestFunction] end; // stack frame not popped // ret
Error handling function and initialization:
function _TestFunctionError: Integer; stdcall; begin Result := 0; GetProcedureAddressError( _TestFunction ); end; initialization GetProcedureAddress( _TestFunction, @_TestFunctionError, LibReal, 'TestFunction' );
Conrad T. Pino 11:50, 2 February 2010 (UTC)
JwaWinType GetProcedureAddress Optimization
There is a another way : User_talk:ChristianWimmer/GetProcedureAddress_Improvements
New Public Types
type EJwaErrorClass = class of EJwaError; TJwaDynamicProcedureRecord = record ProcedureAddress: Pointer; ExceptionClass: EJwaErrorClass; ExceptionMessage: AnsiString; end;
New Public Procedures
procedure GetProcedureAddress(var DynProcRec: TJwaDynamicProcedureRecord; const ErrProc: Pointer; const ModuleName, ProcName: AnsiString); overload; procedure GetProcedureAddress(var DynProcRec: TJwaDynamicProcedureRecord; const ErrProc: Pointer; const ModuleName : AnsiString; const ProcNumber : Cardinal); overload; procedure GetProcedureAddressError(const DynProcRec: TJwaDynamicProcedureRecord);
New Private Procedures
function JwaGetModuleHandle(const ModuleName: AnsiString): HMODULE; overload; function JwaGetModuleHandle(var DynProcRec: TJwaDynamicProcedureRecord; const ErrProc: Pointer; const ModuleName: AnsiString): HMODULE; overload; function JwaGetProcAddress(const Handle: HMODULE; const ProcName: AnsiString): FARPROC; overload; function JwaGetProcAddress(const Handle: HMODULE; const ProcNumber: Cardinal): FARPROC; overload;
JwaWinType Excerpts
interface
type EJwaError = class(Exception); EJwaLoadLibraryError = class(EJwaError); EJwaGetProcAddressError = class(EJwaError); EJwaErrorClass = class of EJwaError; TJwaDynamicProcedureRecord = record ProcedureAddress: Pointer; ExceptionClass: EJwaErrorClass; ExceptionMessage: AnsiString; end; procedure GetProcedureAddress(var P: Pointer; const ModuleName, ProcName: AnsiString); overload; procedure GetProcedureAddress(var P: Pointer; const ModuleName : AnsiString; const ProcNumber : Cardinal); overload; procedure GetProcedureAddress(var DynProcRec: TJwaDynamicProcedureRecord; const ErrProc: Pointer; const ModuleName, ProcName: AnsiString); overload; procedure GetProcedureAddress(var DynProcRec: TJwaDynamicProcedureRecord; const ErrProc: Pointer; const ModuleName : AnsiString; const ProcNumber : Cardinal); overload; procedure GetProcedureAddressError(const DynProcRec: TJwaDynamicProcedureRecord);
implementation
function JwaWinType_GetModuleHandle(lpModuleName: LPCSTR): HMODULE; stdcall; external kernel32 name 'GetModuleHandleA'; function JwaWinType_LoadLibrary(lpLibFileName: LPCSTR): HMODULE; stdcall; external kernel32 name 'LoadLibraryA'; function JwaWinType_GetProcAddress(hModule: HMODULE; lpProcName: LPCSTR): FARPROC; stdcall; external kernel32 name 'GetProcAddress'; const RsELibraryNotFound = 'Library not found: %0:s'; RsEFunctionNotFound = 'Function not found: %0:s.%1:s'; RsEFunctionNotFound2 = 'Function not found: %0:s.%1:d'; function JwaGetModuleHandle(const ModuleName: AnsiString): HMODULE; overload; begin Result := jwaWinType_GetModuleHandle(PAnsiChar(ModuleName)); if Result = 0 then begin Result := jwaWinType_LoadLibrary(PAnsiChar(ModuleName)); end; end; function JwaGetModuleHandle(var DynProcRec: TJwaDynamicProcedureRecord; const ErrProc: Pointer; const ModuleName: AnsiString): HMODULE; overload; begin Result := JwaGetModuleHandle(ModuleName); if Result = 0 then begin DynProcRec.ExceptionClass := EJwaLoadLibraryError; DynProcRec.ExceptionMessage := Format(RsELibraryNotFound, [ModuleName]); DynProcRec.ProcedureAddress := ErrProc; end; end; function JwaGetProcAddress(const Handle: HMODULE; const ProcName: AnsiString): FARPROC; overload; begin Result := jwaWinType_GetProcAddress(Handle, PAnsiChar(ProcName)); end; function JwaGetProcAddress(const Handle: HMODULE; const ProcNumber: Cardinal): FARPROC; overload; begin Result := jwaWinType_GetProcAddress(Handle, PAnsiChar(ProcNumber)); end; procedure GetProcedureAddress(var P: Pointer; const ModuleName, ProcName: AnsiString); var ModuleHandle: HMODULE; begin if not Assigned(P) then begin ModuleHandle := JwaGetModuleHandle(ModuleName); if ModuleHandle = 0 then raise EJwaLoadLibraryError.CreateFmt(RsELibraryNotFound, [ModuleName]); P := JwaGetProcAddress(ModuleHandle, ProcName); if not Assigned(P) then raise EJwaGetProcAddressError.CreateFmt(RsEFunctionNotFound, [ModuleName, ProcName]); end; end; procedure GetProcedureAddress(var P: Pointer; const ModuleName : AnsiString; const ProcNumber : Cardinal); var ModuleHandle: HMODULE; begin if not Assigned(P) then begin ModuleHandle := JwaGetModuleHandle(ModuleName); if ModuleHandle = 0 then raise EJwaLoadLibraryError.CreateFmt(RsELibraryNotFound, [ModuleName]); P := JwaGetProcAddress(ModuleHandle, ProcNumber); if not Assigned(P) then raise EJwaGetProcAddressError.CreateFmt(RsEFunctionNotFound2, [ModuleName, ProcNumber]); end; end; procedure GetProcedureAddress(var DynProcRec: TJwaDynamicProcedureRecord; const ErrProc: Pointer; const ModuleName, ProcName: AnsiString); overload; var ModuleHandle: HMODULE; begin ModuleHandle := JwaGetModuleHandle(DynProcRec, ErrProc, ModuleName); if ModuleHandle = 0 then Exit; DynProcRec.ProcedureAddress := JwaGetProcAddress(ModuleHandle, ProcName); if not Assigned( DynProcRec.ProcedureAddress ) then begin DynProcRec.ExceptionClass := EJwaGetProcAddressError; DynProcRec.ExceptionMessage := Format(RsEFunctionNotFound, [ModuleName, ProcName]); DynProcRec.ProcedureAddress := ErrProc; end; end; procedure GetProcedureAddress(var DynProcRec: TJwaDynamicProcedureRecord; const ErrProc: Pointer; const ModuleName : AnsiString; const ProcNumber : Cardinal); overload; var ModuleHandle: HMODULE; begin ModuleHandle := JwaGetModuleHandle(DynProcRec, ErrProc, ModuleName); if ModuleHandle = 0 then Exit; DynProcRec.ProcedureAddress := JwaGetProcAddress(ModuleHandle, ProcNumber); if not Assigned( DynProcRec.ProcedureAddress ) then begin DynProcRec.ExceptionClass := EJwaGetProcAddressError; DynProcRec.ExceptionMessage := Format(RsEFunctionNotFound2, [ModuleName, ProcNumber]); DynProcRec.ProcedureAddress := ErrProc; end; end; procedure GetProcedureAddressError(const DynProcRec: TJwaDynamicProcedureRecord); begin with DynProcRec do raise ExceptionClass.Create( ExceptionMessage ); end;
Conrad T. Pino 12:18, 2 February 2010 (UTC)
Add Comments Here
Conrad T. Pino 12:58, 2 February 2010 (UTC):
- There's no downside to extending the current
GetProcedureAddress
implementation to add the three (3) optimization procedures. - Current
GetProcedureAddress
implementation should NOT be deprecated as it remains useful where aninitialization
section is undesirable. - There's no benefit to using conditional compilation to change procedure aliases, example
GetModuleHandle
orJwaWinType_GetModuleHandle
.
- There's no downside to extending the current
ChristianWimmer 14:24, 2 February 2010 (UTC):
- Well, this approach has several issues:
- We don't know if it still works with 64bit. The asm registers must be 64 wide which usually mean to use rsp and rbp instead of esp and ebp. I don't know how this is done in 64bit FP.
- Microsoft has removed Assembler for 64bit in their compiler. Instead they introduced Compiler Intrinsics. If Embarcadero decides to do the same we have the same situation again.
- You are loading the function in the initialization. That is like static function loading. If you do this for all other functions, the application will load all functions that are declared in this way, even if they are not used. Delphi won't remove them because it is executed code. Furthermore the binary will take a lot of time to be loaded. If all functions are loaded this way I even think that the binary cannot be loaded into memory.
- You are doing the nearly same thing as Delphi 2010 does if the reserverd word "delayed" is used. First the function is a stub that contains a loading call which then moves the function pointer itself to another location: to the real function. I have reversed this process in CPU window and it is very fast (also read the discussion "api stubs and win x64" in jedi.apiconversion) However, your are introducing new code which must be added to all ~20.000 functions in the JEDI API. That is a work which cannot be done by hand. Furthermore additional code means that the produced binary file increases in size. Even today people still care about their exe file size.
Conrad T. Pino 17:42, 2 February 2010 (UTC):
- I shall follow your points strictly:
- We don't know if it still works with 64bit. Agreed AND this applies to the current implementation as well. Since Native 64-bit Code implementation is unknowable all we can do is proceed without considering 64-bit.
- Microsoft has removed Assembler ... If Embarcadero decides to do the same ... Again, all we can do is proceed without considering 64-bit.
- ... is like static function loading. Agreed after a good sleep however it's compelling only for
JwaWindows
but not compelling enough to continue. - ... work which cannot be done by hand. I never planned to do the using units by hand. ... binary file increases in size. Small compared to 64-bit! LOL
- Conclusion:
- As a learning exercise this was very worthwhile.
- There is no compelling reason to do this now.
ChristianWimmer 17:57, 2 February 2010 (UTC):
- Well there is a reason to do so. As I suggested we can still use "delayed" to make it work on 64 for future Delphi because in this way Emb. has to deal with compatibility.
- Of course it doesn't solve it for FP until they support "delayed". It would be interesting how FP 64 does it.
ChristianWimmer 11:16, 3 February 2010 (UTC):
initialization GetProcedureAddress( _TestFunctionReal, @_TestFunctionRealError, LibReal, 'TestFunctionReal' );
- Sorry, but this looks wrong to me. As I already have pointed out, we cannot init all functions at once. The execution of the binary would take too long and needed disk space would explode since it is all dead code that is not removed by Delphi.
- This is a no go reason for me.
Conrad T. Pino 18:24, 3 February 2010 (UTC): Your 14:24, 2 February 2010 (UTC) reply was persuasive; therefore I tabled and archived this topic.