Please explain package use - delphi

Please explain package use (in short sentences with small words (for Dummies)).
I just moved from D7 to XE2 and packages seem much more prevelant, and I seem to need to qualify a lot more things.
In D7 I would say uses windows and now I must say uses winapi.windows.
I find that when I call MessageDlg() I can't pass in mtError, I need to qualify it.
Before I go too far down the wrong road - what's the simple solution?
Can I somehow continue to use my old code with package names which I suspect are terminal (that is, for packages A.B.C I have auses C clause).
Can I add one statement somewhere to do this? Or configure the project.
Sorry to sound so dumb :-(

Package use is no different in XE2 than in earlier versions. What is different is that all of Embarcadero's unit names are now prefixed with new Unit Scope Names ("System", "Vcl", "Winapi", etc) to help designate which units belong to the RTL, the VCL, FireMonkey, specific platforms, etc.
You can update your code to fully qualify everything now, if you want to, but you do not have to. You can instead specify the particular scope names in the "Unit Scope Names" setting of the Project Options instead, then no code changes are needed (other than the usual changes when migrating from one version to another).

These are called unit scopes. Because of the new FireMonkey libraries, and the cross-platform support, it's required that you declare which unit you're referring to for types and function declarations.
You can set defaults using Project->Options->Compiler->Unit Scope Names. This is documented as well.

Related

Delphi Prompt to Add to Uses

Just upgraded from Delphi XE to Tokyo, and was hoping for some updated IDE features.
One nice to have feature would be automatically providing options to add a unit to the uses clause. For example, if you reference something in code that isn't in a uses, it would be nice if the IDE prompted you to add the related unit(s).
For example, keeping it simple, in IntelliJ, you might declare a Button, but not yet have added the associated Library to the Import clause.
When this happens, the offending line is highlighted (just like in Delphi), but the IDE will let you add the necessary library with an Alt-Enter. If there are multiple libraries (it knows about it) it will prompt you for the one you want.
Anything like this for Delphi?
Delphi 2007 and later versions support this for most types that it knows about (in the search or library path). (It may have been available in D2005/2006; I don't have them installed anywhere now to check. I know it was not in Delphi 7.)
Put in the type, and use the Refactor menu (Refactor->Find Unit) or press Ctrl+Shift+A.
Here's an example:
It's not 100% effective, but it's a vast improvement over the old way.
(And yes, I know about TArray<string>, before someone chimes in. I just grabbed a quick type that I knew wouldn't be in the default VCL form uses clause for an example.)

Run-time package load vs static linking

So, I have a myself written run-time package. If a package is statically linked, the project that uses has full access to exported data because the compiler has full knowledge of what is imported from it, am I right? But it's also possible to load a package dynamically via LoadPackage(). But, how to work with imported complex data structures like classes then? I couldn't find a feasible way other than constructing complex expressions like using FindClass('TSomeClass') and invoking RTTI to operate on an instance of the imported class.
The compiler has full knowledge of what's in the package because the DCU and DCP files tell it what's there.
The IDE knows what's in the package because it knows how to find the Register procedure in all the units, and that procedure tells the IDE about the available classes.
In most cases, a program knows what's in a package because the program used units from that package, and the compiler assured that mentioning names of things in those units would resolve to corresponding things in the BPL file at run time. This includes mentioning the BPL file in the program's import table, so the OS loads the BPL automatically.
If the list of BPLs you wish to load can only be determined at run time, then you cannot use any units from those packages. You have to load the package dynamically.
There's still the matter of how to use what's in those packages. You could try to discover the entire contents with RTTI. That's no picnic, though. Instead, define an intermediary package that all involved modules will use.
Define an interface or a common base class for all your packages' classes to have. Put the definition of that class in a unit that's in its own package, which we'll call Shared.bpl. Include that package in the "requires" list of all your other packages and your EXE. Now, everything can refer to the shared unit and the common base class.
This is exactly what Delphi itself does. The shared packages are called RTL and VCL. There are several common base classes already defined there, including TComponent. In your case, it sounds like you need some common definitions beyond what TComponent has.
Short answer:
What you need is the compiler/linker to set you up and use DCPs to do all the linking to types and such.
Then loading of BPLs should be delayed/done by you in custom code.
Unfortunately Delphi won't allow this, probably because of political reasons, you could try and hack it though, see below for more thorough answer.
Long answer:
Apperently DCP describe to the compiler/linker/Delphi yadayadayada what's in those packages/DLLs/BPLs type-wise.
These DCPs can be considered the interface to these DLLs/BPLs, these DCPs are probably somehow compiled into the executable.
Then the BPL probably contains the "implementation".
Now here is where the problem begins. These BPLs are "auto-loaded" as somebody else already mentioned by mentioning "import table".
What you could try is "nuking" / altering the import table so that these BPLs are not loaded automatically anymore.
Then you could try loading these BPLs manually.
I am not sure if it's just as simple as doing a "load package" operation.
Perhaps the loading involves more like acquiring pointers to routines/methods/classes, not sure how that works.
However perhaps that code can be re-used, so all you need to do is "hack" into the import table and hack into the "load bpl" and disable it.
Then you should be able to replace that with your own custom loading code and perhaps finally "re-patch" into other importing routines... like routines calling getprocaddress and such if so required, not sure about this last part.
Anyway this is very sloppy from Delphi developers that there is no functionality to do this little "import table"/"load step" yourself.
This is politics at play though, there is no technical reason why this could not be delayed, done custom by you and then call the rest if necessary.
For some reason they do not want you loading these BPLs manually. They probably want you to keep using the IDE this way.
If you were able to load these things manually, maybe the IDE would then no longer be necessary.
Currently the IDE is necessary to specify where to load this stuff from, without the IDE it will get difficult. Though there are probably also some compiler options somewhere to specify the same, though few would use that.
So to me it seems some very weird "tie-in" into this product, which I honestly must admit is pretty fucking retarded.
They did the same with winsock for example which has two different versions, very easy to load this yourself manually so you can choose which version to use instead of always v1 or v2. For easy for them to delay these getprocs and such, yet they do not...
If they did, Delphi would be much more backwards compatible with windows 95/98, currently Delphi exes no longer works for those older operating systems, though there is no real technical reason why it could not work, it basically has to do with which DLLs are loaded, just a few lines of code could very easily make it work.
This is probably again politics as play to make Delphi only support the latest Windows versions a deal struck between Delphi creators and Microsoft.
Same thing with Windows 10, it apperently detects older processors and then refuses to run on them, removing/hacking these few lines of code/instructions will make Windows 10 work on older processors. Surprise ! =D

Is there a quick way to add Unit Scope to my Delphi units?

We're in the process of moving out Delphi XE2 apps over to Delphi XE5 (We have a window :) )
I'm wondering whether, as part of the move, I should be thinkning of adding my own Unit Scope to our internal applications. This question suggests that it's just of case of renaming units as Company.Unit.
Is it as simple as using Rename in the Project Manager?
Am I missing anything?
It's nearly that simple. You can use the Rename action in the project manager and that will rename the file, give the unit a new name, and change references in the .dpr and .dproj file. But that will not rename any references in code.
So, if you list the unit in uses clauses (seems likely that you will), or use the unit name to resolve ambiguous scope, then you will need to change the names there too.
If you don't have too many units it won't be very difficult to make the change in the project manager, and then fix all the errors that the compiler throws at you. If you have a larger project then you may be better scripting the change. I expect that you could go 99% of the way there with a simple regex based script that did the following:
Update references in the .dpr and .dproj files.
Change the file names and the unit names.
Find the uses clauses (interface and implementation sections) and update references there.
This would leave you to deal with the uses that perform scope resolution and my guess is that there would be few enough of these to let the compiler find them all.
I use a great Tool from http://www.easy-ip.net (DELPHI UNIT DEPENDENCY SCANNER) for that task.
You can change the Name of the unit and the DUPS change all your uses clause's.

What does TDataModule.ClassGroup pseudo-property in Delphi XE2 really do?

I tried to copy and paste a component from one data module into another in Delphi XE2. The component was a Fast Report data source link component. The data module was brand new, just created that second, in XE2.
Someone else had the same problem and reported it on quality central as 106369 and same error message leading me to this mysterious DocWiki entry.
So data modules now have a framework affinity, and a designtime-only pseudo property, which according to the docs:
"Because the ClassGroup pseudo-property is used only by the IDE and is not a compiler-generated property (hence, 'pseudo-property'), ClassGroup is not documented in the compiler-generated Libraries Reference. The page you are reading is the documentation for ClassGroup."
So, the first time I even learn this exists is when it blocks me from copy and pasting components into my data module from an existing set of designtime building blocks that I didn't wish to rebuild from scratch.
Once I change the data module affinity, I can paste stuff into data modules without it bugging me. Thank goodness for Google-that-error or I'd be stuck.
If it is intended to help us write cross platform data modules, and yet it only affects the IDE, according to the documentation, that's inconsistent with the warning that you get when you play with this at designtime, here's the error you get if you change it:
EInvalidType : The following component(s) are not available in the specified
class group. This is likely to cause compile or runtime errors.
frxDBSet.TfrxDBDataset.
What I can't see is how that error message can be correct, and the documentation can also be correct.
The warnings seem to suggest compile, link, and runtime errors if this is set incorrectly. Curious minds who want to know what's really going on, want to know: What is this thing about and why did it get added to the data modules in XE2. I anticipate other people will stumble upon this weird feature, with the feeling that they've stepped in something like dinosaur droppings, and want to know what is up with this feature.
My best answer at this point is that a data module affinity for TPersistent which means, in XE2 lingo, that this data module doesn't want non-visual controls in it, that are VCL-specific. In a future version of Delphi, perhaps a similar marker would allow us to mark forms as being "clean of dependencies on the VCL or windows" too?
Update 1: The .PAS source code of your data module stores this pseudo-property in a way that looks a bit like a compiler directive, like this:
implementation
{%CLASSGROUP 'Vcl.Controls.TControl'}
I think it is pretty obviously what its intended use is for, and the documentation you link to is pretty extensive about that purpose.
It is meant to prevent VCL-only components from being placed on a FireMonkey-accessible DataModule, and vice versa.
Since TDataModule is initially framework-neutral, only framework-neutral components can be placed on it.
So obviously, your source DataModule has a different affinity than your new DataModule, which is why copy/paste does not work until you change the affinity of the new DataModule to match the affinity of the source DataModule.

Tool to change a unit name and all references to it

If I make major changes to a unit I like to change its name to make it clear this is a different version and avoid confusion with other archived versions for example:
CSVUtils.PAS becomes CsvUtilsNew.PAS
But all references to it from other units have to be changed manually, eg:
Unit ManiForm
Uses
CSVUtils
becomes
Unit ManiForm
Uses
CsvUtilsNew
This gets very laborious in complex projects.
Icarus is very good at finding all the references, but I cant find any tool that would automatically update all the other units.
Does anyone know of any tools which automatically update unit references?
Many thanks
I would use GExperts Grep Search and Replace.
I would use Notepad++ or UltraEdit to Search&Replace in all files (*.pas, *.dpr, *.dproj) the old unit name with the new unit name. Unless you have variables or functions that have the same name as the unit, this works very well, is fast and does not require any specialized parser.
You could also define a unit alias in your project options.
E.g. CSVUtils=CsvUtilsNew

Resources