Tuesday, December 16, 2003

!DANGER! WILL ROBINSON...

In previous versions of Delphi, when a user built a package, the IDE provided significant assistance in making sure the package was being built correctly by limiting what units got implicitly linked into the package. In Delphi 8 for the Microsoft .NET Framework, much of this assistance isn't being done due to the overwhelming complexity of the problem now that we've moved into the managed space. Currently, when you build a package/assembly, this is ones using the "package" keyword, not using library, which I'll comment on later, the compiler will display warnings when any unit was implicitly linked into the package. Please treat these as ERRORS. In a future release of the D4Net compiler, these warnings will change to errors.

What does this mean?

You must make sure that your "requires" clause refers to the proper Delphi assemblies and your "contains" clause explicitly lists all the units to be included in the package/assembly. What has been happening, is that folks have been blithely ignoring those errors, then proceeding to wonder why their component behaves strangely when they load it into the IDE designer. If you see any "implicitly linked unit" warnings, you need to eliminate them. For instance, if you see a warning that states Borland.Vcl.Classes was implicitly linked into your package, you forgot to add Borland.VclRtl to the "requires" clause. The same if you see Borland.Vcl.Controls implicit link warning, only you forgot Borland.Vcl.

Yes there are cases where this is legitimate, like you want to build your own specific run-time assembly with a mix of VCL units and your custom units. However, the rules regarding the use of an assembly/package such as this haven't changed... you can't build one that links into itself *any* Borland supplied .dcu/.dcuils. or to any non-Borland supplied package/assembly that does.

What's the big deal, its the same code, right?

Yes, the actual machine/IL instructions are the same, however that isn't the only thing in the assembly. There is all that metadata (this is also true for Delphi/win32) that describes certain types, such as classes, records, enums, etc... CLR is a little better at this because it will never mistake one type for another because it always internally deals with types as full assembly qualified names.
Suppose you have two assemblies, A1.dll and A2.dll. You linked into both of these assemblies, the MyComp class. To the CLR, the MyComp class name in A1.dll is actually, "MyNamespace.MyComp, A1" which, to the CLR, is a totally different type than "MyNamespace.MyComp, A2." They are not interchangeable.

But C# lets me do this.

Oh sure it does.. you keep telling yourself that. Yes, C# does allow you to create two assemblies that contain the same class, however it also warns you and will select only one of the MyComp types to use in the calling assembly/.exe. Even Delphi has always allowed you to do this. The problem comes in when you want to load those assemblies/packages into the same process/AppDomain. Consider this scenario, you have assemblies B1.dll and B2.dll, each of which uses A1.dll and A2.dll respectively. B1 and B2 contain all unique types, so all is well... not! What if B1 has a class that descends from "MyNamespace.MyComp, A1" and B2 does the same with A2? Now you have a third assembly/.exe, C, that uses A1, B1 and B2 only. It knows nothing of A2. C constructs an instance of the MyComp descendent from B2. If you did an "as" cast or and "is" test, it will *not* report that it is a MyComp descendent.

If you did the above scenario with Delphi, when it came time to link with A1, B1, and B2, you would also implicitly link with A2. When the compiler goes to link things together, it will discover that you have the same unit in A1 and A2 then fail to link. If the A1 and A2 assemblies were not Delphi written, then the compiler will still detect that it is importing two types that appear to be the same name and it will issue a warning and select one with which to link, just like C#.

So the bottom line is that a type can exist in one and only one place within a process/AppDomain. In fact this has always been the rule for Delphi/Win32 as well. So, again, I find myself saying that the rules have not changed.