dynlibs.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 1999-2000 by the Free Pascal development team
  4. Implements OS-independent loading of dynamic libraries.
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {$IFDEF FPC}
  12. {$MODE OBJFPC}
  13. {$ENDIF}
  14. unit dynlibs;
  15. interface
  16. uses
  17. SysUtils, RtlConsts, SysConst;
  18. { ---------------------------------------------------------------------
  19. Read OS-dependent interface declarations.
  20. ---------------------------------------------------------------------}
  21. {$define readinterface}
  22. {$i dynlibs.inc}
  23. {$undef readinterface}
  24. { ---------------------------------------------------------------------
  25. OS - Independent declarations.
  26. ---------------------------------------------------------------------}
  27. Function SafeLoadLibrary(const Name : AnsiString) : TLibHandle;
  28. Function LoadLibrary(const Name : AnsiString) : TLibHandle;
  29. Function GetProcedureAddress(Lib : TlibHandle; const ProcName : AnsiString) : Pointer;
  30. Function UnloadLibrary(Lib : TLibHandle) : Boolean;
  31. // Kylix/Delphi compability
  32. Type
  33. HModule = TLibHandle;
  34. Function FreeLibrary(Lib : TLibHandle) : Boolean;
  35. Function GetProcAddress(Lib : TlibHandle; const ProcName : AnsiString) : Pointer;
  36. // Dynamic Library Manager
  37. { Note: If you look for some code that uses this library handler, take a look at
  38. sqlite3.inc of sqlite package (simple) or
  39. mysql.inc of mysql package (advanced)
  40. }
  41. type
  42. PLibHandler = ^TLibHandler;
  43. TLibEventLoading = function(User: Pointer; Handler: PLibHandler): Boolean;
  44. TLibEventUnloading = procedure(Handler: PLibHandler);
  45. TLibIdent = QWord;
  46. TLibIdentGetter = function(const Filename: String): TLibIdent;
  47. PPLibSymbol = ^PLibSymbol;
  48. PLibSymbol = ^TLibSymbol;
  49. TLibSymbol = record
  50. pvar: PPointer; { pointer to Symbol variable }
  51. name: String; { name of the Symbol }
  52. weak: Boolean; { weak }
  53. end;
  54. PLibSymbolPtrArray = ^TLibSymbolPtrArray;
  55. TLibSymbolPtrArray = array of PLibSymbol;
  56. TLibHandler = record
  57. InterfaceName: String; { abstract name of the library }
  58. Defaults : array of String; { list of default library filenames }
  59. Filename : String; { filename of the current loaded library }
  60. Handle : TLibHandle; { handle of the current loaded library }
  61. Loading : TLibEventLoading; { loading event, called after the unit is loaded }
  62. Unloading : TLibEventUnloading; { unloading event, called before the unit is unloaded }
  63. IdentGetter : TLibIdentGetter; { identifier getter event }
  64. Ident : TLibIdent; { identifier of the current loaded library }
  65. SymCount : Integer; { number of symbols }
  66. Symbols : PLibSymbol; { symbol address- and namelist }
  67. ErrorMsg : String; { last error message }
  68. RefCount : Integer; { reference counter }
  69. end;
  70. { handler definition }
  71. function LibraryHandler(const InterfaceName: String; const DefaultLibraries: array of String;
  72. const Symbols: PLibSymbol; const SymCount: Integer; const AfterLoading: TLibEventLoading = nil;
  73. const BeforeUnloading: TLibEventUnloading = nil; const IdentGetter: TLibIdentGetter = nil): TLibHandler;
  74. { initialization/finalization }
  75. function TryInitializeLibrary(var Handler: TLibHandler; const LibraryNames: array of String;
  76. const User: Pointer = nil; const NoSymbolErrors: Boolean = True): Integer;
  77. function TryInitializeLibrary(var Handler: TLibHandler; const LibraryName: String = '';
  78. const User: Pointer = nil; const NoSymbolErrors: Boolean = True): Integer;
  79. function InitializeLibrary(var Handler: TLibHandler; const LibraryNames: array of String;
  80. const User: Pointer = nil; const NoSymbolErrors: Boolean = True): Integer;
  81. function InitializeLibrary(var Handler: TLibHandler; const LibraryName: String = '';
  82. const User: Pointer = nil; const NoSymbolErrors: Boolean = True): Integer;
  83. function ReleaseLibrary(var Handler: TLibHandler): Integer;
  84. { errors }
  85. procedure AppendLibraryError(var Handler: TLibHandler; const Msg: String);
  86. function GetLastLibraryError(var Handler: TLibHandler): String;
  87. procedure RaiseLibraryException(var Handler: TLibHandler);
  88. { symbol load/clear }
  89. function LoadLibrarySymbols(const Lib: TLibHandle; const Symbols: PLibSymbol; const Count: Integer;
  90. const ErrorSymbols: PLibSymbolPtrArray = nil): Boolean;
  91. procedure ClearLibrarySymbols(const Symbols: PLibSymbol; const Count: Integer);
  92. Implementation
  93. { ---------------------------------------------------------------------
  94. OS - Independent declarations.
  95. ---------------------------------------------------------------------}
  96. {$i dynlibs.inc}
  97. Function FreeLibrary(Lib : TLibHandle) : Boolean;
  98. begin
  99. Result:=UnloadLibrary(lib);
  100. end;
  101. Function GetProcAddress(Lib : TlibHandle; const ProcName : AnsiString) : Pointer;
  102. begin
  103. Result:=GetProcedureAddress(Lib,Procname);
  104. end;
  105. Function SafeLoadLibrary(const Name : AnsiString) : TLibHandle;
  106. {$ifdef i386}
  107. var w : word;
  108. {$endif}
  109. Begin
  110. {$ifdef i386}
  111. w:=get8087cw;
  112. {$endif}
  113. result:=loadlibrary(name);
  114. {$ifdef i386}
  115. set8087cw(w);
  116. {$endif}
  117. End;
  118. function LibraryHandler(const InterfaceName: String; const DefaultLibraries: array of String;
  119. const Symbols: PLibSymbol; const SymCount: Integer; const AfterLoading: TLibEventLoading;
  120. const BeforeUnloading: TLibEventUnloading; const IdentGetter: TLibIdentGetter): TLibHandler;
  121. var
  122. I: Integer;
  123. begin
  124. Result.InterfaceName := InterfaceName;
  125. Result.Filename := '';
  126. Result.Handle := NilHandle;
  127. Result.Loading := AfterLoading;
  128. Result.Unloading := BeforeUnloading;
  129. Result.IdentGetter := IdentGetter;
  130. Result.Ident := 0;
  131. Result.SymCount := SymCount;
  132. Result.Symbols := Symbols;
  133. Result.ErrorMsg := '';
  134. Result.RefCount := 0;
  135. SetLength(Result.Defaults, Length(DefaultLibraries));
  136. for I := 0 to High(DefaultLibraries) do
  137. Result.Defaults[I] := DefaultLibraries[I];
  138. end;
  139. function TryInitializeLibraryInternal(var Handler: TLibHandler; const LibraryName: String;
  140. const User: Pointer; const NoSymbolErrors: Boolean): Integer;
  141. var
  142. ErrSyms: TLibSymbolPtrArray;
  143. NewIdent: TLibIdent;
  144. I: Integer;
  145. begin
  146. if Handler.Filename <> '' then
  147. begin
  148. if Assigned(Handler.IdentGetter) then
  149. begin
  150. NewIdent := Handler.IdentGetter(LibraryName);
  151. if NewIdent <> Handler.Ident then
  152. begin
  153. AppendLibraryError(Handler, Format(SLibraryAlreadyLoaded, [Handler.InterfaceName, Handler.Filename]));
  154. Result := -1;
  155. Exit;
  156. end;
  157. end;
  158. end;
  159. Result := InterlockedIncrement(Handler.RefCount);
  160. if Result = 1 then
  161. begin
  162. Handler.Handle := LoadLibrary(LibraryName);
  163. if Handler.Handle = NilHandle then
  164. begin
  165. AppendLibraryError(Handler, Format(SLibraryNotLoaded, [Handler.InterfaceName, LibraryName]));
  166. Handler.RefCount := 0;
  167. Result := -1;
  168. Exit;
  169. end;
  170. Handler.Filename := LibraryName;
  171. if not LoadLibrarySymbols(Handler.Handle, Handler.Symbols, Handler.SymCount, @ErrSyms) and not NoSymbolErrors then
  172. begin
  173. for I := 0 to Length(ErrSyms) - 1 do
  174. AppendLibraryError(Handler, Format(SLibraryUnknownSym, [ErrSyms[I]^.name, Handler.InterfaceName, LibraryName]));
  175. UnloadLibrary(Handler.Handle);
  176. Handler.Handle := NilHandle;
  177. Handler.Filename := '';
  178. Handler.RefCount := 0;
  179. Result := -1;
  180. Exit;
  181. end;
  182. if Assigned(Handler.Loading) and not Handler.Loading(User, @Handler) then
  183. begin
  184. UnloadLibrary(Handler.Handle);
  185. Handler.Handle := NilHandle;
  186. Handler.Filename := '';
  187. Handler.RefCount := 0;
  188. Result := -1;
  189. Exit;
  190. end;
  191. if Assigned(Handler.IdentGetter) then
  192. Handler.Ident := Handler.IdentGetter(Handler.Filename)
  193. else
  194. Handler.Ident := 0;
  195. end;
  196. end;
  197. function TryInitializeLibrary(var Handler: TLibHandler; const LibraryName: String;
  198. const User: Pointer; const NoSymbolErrors: Boolean): Integer;
  199. begin
  200. if LibraryName <> '' then
  201. begin
  202. Handler.ErrorMsg := '';
  203. Result := TryInitializeLibraryInternal(Handler, LibraryName, User, NoSymbolErrors);
  204. end else
  205. Result := TryInitializeLibrary(Handler, Handler.Defaults, User, NoSymbolErrors);
  206. end;
  207. function TryInitializeLibrary(var Handler: TLibHandler; const LibraryNames: array of String;
  208. const User: Pointer; const NoSymbolErrors: Boolean): Integer;
  209. var
  210. I: Integer;
  211. begin
  212. Handler.ErrorMsg := '';
  213. if Length(LibraryNames) <= 0 then
  214. begin
  215. if Length(Handler.Defaults) > 0 then
  216. begin
  217. Result := TryInitializeLibrary(Handler, Handler.Defaults, User, NoSymbolErrors);
  218. Exit;
  219. end;
  220. AppendLibraryError(Handler, SVarInvalid);
  221. Result := -1;
  222. Exit;
  223. end;
  224. for I := 0 to High(LibraryNames) do
  225. begin
  226. Result := TryInitializeLibraryInternal(Handler, LibraryNames[I], User, NoSymbolErrors);
  227. if Result > 0 then
  228. begin
  229. Handler.ErrorMsg := '';
  230. Exit;
  231. end;
  232. end;
  233. end;
  234. function InitializeLibrary(var Handler: TLibHandler; const LibraryNames: array of String;
  235. const User: Pointer; const NoSymbolErrors: Boolean): Integer;
  236. begin
  237. Result := TryInitializeLibrary(Handler, LibraryNames, User, NoSymbolErrors);
  238. if Result < 0 then
  239. RaiseLibraryException(Handler);
  240. end;
  241. function InitializeLibrary(var Handler: TLibHandler; const LibraryName: String;
  242. const User: Pointer; const NoSymbolErrors: Boolean): Integer;
  243. begin
  244. Result := TryInitializeLibrary(Handler, LibraryName, User, NoSymbolErrors);
  245. if Result < 0 then
  246. RaiseLibraryException(Handler);
  247. end;
  248. function ReleaseLibrary(var Handler: TLibHandler): Integer;
  249. begin
  250. Handler.ErrorMsg := '';
  251. Result := InterlockedDecrement(Handler.RefCount);
  252. if Result = 0 then
  253. begin
  254. if Assigned(Handler.Unloading) then
  255. Handler.Unloading(@Handler);
  256. ClearLibrarySymbols(Handler.Symbols, Handler.SymCount);
  257. UnloadLibrary(Handler.Handle);
  258. Handler.Handle := NilHandle;
  259. Handler.Filename := '';
  260. Handler.Ident := 0;
  261. end else
  262. if Result < 0 then
  263. Handler.RefCount := 0;
  264. end;
  265. procedure AppendLibraryError(var Handler: TLibHandler; const Msg: String);
  266. begin
  267. if Handler.ErrorMsg <> '' then
  268. Handler.ErrorMsg := Handler.ErrorMsg + LineEnding + Msg
  269. else
  270. Handler.ErrorMsg := Msg;
  271. end;
  272. function GetLastLibraryError(var Handler: TLibHandler): String;
  273. begin
  274. Result := Handler.ErrorMsg;
  275. Handler.ErrorMsg := '';
  276. end;
  277. procedure RaiseLibraryException(var Handler: TLibHandler);
  278. var
  279. Msg: String;
  280. begin
  281. Msg := GetLastLibraryError(Handler);
  282. if Msg <> '' then
  283. raise EInOutError.Create(Msg)
  284. else
  285. raise EInOutError.Create(SUnknown);
  286. end;
  287. function LoadLibrarySymbols(const Lib: TLibHandle; const Symbols: PLibSymbol; const Count: Integer;
  288. const ErrorSymbols: PLibSymbolPtrArray): Boolean;
  289. var
  290. P,L: PLibSymbol;
  291. Len: Integer;
  292. begin
  293. P := Symbols;
  294. L := @Symbols[Count];
  295. while P < L do
  296. begin
  297. P^.pvar^ := GetProcedureAddress(Lib, P^.name);
  298. if not Assigned(P^.pvar^) and not P^.weak then
  299. begin
  300. if Assigned(ErrorSymbols) then
  301. begin
  302. Len := Length(ErrorSymbols^);
  303. SetLength(ErrorSymbols^, Len+1);
  304. ErrorSymbols^[Len] := P;
  305. end;
  306. Result := False;
  307. end;
  308. Inc(P);
  309. end;
  310. Result := True;
  311. end;
  312. procedure ClearLibrarySymbols(const Symbols: PLibSymbol; const Count: Integer);
  313. var
  314. P,L: PLibSymbol;
  315. begin
  316. P := Symbols;
  317. L := @Symbols[Count];
  318. while P < L do
  319. begin
  320. P^.pvar^ := nil;
  321. Inc(P);
  322. end;
  323. end;
  324. end.