ModuleLoader.pas 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. ****************************************************************}
  2. { }
  3. { Project JEDI }
  4. { OS independent Dynamic Loading Helpers }
  5. { }
  6. { The initial developer of the this code is }
  7. { Robert Marquardt <robert_marquardt att gmx dott de) }
  8. { }
  9. { Copyright (C) 2000, 2001 Robert Marquardt. }
  10. { }
  11. { Obtained through: }
  12. { Joint Endeavour of Delphi Innovators (Project JEDI) }
  13. { }
  14. { You may retrieve the latest version of this file at the Project }
  15. { JEDI home page, located at http://delphi-jedi.org }
  16. { }
  17. { The contents of this file are used with permission, subject to }
  18. { the Mozilla Public License Version 1.1 (the "License"); you may }
  19. { not use this file except in compliance with the License. You may }
  20. { obtain a copy of the License at }
  21. { http://www.mozilla.org/NPL/NPL-1_1Final.html }
  22. { }
  23. { Software distributed under the License is distributed on an }
  24. { "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or }
  25. { implied. See the License for the specific language governing }
  26. { rights and limitations under the License. }
  27. { }
  28. {******************************************************************}
  29. {$IFNDEF JWA_OMIT_SECTIONS}
  30. unit ModuleLoader;
  31. {$ENDIF JWA_OMIT_SECTIONS}
  32. {.$I jvcl.inc}
  33. {$IFNDEF JWA_OMIT_SECTIONS}
  34. {$WEAKPACKAGEUNIT ON}
  35. interface
  36. {$IFDEF MSWINDOWS}
  37. uses
  38. Windows;
  39. {$ENDIF MSWINDOWS}
  40. {$IFDEF UNIX}
  41. uses
  42. Types, Libc;
  43. {$ENDIF UNIX}
  44. {$ENDIF JWA_OMIT_SECTIONS}
  45. {$IFNDEF JWA_IMPLEMENTATIONSECTION}
  46. {$IFDEF MSWINDOWS}
  47. type
  48. // Handle to a loaded DLL
  49. TModuleHandle = HINST;
  50. {$ENDIF MSWINDOWS}
  51. {$IFDEF UNIX}
  52. type
  53. // Handle to a loaded .so
  54. TModuleHandle = Pointer;
  55. {$ENDIF UNIX}
  56. const
  57. // Value designating an unassigned TModuleHandle or a failed loading
  58. INVALID_MODULEHANDLE_VALUE = TModuleHandle(0);
  59. {$IFNDEF JWA_INCLUDEMODE}
  60. function LoadModule(var Module: TModuleHandle; FileName: Ansistring): Boolean;
  61. {$ELSE}
  62. function ModuleLoader_LoadModule(var Module: TModuleHandle; FileName: Ansistring): Boolean;
  63. {$ENDIF JWA_INCLUDEMODE}
  64. function LoadModuleEx(var Module: TModuleHandle; FileName: Ansistring; Flags: Cardinal): Boolean;
  65. procedure UnloadModule(var Module: TModuleHandle);
  66. function GetModuleSymbol(Module: TModuleHandle; SymbolName: Ansistring): Pointer;
  67. function GetModuleSymbolEx(Module: TModuleHandle; SymbolName: Ansistring; var Accu: Boolean): Pointer;
  68. function ReadModuleData(Module: TModuleHandle; SymbolName: Ansistring; var Buffer; Size: Cardinal): Boolean;
  69. function WriteModuleData(Module: TModuleHandle; SymbolName: Ansistring; var Buffer; Size: Cardinal): Boolean;
  70. // (p3)
  71. // Simple DLL loading class. The idea is to use it to dynamically load
  72. // a DLL at run-time using the GetProcedure method. Another (better) use is to derive a
  73. // new class for each DLL you are interested in and explicitly call GetProcedure for
  74. // each function in an overridden Load method. You would then add procedure/function
  75. // aliases to the new class that maps down to the internally managed function pointers.
  76. // This class is built from an idea I read about in Delphi Magazine a while ago but
  77. // I forget who was the originator. If you know, let me know and I'll put it in the credits
  78. // NB!!!
  79. // * Prepared for Kylix but not tested
  80. // * Is GetLastError implemented on Kylix? RaiseLastOSError implies it is...
  81. type
  82. TModuleLoadMethod = (ltDontResolveDllReferences, ltLoadAsDataFile, ltAlteredSearchPath);
  83. TModuleLoadMethods = set of TModuleLoadMethod;
  84. TModuleLoader = class(TObject)
  85. private
  86. FHandle: TModuleHandle;
  87. FDLLName: ansistring;
  88. function GetLoaded: Boolean;
  89. protected
  90. procedure Load(LoadMethods: TModuleLoadMethods); virtual;
  91. procedure Unload; virtual;
  92. procedure Error(ErrorCode: Cardinal); virtual;
  93. public
  94. // Check whether a DLL (and optionally a function) is available on the system
  95. // To only check the DLL, leave ProcName empty
  96. class function IsAvaliable(const ADLLName: ansistring; const AProcName: AnsiString = ''): Boolean;
  97. constructor Create(const ADLLName: ansistring; LoadMethods: TModuleLoadMethods = []);
  98. destructor Destroy; override;
  99. // Get a pointer to a function in the DLL. Should be called as GetProcedure('Name',@FuncPointer);
  100. // Returns True if the function was found. Note that a call to GetProcAddress is only executed if AProc = nil
  101. function GetProcedure(const AName: ansistring; var AProc: Pointer): Boolean;
  102. // Returns a symbol exported from the DLL and puts it in Buffer.
  103. // Make sure AName is actually a symbol and not a function or this will crash horribly!
  104. function GetExportedSymbol(const AName: ansistring; var Buffer; Size: Integer): Boolean;
  105. // Changes a symbol exported from the DLL into the value in Buffer.
  106. // The change is not persistent (it will get lost when the DLL is unloaded)
  107. // Make sure AName is actually a symbol and not a function or this will crash horribly!
  108. function SetExportedSymbol(const AName: ansistring; var Buffer; Size: Integer): Boolean;
  109. property Loaded: Boolean read GetLoaded;
  110. property DLLName: ansistring read FDLLName;
  111. property Handle: TModuleHandle read FHandle;
  112. end;
  113. {$ENDIF JWA_IMPLEMENTATIONSECTION}
  114. {$IFNDEF JWA_OMIT_SECTIONS}
  115. implementation
  116. //uses ...
  117. {$ENDIF JWA_OMIT_SECTIONS}
  118. {$IFNDEF JWA_INTERFACESECTION}
  119. {$IFDEF MSWINDOWS}
  120. // load the DLL file FileName
  121. // the rules for FileName are those of LoadLibrary
  122. // Returns: True = success, False = failure to load
  123. // Assigns: the handle of the loaded DLL to Module
  124. // Warning: if Module has any other value than INVALID_MODULEHANDLE_VALUE
  125. // on entry the function will do nothing but returning success.
  126. {$IFNDEF JWA_INCLUDEMODE}
  127. function LoadModule(var Module: TModuleHandle; FileName: AnsiString): Boolean;
  128. {$ELSE}
  129. function ModuleLoader_LoadModule(var Module: TModuleHandle; FileName: AnsiString): Boolean;
  130. {$ENDIF JWA_INCLUDEMODE}
  131. begin
  132. if Module = INVALID_MODULEHANDLE_VALUE then
  133. Module := LoadLibraryA(PAnsiChar(FileName));
  134. Result := Module <> INVALID_MODULEHANDLE_VALUE;
  135. end;
  136. // load the DLL file FileName
  137. // LoadLibraryEx is used to get better control of the loading
  138. // for the allowed values for flags see LoadLibraryEx documentation.
  139. function LoadModuleEx(var Module: TModuleHandle; FileName: AnsiString; Flags: Cardinal): Boolean;
  140. begin
  141. if Module = INVALID_MODULEHANDLE_VALUE then
  142. Module := LoadLibraryExA(PAnsiChar(FileName), 0, Flags);
  143. Result := Module <> INVALID_MODULEHANDLE_VALUE;
  144. end;
  145. // unload a DLL loaded with LoadModule or LoadModuleEx
  146. // The procedure will not try to unload a handle with
  147. // value INVALID_MODULEHANDLE_VALUE and assigns this value
  148. // to Module after unload.
  149. procedure UnloadModule(var Module: TModuleHandle);
  150. begin
  151. if Module <> INVALID_MODULEHANDLE_VALUE then
  152. FreeLibrary(Module);
  153. Module := INVALID_MODULEHANDLE_VALUE;
  154. end;
  155. // returns the pointer to the symbol named SymbolName
  156. // if it is exported from the DLL Module
  157. // nil is returned if the symbol is not available
  158. function GetModuleSymbol(Module: TModuleHandle; SymbolName: AnsiString): Pointer;
  159. begin
  160. Result := nil;
  161. if Module <> INVALID_MODULEHANDLE_VALUE then
  162. Result := GetProcAddress(Module, PAnsiChar(SymbolName));
  163. end;
  164. // returns the pointer to the symbol named SymbolName
  165. // if it is exported from the DLL Module
  166. // nil is returned if the symbol is not available.
  167. // as an extra the Boolean variable Accu is updated
  168. // by anding in the success of the function.
  169. // This is very handy for rendering a global result
  170. // when accessing a long list of symbols.
  171. function GetModuleSymbolEx(Module: TModuleHandle; SymbolName: AnsiString; var Accu: Boolean): Pointer;
  172. begin
  173. Result := nil;
  174. if Module <> INVALID_MODULEHANDLE_VALUE then
  175. Result := GetProcAddress(Module, PAnsiChar(SymbolName));
  176. Accu := Accu and (Result <> nil);
  177. end;
  178. // get the value of variables exported from a DLL Module
  179. // Delphi cannot access variables in a DLL directly, so
  180. // this function allows to copy the data from the DLL.
  181. // Beware! You are accessing the DLL memory image directly.
  182. // Be sure to access a variable not a function and be sure
  183. // to read the correct amount of data.
  184. function ReadModuleData(Module: TModuleHandle; SymbolName: AnsiString; var Buffer; Size: Cardinal): Boolean;
  185. var
  186. Sym: Pointer;
  187. begin
  188. Result := True;
  189. Sym := GetModuleSymbolEx(Module, SymbolName, Result);
  190. if Result then
  191. Move(Sym^, Buffer, Size);
  192. end;
  193. // set the value of variables exported from a DLL Module
  194. // Delphi cannot access variables in a DLL directly, so
  195. // this function allows to copy the data to the DLL!
  196. // BEWARE! You are accessing the DLL memory image directly.
  197. // Be sure to access a variable not a function and be sure
  198. // to write the correct amount of data.
  199. // The changes are not persistent. They get lost when the
  200. // DLL is unloaded.
  201. function WriteModuleData(Module: TModuleHandle; SymbolName: AnsiString; var Buffer; Size: Cardinal): Boolean;
  202. var
  203. Sym: Pointer;
  204. begin
  205. Result := True;
  206. Sym := GetModuleSymbolEx(Module, SymbolName, Result);
  207. if Result then
  208. Move(Buffer, Sym^, Size);
  209. end;
  210. {$ENDIF MSWINDOWS}
  211. {$IFDEF UNIX}
  212. const
  213. TYPE_E_ELEMENTNOTFOUND = $8002802B;
  214. // load the .so file FileName
  215. // the rules for FileName are those of dlopen()
  216. // Returns: True = success, False = failure to load
  217. // Assigns: the handle of the loaded .so to Module
  218. // Warning: if Module has any other value than INVALID_MODULEHANDLE_VALUE
  219. // on entry the function will do nothing but returning success.
  220. {$IFNDEF JWA_INCLUDEMODE}
  221. function LoadModule(var Module: TModuleHandle; FileName: AnsiString): Boolean;
  222. {$ELSE}
  223. function ModuleLoader_LoadModule(var Module: TModuleHandle; FileName: AnsiString): Boolean;
  224. {$ENDIF JWA_INCLUDEMODE}
  225. begin
  226. if Module = INVALID_MODULEHANDLE_VALUE then
  227. Module := dlopen(PAnsiChar(FileName), RTLD_NOW);
  228. Result := Module <> INVALID_MODULEHANDLE_VALUE;
  229. end;
  230. // load the .so file FileName
  231. // dlopen() with flags is used to get better control of the loading
  232. // for the allowed values for flags see "man dlopen".
  233. function LoadModuleEx(var Module: TModuleHandle; FileName: AnsiString; Flags: Cardinal): Boolean;
  234. begin
  235. if Module = INVALID_MODULEHANDLE_VALUE then
  236. Module := dlopen(PAnsiChar(FileName), Flags);
  237. Result := Module <> INVALID_MODULEHANDLE_VALUE;
  238. end;
  239. // unload a .so loaded with LoadModule or LoadModuleEx
  240. // The procedure will not try to unload a handle with
  241. // value INVALID_MODULEHANDLE_VALUE and assigns this value
  242. // to Module after unload.
  243. procedure UnloadModule(var Module: TModuleHandle);
  244. begin
  245. if Module <> INVALID_MODULEHANDLE_VALUE then
  246. dlclose(Module);
  247. Module := INVALID_MODULEHANDLE_VALUE;
  248. end;
  249. // returns the pointer to the symbol named SymbolName
  250. // if it is exported from the .so Module
  251. // nil is returned if the symbol is not available
  252. function GetModuleSymbol(Module: TModuleHandle; SymbolName: AnsiString): Pointer;
  253. begin
  254. Result := nil;
  255. if Module <> INVALID_MODULEHANDLE_VALUE then
  256. Result := dlsym(Module, PAnsiChar(SymbolName));
  257. end;
  258. // returns the pointer to the symbol named SymbolName
  259. // if it is exported from the .so Module
  260. // nil is returned if the symbol is not available.
  261. // as an extra the Boolean variable Accu is updated
  262. // by anding in the success of the function.
  263. // This is very handy for rendering a global result
  264. // when accessing a long list of symbols.
  265. function GetModuleSymbolEx(Module: TModuleHandle; SymbolName: AnsiString; var Accu: Boolean): Pointer;
  266. begin
  267. Result := nil;
  268. if Module <> INVALID_MODULEHANDLE_VALUE then
  269. Result := dlsym(Module, PAnsiChar(SymbolName));
  270. Accu := Accu and (Result <> nil);
  271. end;
  272. // get the value of variables exported from a .so Module
  273. // Delphi cannot access variables in a .so directly, so
  274. // this function allows to copy the data from the .so.
  275. // Beware! You are accessing the .so memory image directly.
  276. // Be sure to access a variable not a function and be sure
  277. // to read the correct amount of data.
  278. function ReadModuleData(Module: TModuleHandle; SymbolName: AnsiString; var Buffer; Size: Cardinal): Boolean;
  279. var
  280. Sym: Pointer;
  281. begin
  282. Result := True;
  283. Sym := GetModuleSymbolEx(Module, SymbolName, Result);
  284. if Result then
  285. Move(Sym^, Buffer, Size);
  286. end;
  287. // set the value of variables exported from a .so Module
  288. // Delphi cannot access variables in a .so directly, so
  289. // this function allows to copy the data to the .so!
  290. // BEWARE! You are accessing the .so memory image directly.
  291. // Be sure to access a variable not a function and be sure
  292. // to write the correct amount of data.
  293. // The changes are not persistent. They get lost when the
  294. // .so is unloaded.
  295. function WriteModuleData(Module: TModuleHandle; SymbolName: AnsiString; var Buffer; Size: Cardinal): Boolean;
  296. var
  297. Sym: Pointer;
  298. begin
  299. Result := True;
  300. Sym := GetModuleSymbolEx(Module, SymbolName, Result);
  301. if Result then
  302. Move(Buffer, Sym^, Size);
  303. end;
  304. {$ENDIF UNIX}
  305. //=== { TModuleLoader } ======================================================
  306. constructor TModuleLoader.Create(const ADLLName: AnsiString; LoadMethods: TModuleLoadMethods = []);
  307. begin
  308. inherited Create;
  309. FHandle := INVALID_MODULEHANDLE_VALUE;
  310. FDLLName := ADLLName;
  311. Load(LoadMethods);
  312. end;
  313. destructor TModuleLoader.Destroy;
  314. begin
  315. Unload;
  316. inherited Destroy;
  317. end;
  318. procedure TModuleLoader.Error(ErrorCode: Cardinal);
  319. begin
  320. // overridden classes should handle this
  321. end;
  322. function TModuleLoader.GetExportedSymbol(const AName: AnsiString; var Buffer;
  323. Size: Integer): Boolean;
  324. var
  325. ASymbol: Pointer;
  326. begin
  327. Result := GetProcedure(AName, ASymbol);
  328. if Result then
  329. Move(ASymbol^, Buffer, Size);
  330. end;
  331. function TModuleLoader.GetLoaded: Boolean;
  332. begin
  333. Result := Handle <> INVALID_MODULEHANDLE_VALUE;
  334. end;
  335. function TModuleLoader.GetProcedure(const AName: AnsiString; var AProc: Pointer): Boolean;
  336. begin
  337. Result := Loaded;
  338. if Result and not Assigned(AProc) then
  339. begin
  340. AProc := GetModuleSymbol(Handle, AName);
  341. Result := Assigned(AProc);
  342. end;
  343. if not Result then
  344. begin
  345. AProc := nil;
  346. Error(DWORD(TYPE_E_ELEMENTNOTFOUND));
  347. end;
  348. end;
  349. class function TModuleLoader.IsAvaliable(const ADLLName: AnsiString; const AProcName: AnsiString = ''): Boolean;
  350. var
  351. Module: TModuleHandle;
  352. P: Pointer;
  353. begin
  354. {$IFNDEF JWA_INCLUDEMODE}
  355. Result := LoadModule(Module, ADLLName);
  356. {$ELSE}
  357. Result := ModuleLoader_LoadModule(Module, ADLLName);
  358. {$ENDIF JWA_INCLUDEMODE}
  359. if Result then
  360. begin
  361. if AProcName <> '' then
  362. begin
  363. P := GetModuleSymbol(Module, AProcName);
  364. Result := Assigned(P);
  365. end;
  366. UnloadModule(Module);
  367. end;
  368. end;
  369. procedure TModuleLoader.Load(LoadMethods: TModuleLoadMethods);
  370. const
  371. cLoadMethods: array [TModuleLoadMethod] of DWORD =
  372. {$IFDEF MSWINDOWS}
  373. (DONT_RESOLVE_DLL_REFERENCES, LOAD_LIBRARY_AS_DATAFILE, LOAD_WITH_ALTERED_SEARCH_PATH);
  374. {$ENDIF MSWINDOWS}
  375. {$IFDEF UNIX}
  376. (RTLD_LAZY, RTLD_LAZY, RTLD_LAZY); // there is not really a equivalent under Linux
  377. {$ENDIF UNIX}
  378. var
  379. Flags: DWORD;
  380. I: TModuleLoadMethod;
  381. begin
  382. Flags := 0;
  383. for I := Low(TModuleLoadMethod) to High(TModuleLoadMethod) do
  384. if I in LoadMethods then
  385. Flags := Flags or cLoadMethods[I];
  386. if FHandle = INVALID_MODULEHANDLE_VALUE then
  387. LoadModuleEx(FHandle, DLLName, Flags);
  388. if FHandle = INVALID_MODULEHANDLE_VALUE then
  389. Error(GetLastError);
  390. end;
  391. function TModuleLoader.SetExportedSymbol(const AName: AnsiString; var Buffer;
  392. Size: Integer): Boolean;
  393. var
  394. ASymbol: Pointer;
  395. begin
  396. Result := GetProcedure(AName, ASymbol);
  397. if Result then
  398. Move(Buffer, ASymbol^, Size);
  399. end;
  400. procedure TModuleLoader.Unload;
  401. begin
  402. if FHandle <> INVALID_MODULEHANDLE_VALUE then
  403. UnloadModule(FHandle);
  404. FHandle := INVALID_MODULEHANDLE_VALUE;
  405. end;
  406. {$ENDIF JWA_INTERFACESECTION}
  407. {$IFNDEF JWA_OMIT_SECTIONS}
  408. end.
  409. {$ENDIF JWA_OMIT_SECTIONS}