Mastering Delphi 7 [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Mastering Delphi 7 [Electronic resources] - نسخه متنی

Marco Cantu

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
توضیحات
افزودن یادداشت جدید

The Structure of a Package

You may wonder whether it is possible to know if a unit has been linked in the executable file or if it's part of a run-time package. Not only is this possible in Delphi, but you can also explore the overall structure of an application. A component can use the undocumented ModuleIsPackage global variable, declared in the SysInit unit. You should never need this variable, but it is technically possible for a component to have different code depending on whether it is packaged. The following code extracts the name of the run-time package hosting the component, if any:


var
fPackName: string;
begin
// get package name
SetLength (fPackName, 100);
if ModuleIsPackage then
begin
GetModuleFileName (HInstance, PChar (fPackName), Length (fPackName));
fPackName := PChar (fPackName) // string length fixup
end
else
fPackName := 'Not packaged';

Besides accessing package information from within a component (as in the previous code), you can also do so from a special entry point of the package libraries, the GetPackageInfoTable function. This function returns some specific package information that Delphi stores as resources and includes in the package DLL. Fortunately, you don't need to use low-level techniques to access this information, because Delphi provides some high-level functions to manipulate it.

You can use two functions to access package information:



  • GetPackageDescription returns a string that contains a description of the package. To call this function, you must supply the name of the module (the package library) as the only parameter.



  • GetPackageInfo doesn't directly return information about the package. Instead, you pass it a function that it calls for every entry in the package's internal data structure. In practice, GetPackageInfo will call your function for every one of the package's contained units and required packages. In addition, GetPackageInfo sets several flags in an Integer variable.



These two function calls allow you to access internal information about a package, but how do you know which packages your application is using? You could determine this information by looking at an executable file using low-level functions, but Delphi helps you again by supplying a simpler approach. The EnumModules function doesn't directly return information about an application's modules; but it lets you pass it a function, which it calls for each module of the application, for the main executable file, and for each of the packages the application relies on.

To demonstrate this approach, I've built a program that displays the module and package information in a TreeView component. Each first-level node corresponds to a module; within each module I've built a subtree that displays the contained and required packages for that module, as well as the package description and compiler flags (RunOnly and DesignOnly). You can see the output of this example in Figure 10.6.


Figure 10.6: The output of the PackInfo example, with details of the packages it uses

In addition to the TreeView component, I've added several other components to the main form but hidden them from view: a DBEdit, a Chart, and a FilterComboBox. I added these components simply to include more run-time packages in the application, beyond the ubiquitous Vcl and Rtl packages. The only method of the form class is FormCreate, which calls the module enumeration function:


procedure TForm1.FormCreate(Sender: TObject);
begin
EnumModules(ForEachModule, nil);
end;

The EnumModules function accepts two parameters: the callback function (in this case, ForEachModule) and a pointer to a data structure that the callback function will use (in this case, nil, because you don't need this). The callback function must accept two parameters— an HInstance value and an untyped pointer—and must return a Boolean value. The EnumModules function will, in turn, call your callback function for each module, passing the instance handle of each module as the first parameter and the data structure pointer (nil in this example) as the second:


function ForEachModule (HInstance: Longint;
Data: Pointer): Boolean;
var
Flags: Integer;
ModuleName, ModuleDesc: string;
ModuleNode: TTreeNode;
begin
with Form1.TreeView1.Items do
begin
SetLength (ModuleName, 200);
GetModuleFileName (HInstance,
PChar (ModuleName), Length (ModuleName));
ModuleName := PChar (ModuleName); // fixup
ModuleNode := Add (nil, ModuleName);
// get description and add fixed nodes
ModuleDesc := GetPackageDescription (PChar (ModuleName));
ContNode := AddChild (ModuleNode, 'Contains');
ReqNode := AddChild (ModuleNode, 'Requires');
// add information if the module is a package
GetPackageInfo (HInstance, nil, Flags, ShowInfoProc);
if ModuleDesc <> '' then
begin
AddChild (ModuleNode, 'Description: ' + ModuleDesc);
if Flags and pfDesignOnly = pfDesignOnly then
AddChild (ModuleNode, 'Design Only');
if Flags and pfRunOnly = pfRunOnly then
AddChild (ModuleNode, 'Run Only');
end;
end;
Result := True;
end;

As you can see in the preceding code, the ForEachModule function begins by adding the module name as the main node of the tree (by calling the Add method of the TreeView1.Items object and passing nil as the first parameter). It then adds two fixed child nodes, which are stored in the ContNode and ReqNode variables declared in the implementation section of this unit.

Next, the program calls the GetPackageInfo function and passes it another callback function, ShowInfoProc, which I'll discuss shortly, to provide a list of the application's or package's units. At the end of the ForEachModule function, if the module is a package the program adds more information, such as its description and compiler flags (the program knows it's a package if its description isn't an empty string).

Earlier, I mentioned passing another callback function (the ShowInfoProc procedure) to the GetPackageInfo function, which in turn calls the callback function for each contained or required package of a module. This procedure creates a string that describes the package and its main flags (added within parentheses), and then inserts that string under one of the two nodes (ContNode and ReqNode), depending on the type of the module. You can determine the module type by examining the NameType parameter. Here is the complete code for the second callback function:


procedure ShowInfoProc (const Name:
string; NameType: TNameType; Flags: Byte;
Param: Pointer);
var
FlagStr: string;
begin
FlagStr := ' ';
if Flags and ufMainUnit <> 0 then
FlagStr := FlagStr + 'Main Unit ';
if Flags and ufPackageUnit <> 0 then
FlagStr := FlagStr + 'Package Unit ';
if Flags and ufWeakUnit <> 0 then
FlagStr := FlagStr + 'Weak Unit ';
if FlagStr <> ' ' then
FlagStr := ' (' + FlagStr + ')';
with Form1.TreeView1.Items do
case NameType of
ntContainsUnit: AddChild (ContNode, Name + FlagStr);
ntRequiresPackage: AddChild (ReqNode, Name);
end;
end;

/ 279