dynlibs.pas 11 KB

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