Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Packages in anderem Verzeichnis (https://www.delphipraxis.net/215891-packages-anderem-verzeichnis.html)

hschmid67 22. Sep 2024 06:08

Packages in anderem Verzeichnis
 
Hallo zusammen,

ich habe eine „einfache“ Frage und komme nicht so recht weiter mit all dem, was ich bisher dazu gefunden habe:

Mein Programm verwendet Packages, eingebunden über die Programmoptionen. Gibt es eine Möglichkeit, beim Programmstart Code auszuführen - vermutlich in der dpr-Datei - bevor versucht wird, diese Packages (bpl) zu laden? Wo muss der Code dann hin?

Hintergrund meiner Frage:
Ich möchte mein Programmverzeichnis gerne „sauber“ halten, also nicht viele libraries/packages dort unterbringen. dort sollten sich nur die paar Dateien befinden, die ausschließlich zum Progamm gehören (also .exe, .ini, .db zum Beispiel). Dazu möchte ich gerne die bpl-Dateien in ein Unterverzeichnis packen und vor deren Laden den Windows-Pfad entsprechend anpassen - und beim Entladen wieder zuück. Habe dazu eine interessante dll gefunden https://github.com/Bill-Stewart/PathMgr.

Wo packe ich also den Code zum Anpassen der Path-Variablen hin?

Delphi-Quellcode:
 begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TformMain, formMain);
  Application.Run;
end.
hier irgendwo?

Oder in das initialization einer eigenen unit, die ich dann als erstes einbinde?

Leider weiß ich zu wenig, wie ein Programmstart intern abläuft, um das richtig zu verstehen.

Für jeden Hinweis dankbar
Harald

Kas Ob. 22. Sep 2024 09:26

AW: Packages in anderem Verzeichnis
 
Hi, very interesting problem !

I spent almost two hours testing different approaches and ...

Zitat:

Zitat von hschmid67 (Beitrag 1541368)
Delphi-Quellcode:
 begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TformMain, formMain);
  Application.Run;
end.
hier irgendwo?

Alas, this is not possible !
One approach i tested is to add a DLL that will change set the PATH variable with SetEnvironmentVariable, the DLL worked fine right after loading by the OS, but ... it seems the OS loads few DLLs in bulk then after that start to call them, if it hit missed one it will stop and unload the loaded ones, so that DLL was loaded but no called, i mean DllMain not called.
Also patched the binary to make sure it is the first to be in the Import Table, and yes the OS tried and loaded it first but didn't call DllMain, even it loaded few more DLL, non being executed, so this was not a solution.

Zitat:

Oder in das initialization einer eigenen unit, die ich dann als erstes einbinde?
Same as above, this will not help at all, as Runtime packages imported by the Import Table as declared in the PE header, meaning, nothing will execute from the EXE Project, before all the imported libraries loaded (attached) and then all the thunk table resolved.

There is few ways to do this, but all are ugly !!
1) Use batch file to execute your EXE, with :
Zitat:

set PATH=YOUR_PACKAGE_PATH;%PATH%
//like
set PATH=D:\Program Files (x86)\Embarcadero\Studio\16.0\bin;%PATH%
2) Add another small, minimum (bare exe) to load your application but before calling CreateProcess do the same with PATH variable in the environment, in this case use SetEnvironmentVariable
https://learn.microsoft.com/en-us/wi...onmentvariable
Also here i am not sure if the the same PATH value will work, i mean passing this "YOUR_PACKAGE_PATH;%PATH%" to SetEnvironmentVariable, so to be on the safe side i would read PATH value first with GetEnvironmentVariable, then append the path then call SetEnvironmentVariable, this before CreateProcess (or any other executing calls that works)
3) Move your EXE to the same package directory, and leave a shortcut !! :duck:

Hope that helps, or may be someone else might have ideas to entertain.

dummzeuch 22. Sep 2024 10:14

AW: Packages in anderem Verzeichnis
 
Eine weitere Möglichkeit wäre kleines Vorschalt-Programm, das das eigentliche Programm aufruft. Dieses eigentliche Programm liegt dann im gleichen Ordner wie die BPLs. Also quasi dasselbe wie eine Verknüpfung, wie Kas Ob es vorgeschlagen hat, aber flexibler.

Wir benutzen sowas intern, um aus Unterordnern jeweils die neueste Version eines Programms aufzurufen:

Code:
Pfad\zum\Programm\
                  Version_1.0.0\
                  Version_1.0.1\
                  Version_1.2.0\
                  Version_1.3.5\
                  Vorschaltprogramm.exe
Hintergrund: So kann man eine neue Programmversion releasen, wenn die alte noch irgendwo in Benutzung ist und deshalb nicht ersetzt werden kann.

Kas Ob. 22. Sep 2024 10:17

AW: Packages in anderem Verzeichnis
 
Liste der Anhänge anzeigen (Anzahl: 1)
Still lingering in my head and i think i found more elegant solution :cheer:

This involve utilizing Windows Application Registration
https://learn.microsoft.com/en-us/wi...p-registration

In short the steps are:
1) add a key in the registry with your EXE name to this path
Zitat:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Curr entVersion\App Paths\DelphiRuntimeTest.exe
then make sure the default (entry without name) in that key has the Application name and path like this
Code:
(default) REG_SZ D:\Projects Delphi\DelphiRuntime\Win32\Debug\DelphiRuntimeTest.exe
and at last add the PATH value
Code:
PATH REG_SZ D:\Projects Delphi\DelphiRuntime\Win32\Debug;D:\Program Files (x86)\Embarcadero\Studio\16.0\bin
and that is it !!


I tested it and it worked perfectly, thoughts on this:
1) For some reason, yet i don't understand it works fine with HKEY_LOCAL_MACHINE but didn't work with HKEY_CURRENT_USER on my OS, yet again my OS is %$#%$ the least, i heavily tweaked many things about paths and users, specially my user, so it might work for you with HKEY_CURRENT_USER.
2) Here how the registry entries look like
Anhang 57141
3) Not sure about adding the current directory in my case above "D:\Projects Delphi\DelphiRuntime\Win32\Debug", most likely it is not needed.

Hope that helps.

Kas Ob. 22. Sep 2024 10:34

AW: Packages in anderem Verzeichnis
 
OK, with the registry trick above, it works only with ShellExecute, with the debugger executing the application with CreateProcess will not work.
If you run the exe using Windows Explorer then it is fine and the DLL will be found and loaded, but other executing method might not work !

himitsu 22. Sep 2024 12:02

AW: Packages in anderem Verzeichnis
 
Delphi-Referenz durchsuchenLoadPackage, aber das geht natürlich nicht bei statisch eingebundenen Packagages. (genauso, wie LoadLibrary bei DLLs)


Side-by-Side Manifest (SxS) : <PackageDependency> in <Dependencies>
bekannte Libraries : HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs
oder, was eben über die Standard-Suchpfade zu finden ist, welches diese Unterverzeichnisse nunmal nicht beinhaltet.

und
MSDN-Library durchsuchenSetDllDirectory
MSDN-Library durchsuchenLoadLibraryEx + LOAD_WITH_ALTERED_SEARCH_PATH
gehen halt erst zur Laufzeit und demnach nur beim dynamischen Laden

hschmid67 22. Sep 2024 19:49

AW: Packages in anderem Verzeichnis
 
erst einmal Euch allen ein dickes Danke! Es ist wirklich großartig, hier solche engagierte und kompetente Hilfe zu erhalten :-)

Nun, die Überlegungen von Kas Ob. sind sehr interessant und haben mir ein wenig Einblick in die Funktionsweise einer exe und Windows gegeben. Ich fürchte, dass ich das aber nicht tun kann, weil ich die Zielsysteme nicht so weit manipulieren möchte. Mein Programm soll eigentlich kaum einen Hinweis auf sich im System, in der Registry hinterlassen - und da ist das Ändern der Path-Variablen schon mehr als eigentlich gewollt. Danke dennoch für die interessanten Ideen, die ich sicherlich für mich weiterverfolgen werde.

Spannend finde ich den Vorschlag von Herrn Müller (dummzeuch), der auch noch andere Vorteile hätte, wie er schreibt. Das werde ich mir genauer ansehen. Einziger Wermutstropfen ist, dass meine Programmstruktur noch komplizierter wird. Aber da das mit den bpl-Dateien und manchen eigenen dll-Dateien eh schon nicht einfach ist, macht das wohl auch nicht mehr viel aus.

Rein verständnishalber würde mich noch die Idee mit dem
Code:
LoadPackage
interessieren. Wenn ich über die Programmoptionen ein Package einbinde, dann ist das wohl statisch. Das Programm wird mit diesem Package kompiliert und alles aus dem Package ist dann statisch eingebunden. Die Exe-Datei wird kleiner und ich muss nichts irgendwo extra deklarieren. Wenn ich ein Package mit LoadPackage einbinde, wie funktioniert das dann mit dem Verwenden der Routinen und Classen des Packages? Muss ich dann ähnlich wie bei LoadLibrary erstmal alle Routinen und Klassen extra deklarieren, oder kann ich die Unit aus dem Package einfach so wie beim statischen Einbinden nutzen. Damit hab ich mich bisher überhaupt nicht beschäftigt und verstehe das mit den Packages gar nicht.

Also nochmal vielen Dank Euch allen und viele Grüße
Harald

jaenicke 23. Sep 2024 07:37

AW: Packages in anderem Verzeichnis
 
Zitat:

Zitat von hschmid67 (Beitrag 1541395)
Wenn ich über die Programmoptionen ein Package einbinde, dann ist das wohl statisch. Das Programm wird mit diesem Package kompiliert und alles aus dem Package ist dann statisch eingebunden. Die Exe-Datei wird kleiner und ich muss nichts irgendwo extra deklarieren. Wenn ich ein Package mit LoadPackage einbinde

Dann funktioniert das nicht. Denn der Compiler benötigt zum Kompilieren die Information, woher die Typen kommen usw., was nur mit Laufzeitpackages extern funktioniert. Später geladene Packages kann der Compiler nicht berücksichtigen. Denn nur bei Laufzeitpackages ist sichergestellt, dass diese bei Verwendung eines Typs auch geladen sind.

dummzeuch 23. Sep 2024 07:44

AW: Packages in anderem Verzeichnis
 
Mit LoadPackage kann man zur Laufzeit weitere Packages einbinden, die für die Funktion optional sind. So, wie die IDE das für Package-basierte Plugins und auch mit den Designtime-Packages der Komponenten macht.

Ich habe mal eine Zeit lang damit geliebäugelt, das für unsere internen Programme zur Modularisierung zu verwenden, aber da die Voraussetzung ist, dass man die Delphi RTL und VCL ebenfalls als Packages einbinden muss, habe ich wieder davon abgesehen. Da Du aber sowieso mit Runtime-Packages arbeitest, wäre das eine Option.

Kas Ob. 23. Sep 2024 10:05

AW: Packages in anderem Verzeichnis
 
LoadPackage will not be much of a help here, as itself (LoadPackage) does exist within RTLxxx.BPL, so even if you managed to change you application and remove all the designtime packages from the IDE designer for all the auto-created forms/frames/modules you still will have trouble to handle RTLxxx.BPL and most likely VCLxxx.BPL with fewer others.

There is other solution i didn't mention before, but because you are interested i will explain it, but before that i will include this small loader, that works perfectly everywhere (i hope) with any adjustment to OS settings, namely the path in environment:
Delphi-Quellcode:
program Loader;
// This application is VCL application and not console/cmd, we don't need the console window to pop every time and scare everyone!!
uses
  Windows;

{$R *.res}

const
  APPLICATION_EXE_NAME = 'DelphiRuntimeTest.exe';
  APPLICATION_EXTRA_COMMANDLINE = '';
  APPLICATION_PATH_TO_ADD ='D:\Program Files (x86)\Embarcadero\Studio\16.0\bin';

procedure SetPathAndRun;
var
  si: TStartupInfo;
  pi: TProcessInformation;
  Path:string;
  PathLen:Integer;  // length in characters
begin
  // adjust the path
  PathLen := GetEnvironmentVariable('PATH',nil,0);
  SetLength(Path,PathLen);
  GetEnvironmentVariable('PATH',@Path[1],PathLen);
  Path := APPLICATION_PATH_TO_ADD +';'+Path;                 // we add out path first to skip handling the null terminator
  SetEnvironmentVariable(PChar('PATH'), PChar(Path));
  //SetEnvironmentVariable(PChar('PATH'), PChar(APPLICATION_PATH_TO_ADD+';%PATH%')); // this might be enough though, instead of all above

  // run and don't wait
  ZeroMemory(@si,SizeOf(si)); //si := Default(TStartupInfo); // ZeroMem for old Delphi versions
  si.cb := sizeof(si);
  CreateProcess(PChar(APPLICATION_EXE_NAME),                 // EXE Name with or without path
    PChar(GetCommandLine + APPLICATION_EXTRA_COMMANDLINE),   // this will pass the same command line to the exe with the extra
    nil, nil, False, 0, nil, nil, si, pi);
end;

begin
  SetPathAndRun;  // will run the exe and exit without waiting
end.
The other solution is to look and understand how packers works, namely UPX for the easy understanding.
UPX is executable packer, that strip and compress sections ... bla bla, we care about one thing here, how it does handle the import table, UPX strip/remove the functions leaving only one from each statically loaded/imported DLL, then when it is unpacking the exe it does resolve the rest, from its own stored and compressed table.
The reason to strip them all, because that table is not optimized on byte level, and might be big, but that is irrelevant as UPX tries to squeeze space every where it can, the question is why it does leave one ?
The answer is: for two reasons first security, it doesn't want to irritate AV(security analyzing software) more than needed, and secondly to keep the OS behavior consistent when loading and handling DLL, so it doesn't go to full length and loaded the libraries on its own and resolve the import addresses, this for compatibility and also will keep the unpacker code as small as it could be.

What can be done here is:
UPX is on GitHub with license GPL-2, but it does some changes, we need to remove the one imported function and replace them with your slightly adjusted table.
This solution should work, but the work and changes could be not-so-practical.

Anyway, wrote this as food for thought.


Alle Zeitangaben in WEZ +1. Es ist jetzt 04:19 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz