dynlibs.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  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(Name : AnsiString) : TLibHandle;
  28. Function LoadLibrary(Name : AnsiString) : TLibHandle;
  29. Function GetProcedureAddress(Lib : TlibHandle; 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; 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. TLibHandler = record
  55. InterfaceName: String; { abstract name of the library }
  56. Defaults : array of String; { list of default library filenames }
  57. Filename : String; { filename of the current loaded library }
  58. Handle : TLibHandle; { handle of the current loaded library }
  59. Loading : TLibEventLoading; { loading event, called after the unit is loaded }
  60. Unloading : TLibEventUnloading; { unloading event, called before the unit is unloaded }
  61. IdentGetter : TLibIdentGetter; { identifier getter event }
  62. Ident : TLibIdent; { identifier of the current loaded library }
  63. SymCount : Integer; { number of symbols }
  64. Symbols : PLibSymbol; { symbol address- and namelist }
  65. ErrorMsg : String; { last error message }
  66. RefCount : Integer; { reference counter }
  67. end;
  68. { handler definition }
  69. function LibraryHandler(const InterfaceName: String; const DefaultLibraries: array of String;
  70. const Symbols: PLibSymbol; const SymCount: Integer; const AfterLoading: TLibEventLoading = nil;
  71. const BeforeUnloading: TLibEventUnloading = nil; const IdentGetter: TLibIdentGetter = nil): TLibHandler;
  72. { initialization/finalization }
  73. function TryInitializeLibrary(var Handler: TLibHandler; const LibraryNames: array of String;
  74. const User: Pointer = nil; const NoSymbolErrors: Boolean = False): Integer;
  75. function TryInitializeLibrary(var Handler: TLibHandler; const LibraryName: String = '';
  76. const User: Pointer = nil; const NoSymbolErrors: Boolean = False): Integer;
  77. function InitializeLibrary(var Handler: TLibHandler; const LibraryNames: array of String;
  78. const User: Pointer = nil; const NoSymbolErrors: Boolean = False): Integer;
  79. function InitializeLibrary(var Handler: TLibHandler; const LibraryName: String = '';
  80. const User: Pointer = nil; const NoSymbolErrors: Boolean = False): Integer;
  81. function ReleaseLibrary(var Handler: TLibHandler): Integer;
  82. { errors }
  83. procedure AppendLibraryError(var Handler: TLibHandler; const Msg: String);
  84. function GetLastLibraryError(var Handler: TLibHandler): String;
  85. procedure RaiseLibraryException(var Handler: TLibHandler);
  86. { symbol load/clear }
  87. function LoadLibrarySymbols(const Lib: TLibHandle; const Symbols: PLibSymbol; const Count: Integer;
  88. const ErrorSym: PPLibSymbol = nil): Boolean;
  89. procedure ClearLibrarySymbols(const Symbols: PLibSymbol; const Count: Integer);
  90. Implementation
  91. { ---------------------------------------------------------------------
  92. OS - Independent declarations.
  93. ---------------------------------------------------------------------}
  94. {$i dynlibs.inc}
  95. Function FreeLibrary(Lib : TLibHandle) : Boolean;
  96. begin
  97. Result:=UnloadLibrary(lib);
  98. end;
  99. Function GetProcAddress(Lib : TlibHandle; ProcName : AnsiString) : Pointer;
  100. begin
  101. Result:=GetProcedureAddress(Lib,Procname);
  102. end;
  103. Function SafeLoadLibrary(Name : AnsiString) : TLibHandle;
  104. {$ifdef i386}
  105. var w : word;
  106. {$endif}
  107. Begin
  108. {$ifdef i386}
  109. w:=get8087cw;
  110. {$endif}
  111. result:=loadlibrary(name);
  112. {$ifdef i386}
  113. set8087cw(w);
  114. {$endif}
  115. End;
  116. function LibraryHandler(const InterfaceName: String; const DefaultLibraries: array of String;
  117. const Symbols: PLibSymbol; const SymCount: Integer; const AfterLoading: TLibEventLoading;
  118. const BeforeUnloading: TLibEventUnloading; const IdentGetter: TLibIdentGetter): TLibHandler;
  119. var
  120. I: Integer;
  121. begin
  122. Result.InterfaceName := InterfaceName;
  123. Result.Filename := '';
  124. Result.Handle := NilHandle;
  125. Result.Loading := AfterLoading;
  126. Result.Unloading := BeforeUnloading;
  127. Result.IdentGetter := IdentGetter;
  128. Result.Ident := 0;
  129. Result.SymCount := SymCount;
  130. Result.Symbols := Symbols;
  131. Result.ErrorMsg := '';
  132. Result.RefCount := 0;
  133. SetLength(Result.Defaults, Length(DefaultLibraries));
  134. for I := 0 to High(DefaultLibraries) do
  135. Result.Defaults[I] := DefaultLibraries[I];
  136. end;
  137. function TryInitializeLibraryInternal(var Handler: TLibHandler; const LibraryName: String;
  138. const User: Pointer; const NoSymbolErrors: Boolean): Integer;
  139. var
  140. ErrSym: PLibSymbol;
  141. NewIdent: TLibIdent;
  142. begin
  143. if Handler.Filename <> '' then
  144. begin
  145. if Assigned(Handler.IdentGetter) then
  146. begin
  147. NewIdent := Handler.IdentGetter(LibraryName);
  148. if NewIdent <> Handler.Ident then
  149. begin
  150. AppendLibraryError(Handler, Format(SLibraryAlreadyLoaded, [Handler.InterfaceName, Handler.Filename]));
  151. Result := -1;
  152. Exit;
  153. end;
  154. end;
  155. end;
  156. Result := InterlockedIncrement(Handler.RefCount);
  157. if Result = 1 then
  158. begin
  159. Handler.Handle := LoadLibrary(LibraryName);
  160. if Handler.Handle = NilHandle then
  161. begin
  162. AppendLibraryError(Handler, Format(SLibraryNotLoaded, [Handler.InterfaceName, LibraryName]));
  163. Handler.RefCount := 0;
  164. Result := -1;
  165. Exit;
  166. end;
  167. Handler.Filename := LibraryName;
  168. if not LoadLibrarySymbols(Handler.Handle, Handler.Symbols, Handler.SymCount, @ErrSym) and not NoSymbolErrors then
  169. begin
  170. AppendLibraryError(Handler, Format(SLibraryUnknownSym, [ErrSym^.name, Handler.InterfaceName, LibraryName]));
  171. UnloadLibrary(Handler.Handle);
  172. Handler.Handle := NilHandle;
  173. Handler.Filename := '';
  174. Handler.RefCount := 0;
  175. Result := -1;
  176. Exit;
  177. end;
  178. if Assigned(Handler.Loading) and not Handler.Loading(User, @Handler) then
  179. begin
  180. UnloadLibrary(Handler.Handle);
  181. Handler.Handle := NilHandle;
  182. Handler.Filename := '';
  183. Handler.RefCount := 0;
  184. Result := -1;
  185. Exit;
  186. end;
  187. if Assigned(Handler.IdentGetter) then
  188. Handler.Ident := Handler.IdentGetter(Handler.Filename)
  189. else
  190. Handler.Ident := 0;
  191. end;
  192. end;
  193. function TryInitializeLibrary(var Handler: TLibHandler; const LibraryName: String;
  194. const User: Pointer; const NoSymbolErrors: Boolean): Integer;
  195. begin
  196. if LibraryName <> '' then
  197. begin
  198. Handler.ErrorMsg := '';
  199. Result := TryInitializeLibraryInternal(Handler, LibraryName, User, NoSymbolErrors);
  200. end else
  201. Result := TryInitializeLibrary(Handler, Handler.Defaults, User, NoSymbolErrors);
  202. end;
  203. function TryInitializeLibrary(var Handler: TLibHandler; const LibraryNames: array of String;
  204. const User: Pointer; const NoSymbolErrors: Boolean): Integer;
  205. var
  206. I: Integer;
  207. begin
  208. Handler.ErrorMsg := '';
  209. if Length(LibraryNames) <= 0 then
  210. begin
  211. if Length(Handler.Defaults) > 0 then
  212. begin
  213. Result := TryInitializeLibrary(Handler, Handler.Defaults, User, NoSymbolErrors);
  214. Exit;
  215. end;
  216. AppendLibraryError(Handler, SVarInvalid);
  217. Result := -1;
  218. Exit;
  219. end;
  220. for I := 0 to High(LibraryNames) do
  221. begin
  222. Result := TryInitializeLibraryInternal(Handler, LibraryNames[I], User, NoSymbolErrors);
  223. if Result > 0 then
  224. begin
  225. Handler.ErrorMsg := '';
  226. Exit;
  227. end;
  228. end;
  229. end;
  230. function InitializeLibrary(var Handler: TLibHandler; const LibraryNames: array of String;
  231. const User: Pointer; const NoSymbolErrors: Boolean): Integer;
  232. begin
  233. Result := TryInitializeLibrary(Handler, LibraryNames, User, NoSymbolErrors);
  234. if Result < 0 then
  235. RaiseLibraryException(Handler);
  236. end;
  237. function InitializeLibrary(var Handler: TLibHandler; const LibraryName: String;
  238. const User: Pointer; const NoSymbolErrors: Boolean): Integer;
  239. begin
  240. Result := TryInitializeLibrary(Handler, LibraryName, User, NoSymbolErrors);
  241. if Result < 0 then
  242. RaiseLibraryException(Handler);
  243. end;
  244. function ReleaseLibrary(var Handler: TLibHandler): Integer;
  245. begin
  246. Handler.ErrorMsg := '';
  247. Result := InterlockedDecrement(Handler.RefCount);
  248. if Result = 0 then
  249. begin
  250. if Assigned(Handler.Unloading) then
  251. Handler.Unloading(@Handler);
  252. ClearLibrarySymbols(Handler.Symbols, Handler.SymCount);
  253. UnloadLibrary(Handler.Handle);
  254. Handler.Handle := NilHandle;
  255. Handler.Filename := '';
  256. Handler.Ident := 0;
  257. end else
  258. if Result < 0 then
  259. Handler.RefCount := 0;
  260. end;
  261. procedure AppendLibraryError(var Handler: TLibHandler; const Msg: String);
  262. begin
  263. if Handler.ErrorMsg <> '' then
  264. Handler.ErrorMsg := Handler.ErrorMsg + LineEnding + Msg
  265. else
  266. Handler.ErrorMsg := Msg;
  267. end;
  268. function GetLastLibraryError(var Handler: TLibHandler): String;
  269. begin
  270. Result := Handler.ErrorMsg;
  271. Handler.ErrorMsg := '';
  272. end;
  273. procedure RaiseLibraryException(var Handler: TLibHandler);
  274. var
  275. Msg: String;
  276. begin
  277. Msg := GetLastLibraryError(Handler);
  278. if Msg <> '' then
  279. raise EInOutError.Create(Msg)
  280. else
  281. raise EInOutError.Create(SUnknown);
  282. end;
  283. function LoadLibrarySymbols(const Lib: TLibHandle; const Symbols: PLibSymbol; const Count: Integer;
  284. const ErrorSym: PPLibSymbol): Boolean;
  285. var
  286. P,L: PLibSymbol;
  287. begin
  288. P := Symbols;
  289. L := @Symbols[Count];
  290. while P < L do
  291. begin
  292. P^.pvar^ := GetProcedureAddress(Lib, P^.name);
  293. if not Assigned(P^.pvar^) and not P^.weak then
  294. begin
  295. if Assigned(ErrorSym) then
  296. ErrorSym^ := P;
  297. Result := False;
  298. Exit;
  299. end;
  300. Inc(P);
  301. end;
  302. Result := True;
  303. end;
  304. procedure ClearLibrarySymbols(const Symbols: PLibSymbol; const Count: Integer);
  305. var
  306. P,L: PLibSymbol;
  307. begin
  308. P := Symbols;
  309. L := @Symbols[Count];
  310. while P < L do
  311. begin
  312. P^.pvar^ := nil;
  313. Inc(P);
  314. end;
  315. end;
  316. end.