Programming => Operating Systems => Windows => Topic started by: Gizmo on June 30, 2021, 12:13:22 pm

Title: Advice for generating DLL with dependancies
Post by: Gizmo on June 30, 2021, 12:13:22 pm
Not so much a FPC question, but more of a generic programming challenge that I'm hoping the experienced folks here can help with.

As some of my other posts show, I have trodden the path recently of calling other libraries, in the form of DLLs which I am commonly compiling from source using MINGW32 or MINGW64.

But, I am struggling with deployment problems. To use one example, I am creating a DLL that works with a commercial tool who have an API. Using that API, and Lazarus, I can create a DLL that works with that program. My DLL, itself, calls other libraries. Notably (in this example) Tesseract, which itself has a dependancy for Leptonica. Those two libraries, themselves, have further dependancies, several of which are part of the MinGW environment. But, simply "shipping" those DLLs with mine doesnt seem to work on a vanilla Windows 10 environment. Sure there are other things needed like installing the C++ Redistributable package etc, but even then, they still wont work. Yet, they work on my development system flawlessly.

So the problem I think is working out how to create\compile a DLL that just has everything else that it needs bui8lt into it? Or, at least, a system that ensures that when you compile a DLL (for example the Tesseract one) that the other DLL that it needs are either built and written to the same location at make time, or is in some other way included.

I'm sure this can be done, but from what I have Googled, it seems rather tricky. What methods are you folks using to get around these headaches? Is there a way to do it that is as straight forward as "get the source code for library A from Github, run configure, run make, and get a DLL that already has in it, or with it, all the other DLLs that it needs"? 
Title: Re: Advice for generating DLL with dependancies
Post by: 440bx on June 30, 2021, 01:16:41 pm
you weren't specific as to what the problem is exactly but, you got one thing going for you, which is, you have a setup where the program loads and runs successfully.

If the problem is that some DLL dependency isn't met, finding out the dll(s) that cause the problem is easy. 

You need to create a basic, bare bones test program that loads all the needed DLLs _dynamically_ (that is using LoadLibrary) then immediately unloads them. Ensure the program is still running after the DLLs have been unloaded. 

Before ending the test program, use Process Hacker, find your test program in Process Hacker's process list, select it,  right click on it, select "miscellaneous" -> "Unloaded modules" from the popup menu.  That will give you a list of all the dependencies in the dlls you loaded.

After that you just have to make sure your program is "shipped" with all the required dlls (which you got from Process Hacker's "Unloaded modules".

Here is a sample program you can modify to load your dlls.  Before modifying it, run it and check out the list of dlls you get from Process Hacker (you might be a bit surprised.)
Code: Pascal  [Select][+][-]
  3. program _LoadedUnloaded;
  5. uses
  6.   Windows
  7.   ;
  9. const
  10.   Libraries : packed array[0..1]
  11.                         of record
  12.                              LibraryName   : pchar;
  13.                              LibraryHandle : THANDLE
  14.                            end
  15.                            = (
  16.                               (LibraryName:'ws2_32.dll'; LibraryHandle:0),
  17.                               (LibraryName:'urlmon.dll'; LibraryHandle:0)
  18.                              );
  19. var
  20.   i         : longint;
  22. begin
  23.   writeln;
  24.   writeln;
  26.   { load the libraries                                                        }
  28.   for i := low(Libraries) to high(Libraries) do
  29.   begin
  30.     with Libraries[i] do
  31.     begin
  32.       LibraryHandle := LoadLibrary(LibraryName);
  34.       { check for a valid handle (left as an exercise) }
  35.     end;
  36.   end;
  38.   { unload the libraries                                                      }
  40.   for i := low(Libraries) to high(Libraries) do
  41.   begin
  42.     with Libraries[i] do
  43.     begin
  44.       if not FreeLibrary(LibraryHandle) then
  45.       begin
  46.         { output a message or whatever else you want                          }
  48.       end;
  49.     end;
  50.   end;
  53.   { go to ProcessHacker, right click on the process, select "miscellaneous"-> }
  54.   { Unloaded modules to get a list of the dlls that were unloaded.            }
  56.   { DON'T press ENTER/RETURN until you've gotten the list from process hacker }
  58.   writeln('  go to Process Hacker to get the list of unloaded modules');
  60.   writeln('  DON''T press ENTER/RETURN until AFTER getting the list');
  62.   writeln;
  63.   writeln('press ENTER/RETURN to end this program');
  64.   readln;
  65. end.                


PS: Process Hacker should show the following for the program above
Code: Text  [Select][+][-]
  1. 0, ws2_32.dll, 0x7fefdc90000, 308 kB, 9:16:21 AM 11/20/2010, 0x4a892
  2. 1, NSI.dll, 0x7fefeb50000, 32 kB, 9:32:25 PM 7/13/2009, 0x5f1e
  3. 2, urlmon.dll, 0x7fefeb60000, 1.52 MB, 1:38:07 PM 5/22/2015, 0x17a521
  4. 3, WININET.dll, 0x7fefedd0000, 2.35 MB, 1:50:01 PM 5/22/2015, 0x25da00
  5. 4, USERENV.dll, 0x7fefcf80000, 120 kB, 9:15:32 AM 11/20/2010, 0x20aad
  6. 5, profapi.dll, 0x7fefccc0000, 60 kB, 9:32:15 PM 7/13/2009, 0xcb3e
  7. 6, iertutil.dll, 0x7fefd400000, 2.78 MB, 3:00:02 PM 5/22/2015, 0x2d0705
  8. 7, api-ms-win-downlevel-normaliz-l1, 0x7fefcee0000, 12 kB, 4:35:31 PM 1/13/2013, 0x3d4b
  9. 8, api-ms-win-downlevel-version-l1-, 0x7fefccd0000, 16 kB, 4:31:40 PM 1/13/2013, 0x103be
  10. 9, api-ms-win-downlevel-user32-l1-1, 0x7fefce90000, 16 kB, 4:31:48 PM 1/13/2013, 0xf92d
  11. 10, api-ms-win-downlevel-shlwapi-l1-, 0x7fefcf70000, 16 kB, 4:35:31 PM 1/13/2013, 0x8381
  12. 11, api-ms-win-downlevel-ole32-l1-1-, 0x7fefd060000, 16 kB, 4:31:41 PM 1/13/2013, 0x9d07
  13. 12, normaliz.DLL, 0x770d0000, 12 kB, 9:32:18 PM 7/13/2009, 0x3002
  14. 13, version.DLL, 0x7fefbda0000, 48 kB, 9:33:54 PM 7/13/2009, 0x11f3f
  15. 14, shlwapi.DLL, 0x7fefecf0000, 452 kB, 9:14:19 AM 11/20/2010, 0x7cd14

IMPORTANT DETAIL: Some versions of Process Hacker fail to obtain the list of unloaded modules from 32bit programs.  In that case, compile the test program for 64bit or get the latest version of Process Hacker that hopefully has corrected that problem.
Title: Re: Advice for generating DLL with dependancies
Post by: ASerge on June 30, 2021, 03:19:01 pm
Is there a way to do it that is as straight forward as "get the source code for library A from Github, run configure, run make, and get a DLL that already has in it, or with it, all the other DLLs that it needs"?
In general, there is no solution, except not to use libraries at all.
Example. You are using the LibA. Everything is fine, but it uses some LibCommon library from C++ Redistributable. Then later you insert another LibB library into your application (libA and libB are located in the directory of your application). And it also uses LibCommon, but only a newer version. Therefore, you are also adding this new version of LibCommon to the delivery of your application. As a result, if LibB loads first, then when LibA is loaded it is forced to use the new version of LibCommon and its behavior changes, possibly with errors in your application. And each author of his library will be right when he says that everything has been tested and works.
Title: Re: Advice for generating DLL with dependancies
Post by: Gizmo on July 01, 2021, 12:47:37 pm
Whoah 440bx!! That is such an awesome trick!! Wowsers. How incredibly useful! Thanks so much.

My immediate observation is this : there are 40 libraries unloaded for the two DLLs I used. 2 of them are the libs themselves, with another 38! So, my problem here is how do I practically bundle all those DLL's, and are all of them the ones demanded by the DLL, or my the project1.exe?? If the latter, how do I work out which are being loaded by my two DLL's, and which are being loaded by project1.exe?

File count aside, and bloat of the zip I distribute aside, there will no doubt be licensing concerns and requirements as well. :-(

But this is a great start to help me on the road. Thank you.

ASerge - totally understand, and and largely agree, but, some of these libraries are immense. Mature code, well developed, and feature rich. Coding them yourself from scratch seems unviable. Or do you mean there is another way to use them?
TinyPortal © 2005-2018