[SOLVED] Mysterious Path Issue when loading DLLs lazarus / 21-General ---------------------------------------------------------------------- Gizmo · 2021-05-22 15:09 I'm nearing completion of a major overhaul of my program that now includes several DLL libraries. To keep things neat, I've put them in a subfolder called libs\x64 along with the other DLL's that they depend on. So my folder structure is : MyFolder\MyProgram.exe MyFolder\libs\x64\MyDLL1.dll MyFolder\libs\x64\MyDependantDLL.dll In my code, I then have the following : Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i const   LIB_FOLDER : ansistring = 'libs\x64';var  libFileName : ansistring = Default(ansistring);begin...libFileName:=ExtractFilePath(Application.E xeName)+IncludeTrailingPathDelimiter(LIB_FOLDER)+'MyDLL1.dll';if FileExists(libFileName) then  // returns true   begin    fLibHandle := LoadLibraryA(PAnsiChar(libFileName));...end;  OK, nice and simple. When I run the program via the Lazarus IDE, or on my own computer, that works fine. If I go through with the debugger, libFileName correctly shows the full path and the DLL name itself; something like C:\temp\MyFolder\libs\x64\MyDLL1.dll. If I copy it somewhere else and repeat I get the same in the debugger or via ShowMessage C:\SomeOtherFolder\MyFolder\libs\x64\MyDLL1.dll. So I know the paths and delimiters all work OK. I was about to release it like this but thought I'd ask a friend to check it out first. But, when it is sent to him and run on another PC also running Windows 10, the LoadLibraryA fails. At first I thought it might be due to a missing dependancy on his system, but when I get my friend to check the MyDLL1.dll using Dependancy Walker on his PC, the required other DLL's are also found. So then I asked him to check permissions, and they all have read and execute permission. More mysteriously still, is if he then copies (moves) the DLL's from libs\x64 to the ROOT of where the exe runs from, it works, and the library is loaded. Despite the fact that the LoadLibraryA call in the exe specifically states, in code, to look in libs\x64. So what on Earth is going on here that I don't know about? Has LoadLibraryA got some other fall back default behaviour where it looks in the root if the path given does not work? It says on the wiki page that no assumptions are made and the path must be stated explicitly. And if it does do that, how might we explain how it works fine on my PC as is, but not on another PC? How can it work when the DLL's are moved? And not work when they are where they are supposed to be? As it stands, I am considering just going back to having them all in the root with the exe how it was before, but this feels like a step back and is untidy. Any ideas? ---------------------------------------------------------------------- marcov · 2021-05-22 15:14 So basically it works if the DLLs are next to the EXE or in the current directory, or in the DLLPATH and fails otherwise. Solution: manually load the base DLLs in order. If they are already loaded, the dependent DLL will probably reuse them, and won't need the disc image. Another route might be https://stackoverflow.com/questions/518228/is- it-possible-to-add-a-directory-to-dll-search-path-from-a-batch-file- or-cmd-sc ---------------------------------------------------------------------- engkin · 2021-05-22 15:49 https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf- libloaderapi-loadlibrarya: QuoteIf the string specifies a full path, the function searches only that path for the module I wonder, what did you not tell us? ---------------------------------------------------------------------- loaded · 2021-05-22 15:51 In Windows 10 operating systems; To facilitate complexity and control, I put my library files in a folder I created. I add the following code to the lpr file; Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Function  SetDllDirectory(lpPathName: LPCTSTR):LongBool; stdcall; external 'kernel32.dll' name 'SetDllDirectoryA'; //uses add windows,sysutils,Procedure ChangeDllDirectory; function SystemFolder: string;    begin      SetLength(Result, Windows.MAX_PATH);      SetLength(        Result, Windows.GetSystemDirectory(PChar(Result), Windows.MAX_PATH)      );    end;var   s: UTF8String;  begin    s := UTF8ToCP1254(ExtractFilePath(ParamStr(0))) + 'Libraries\';    If Not DirectoryExists(ExtractFilePath(ParamStr(0)) + 'Libraries\') then showmessage('Library Folder Not Found');    SetDllDirectory(PChar(s));end;   Related method in lpr file ; Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  begin  Application.Title:='Project Dll';  ChangeDllDirectory;  RequireDerivedFormResource:=True;  Application.Scaled:=True;  Application.Initialize;  Application.CreateForm(TForm_Main, Form_Main);  Application.Run;end. I call it from within the start block, that way my applications run smoothly. ---------------------------------------------------------------------- Gizmo · 2021-05-22 15:56 Marcov - your link was super helpful. I had no idea DLL loading worked in that way. From it I have switched the call as follows, to use LoadLibraryEx : Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i fLibHandle := LoadLibraryEx(PAnsiChar(libFileName), 0, LOAD_WITH_ALTERED_SEARCH_PATH);  QuoteLOAD_WITH_ALTERED_SEARCH_PATH 0x00000008 If this value is used and lpFileName specifies an absolute path, the system uses the alternate file search strategy discussed in the Remarks section to find associated executable modules that the specified module causes to be loaded. If this value is used and lpFileName specifies a relative path, the behavior is undefined. If this value is not used, or if lpFileName does not specify a path, the system uses the standard search strategy discussed in the Remarks section to find associated executable modules that the specified module causes to be loaded. I recompiled, checked it on my PC, then copied it to a new Windows 10 VM with nothing installed other than the latest OS updates, and it works out of the gate. Thanks guys. And loaded, I like your thinking also. I might need to look at implementing some of that for smoother and more reliable loading. ---------------------------------------------------------------------- 440bx · 2021-05-22 15:58 The profilactic first step is to check the value returned by LoadLibrary and if it is nil/null then call GetLastError to find out what happened.  IOW, get all the help you can get from the O/S to figure out what went wrong. If this is a GUI app, just put {$APPTYPE CONSOLE} in the lpr, only do a writeln if the program can get a valid handle using GetStdHandle(STD_OUTPUT_HANDLE) indicating that there is a console available for output. HTH.