1

My application uses runtime packages and I would like to get a list of all packages that are required by one of these.

Example:

PackA.bpl requires PackB.bpl and PackC.bpl.

How can I get packages required from PackA.bpl at runtime? (In this example, they would be PackB.bpl and PackC.bpl)

Fabrizio
  • 7,603
  • 6
  • 44
  • 104
  • Why do you find yourself in this situation. When writing packages you know this information. Did you forget, or did the package come from elsewhere? If the latter its dependencies should be documented. And what about dynamically loaded packages? – David Heffernan May 12 '15 at 12:51
  • @DavidHeffernan: Hello! PackA.bpl is a dynamically loaded package that's used to download updates for all packages used by application. After running this, PackA is UnLoaded and downloaded packages files are replaced. If another PackA.bpl has been downloaded, there's a backup system that saves the last working PackA.bpl in a backup folder. At the moment, PackA is importing everything. – Fabrizio May 12 '15 at 13:12
  • Another solution is that to save all packages required by PackA in the backup folder, but in this case, I'll prefer to don't set them "manually" because they can change and I'll prefer to reduce possibilities of doing something wrong. – Fabrizio May 12 '15 at 13:12
  • Like I said, you knew the information when you wrote the package and listed its dependencies. – David Heffernan May 12 '15 at 13:17
  • Ok but... Like I said.. "I'll prefer to don't set them "manually" because they can change and I'll prefer to reduce possibilities of doing something wrong.". If there isn't a way to do this dinamically at runtime, as last choice, I'll do it manually. – Fabrizio May 12 '15 at 13:21
  • I've no idea what your problem is. Sorry. I can't understand how you can build a package without understanding its dependencies. What baffles me is how you plan to magic a required package out of thin air when you discover that you need it. – David Heffernan May 12 '15 at 13:30
  • I don't see why you need the dependency list is this situation. If PackA gets updated, you back up PackA. You don't need to back up PackB unless PackB gets updated. You only need to back up both when both are updated simultaneously. – Rob Kennedy May 12 '15 at 13:31
  • I think I understand the problem, @David: How can a BPL discover what was on its DPK file's `requires` list? (I'm assuming we only need load-time dependencies.) Is it necessary to repeat the information in another place, or can the BPL read it from somewhere? But that's the Y in this XY problem; I don't get what X is. – Rob Kennedy May 12 '15 at 13:40
  • @Rob I'm at the same place as you. What is X? Even then, the packages on the requires list are just the start. They in turn have dependencies. – David Heffernan May 12 '15 at 13:46
  • @RobKennedy: Also PackB.bpl and PackC.bpl can be updated, that's why I have to save all files needed for have a PackA.bpl that works. – Fabrizio May 12 '15 at 13:58
  • Why do you need to back up something that didn't change? If PackC changes, then back it up. If it doesn't, then why bother? What I'm describing is known as an *incremental backup*. – Rob Kennedy May 12 '15 at 14:05
  • @DavidHeffernan: Packages can be automatically added to the requires list, just pressing ok button. In certain work conditions, it can happen I don't remember to add the new added package to the "backup file list". – Fabrizio May 12 '15 at 14:05
  • So you need all the dependencies of B and C too. And all of the dependencies of those ones also. And so on. And then any runtime dependencies. And as for "packages can be automatically added to the requires list", well, the requires list can be read by you just as readily as by the compiler. But you do need to recurse. This still feels wrong to me. – David Heffernan May 12 '15 at 14:05
  • @RobKennedy: I don't save PackC if it's not changed. But what if PackC's changed and causes errors in PackA? And what if both PackA and PackC are changed and nothing works? I need to backup a condition where PackA is working, no matter which is the wrong package. – Fabrizio May 12 '15 at 14:11
  • Write code to parse the import table of Package A (you can find this via Google) to find dependencies. For each dependency, parse the import table to find out what it depends on. Rinse and repeat until you have all of them. Or simply be smart enough to keep track of your own files. – Ken White May 12 '15 at 14:11
  • I really appreciate your considerations. I think I'll change way. Thanks to both. – Fabrizio May 12 '15 at 14:15
  • 1
    Back up only the files that changed. (You can back up more, if you want, but why waste the customer's space on duplicate files?) If you discover that PackA doesn't work, then restore everything from that backup. You can't go wrong with that policy. Specifically, if PackC changes, then include it in your backup. Keep each backup set separate: Update 1 creates backup 1, and update 2 creates backup 2. Don't mix the files in backup 1 and backup 2, or else when you restore, you won't know which files are compatible. – Rob Kennedy May 12 '15 at 14:26

1 Answers1

3

You can use GetPackageInfo from SysUtils. Here is an example of using it to enumerate list of units compiled in a package (in your case, in your callback check for ntRequiresPackage instead). For example:

procedure GetRequiredPackageName(const Name: string; NameType: TNameType; Flags: Byte; Param: Pointer);
var
  Names: TStrings absolute Param;
begin
  case NameType of
    ntRequiresPackage:
      Names.Add(Name);
  end;
end;

procedure GetRequiredPackageNames(Module: HMODULE; Names: TStrings);
var
  Flags: Integer;
begin
  Names.BeginUpdate;
  try
    GetPackageInfo(Module, Names, Flags, GetRequiredPackageName);
  finally
    Names.EndUpdate;
  end;
end;

function ListRequiredPackages(HInstance: NativeInt; Data: Pointer): Boolean;
var
  Names: TStrings absolute Data;
  I: Integer;
begin
  Result := True;
  Names.Clear;
  GetRequiredPackageNames(HInstance, Data);
  Writeln(Format('%s requires: %d packages', [ExtractFileName(GetModuleName(HInstance)), Names.Count]));
  if Names.Count > 0 then
    for I := 0 to Names.Count - 1 do
      Writeln(Names[I]);
end;

procedure Main;
var
  Names: TStringList;
  I: Integer;
begin
  Names := TStringList.Create;
  try
    EnumModules(ListRequiredPackages, Names);
    Readln;
  finally
    Names.Free;
  end;
end;

To get the handle of a package by name, you can use GetModuleHandle:

ListRequiredPackages(GetModuleHandle('PackA.bpl'), Names);
Community
  • 1
  • 1
Ondrej Kelle
  • 36,941
  • 2
  • 65
  • 128