how to get all the classes in an application - delphi

I am writing a localization application in which i am reading the DFM information from the application resource through EnumResourceNames API call.
However, the function returns me a name of the form for which the DFM is associated. I tried getting the class from the FindClass, but since this whole operation is coded in a package, the FindClass fails. RegisterClass routine is called from the exe's intialization section.
FindClass works fine when called from within the code written in the exe project. So, i have developed my own registration framework wherein i add all the Form classes, but this is real pain as i need to add the unit of the form and then pass the form class to the RegisterClass routine.
I was hoping if anyone can provide a simple solution of getting all the classes that are in the executable from which the instance of the object can be created by searching the classname.
BTW I am using Delphi 6 Update 2.
Thanks
Rahul W

If the application is calling RegisterClass() and the package is calling FindClass() (or vice versa), that will only work if both the package and the application are compiled with Runtime Packages enabled so they share a single instance of the RTL (which means you have to deploy the RTL and VCL packages alongside your application and package). Otherwise, your application and package will have their own local copies of the RTL instead. In order to share classes in that situation, one project will have to export extra functions that the other project can call when needed to register its local classes in the other project's local class list.
As for detecting the available classes dynamically, that is not possible in D6. The RTTI system did not gain enough detailed information to perform that kind of enumeration until D2010.

Related

Why when we add an android service to an app, delphi automatically include in the dpr of the app the unit of the datamodule of the service?

When i create a new service, then this service will have a main datamodule (DM), and when i add this service to an already existing app, then delphi automatically add to the dpr (in the uses clauses) of this app the unit of the datamodule:
uses ...
...
myService_mainunit;
why ? it's also make after not possible to compile under IOS ...
The IDE has absolutely no idea what your intentions are. It has no clue that you only want to use this for Android, regardless of if it's Android-specific code or not. You have to write the code which tells it this, such as using conditionals.
When you created a new Android Service, the IDE set up a pre-made template for you. This template just happens to use a Data Module to do its work. But when you add the Data Module to a project, the IDE simply does not know why you want to add it. It's just doing its job, it does this for any and every unit you add to your project, regardless of what that unit's purpose is. In fact, there is no such thing as adding a unit to your project and not being in the DPR uses.
"it's also make after not possible to compile under IOS"
Again, this is easily resolved by wrapping platform conditionals around it. Of course, the IDE "takes control" of the DPR file, so perhaps not that easily, as the IDE could malform your code. It is however very possible by not including this Data Module in your project, but place it in a location where your project can find it.

What are the fundamentals of optionally compiling a library or requiring DLL?

I would like to write a component with the ability for a developer to either require a DLL or include a unit in their app which compiles the library with the project, without the DLL.
For example, let's say it's called "MyLibrary". If I include MyLibrary in the uses clause, it will by default require that a DLL be distributed with the application. On the other hand, if another unit MyLibraryImplementation is in the uses clause, it will compile everything inside the app without requiring the DLL.
This component is inside a package installed into the IDE.
I'm not even sure the terminology for this, or anything about how to go about this. I'm very familiar with writing DLL's, and not looking for someone to write any full code.
What are the fundamental things I need to know to make this optional DLL possible?
You probably won't find the result very satisfying. Delphi cannot directly load classes from a DLL. Instead, you'll need to define some class in your MyLibrary unit. You'll also have to implement most of the methods there. You're welcome to make those methods be little more than stubs that delegate all the work to functions in the DLL, though. In the stub constructor, call a DLL function that allocates a data structure and returns a handle. In all three other stub methods, pass that handle to the corresponding DLL functions along with the methods' other arguments.
You'll essentially have three parallel implementations of the class:
The one in MyLibrary.pas that's nothing but stubs.
The one in MyLibrary.dll that backs the stubs.
The one in MyLibraryImplementation.pas that does all the same things as the DLL, but internally.
You can probably avoid duplicating too much, but not completely.
You don't need to do any of this, though. For years, Delphi has already offered comparable functionality built in: packages.
If your customers want to have your class's implementation live in a separate module, they can choose the "build with runtime packages" option in their projects' linker settings. Disabling that setting will cause your code to be compiled in to their EXE files instead, and they won't need to distribute the BPL file anymore.

Creating a new GUID or file ID From a path in delphi

i am using Delphi XE4 to create a Voip program. i am using an outdated VOIP SDK From a company called BigSpeed which is no longer around the current code points to the following path 'C:\Program Files (x86)\BigSpeed Voice SDK\' where the active x controls are stored.
LIBID_bsVoiChatCln: TGUID = '{D2A88515-99E0-4EEE-A030-E5D2AB306A03}';
IID_IbsVoiChatClnX: TGUID = '{5055A626-56A1-4E58-A461-000A69CA3E03}';
DIID_IbsVoiChatClnXEvents: TGUID = '{665DB561-22D3-4624-B55B-4416309A2E03}';
CLASS_bsVoiChatClnX: TGUID = '{BE761C1E-1F6C-46F8-A99B-0AB29C9B2D03}';
How can i create a new GUID and have the program access the active x controls from a new directory.
You don't want to create new GUIDs. The GUIDs are the identifiers of that component. All you want to do, as far as I can tell from the question and your comments, is to register the DLL at a different location.
The ActiveX DLL almost certainly uses self-registration. This means that you can put the DLL somewhere else and register it there. For instance, suppose the DLL is located in:
C:\MyFolder\MyDll.dll
Then you could register it by executing this command:
regsvr32 C:\MyFolder\MyDll.dll
Looks like you do not understand (or do not explain) relations between your program, the library and the GUIDs.
How can i create a new GUID and
1) GUID is just a 128-bit random number. So you can "create new GUID" simply by editing its hexadecimal string. Or you can press Ctrl+Shift+G in Delphi source editor in designtime. In runtime you can use CreateGUID function of SysUtils unit.
http://docwiki.embarcadero.com/CodeExamples/XE5/en/UsingGUIDs_(Delphi)
http://docwiki.embarcadero.com/Libraries/XE2/en/System.SysUtils.CreateGUID
http://en.wikipedia.org/wiki/GUID
But i don't think creating new GUID will do you any good. If anything, it should mean explicitly declared incompatibility with old GUIDs (hence incompatibility with VOIP library)
from a new directory.
2) Why do you think your VoIP library is arranged as a set of ActiveX control ? Just because there are GUIDs there? Not any text file with GUIDs inside would be ActiveX.
ActiveX are specifically arranged Windows servers, that are registered in the registry so that any program could call them. Sometimes you can register them after the fact, if the installer failed it.
http://en.wikipedia.org/wiki/Activex
http://en.wikipedia.org/wiki/Regsvr32
http://support.microsoft.com/?id=207132
So you should read manuals for your library whether they constitute ActiveX or not, and if they do, how to register them in Windows (should be done by the library installer)
If installer does not provide for it, then you can not be sure that the library can work from a different place. Not only your program needs a connection to it, but also the library itself may need connection to its other parts.
have the program access the active x controls
3) If your library really conforms to ActiveX specifications and if it was correctly installed (registered) then you can just import them into Delphi IDE and drop them onto the form like you drop tables and dialogs.
http://docwiki.embarcadero.com/RADStudio/XE3/en/Import_Component_Wizard
http://delphi.about.com/library/howto/htaddactivex.htm
http://www.delphisources.ru/pages/faq/master-delphi-7/content/LiB0125.html
4) if you do not want to drop your VoIP component onto the form, then you can try to create it in runtime with CoCreateInstance. But first you have to read some tutorial about Microsoft COM for beginners. You may miss some advanced concepts, but you should understand the most basic things like how interfaces are similar to and different from classes, how their lifetime is managed, how COM runtime is initialized and finalized for your program and so on.
http://msdn.microsoft.com/en-us/library/windows/desktop/ms686615.aspx
http://delphi.about.com/library/weekly/aa121404a.htm
http://www.delphikingdom.ru/asp/viewitem.asp?catalogid=1135
How to use CoCreateInstance() to get a com object?
https://stackoverflow.com/search?q=%5Bdelphi%5D+cocreateinstance
all the MS Office related examples in c:\RAD Studio\9.0\OCX\Servers\
5) you may also try to bypass the proper Windows ways of locating and loading the server and try to do it yourself, using DllGetClassObject routines in proper DLLs. If the library is permissive, it will work. But if it does rely on Windows-provided services that has a potential for all kinds of crashes and unmet expectations.
https://www.google.com/search?client=opera&q=CoCreateInstance+and+DllGetClassObject&sourceid=opera&ie=utf-8&oe=utf-8&channel=suggest
http://msdn.microsoft.com/en-us/library/windows/desktop/ms680760.aspx
How do I use a COM DLL with LoadLibrary in C++
DllGetClassObject return "No such interface supported" while CoCreateInstance can find it successful
If my memory serves me, You can find examples of that approach in early HTML Help units for delphi. Microsoft HTML Help provides for both late binding using CoCreateInstance and runtime ActiveX servers registry, or early binding towards htmlhlp.ocx treated as DLL. Early versions of HTML Help API for Delphi provided for both options. But i may be wrong here.

Dynamically loaded BPL's sharing code / passing objects

I was toying around with the idea of using dynamically loading BPL's and passing object instances from the main app to a method in a BPL. This poses a problem units between used by the application and by the BPL.
I wrote a small little prototype which did this and was curious how Delphi internally manages differences between classes defined in the app vs. the BPL.
For example, say a basic Widget class like:
TmyWidget = class
private
fId:Integer;
fDescription:String;
public
procedure DoSomething1();
end;
Now the app and the BPL are built using the unit containing TmyWidget class. Later, something changes in TMyWidget and the app is rebuilt, but the BPL is not (or vice-versa.) I added another method DoSomething2() and created an instance of TmyWidget in the app and passed it to the BPL for processing and in the basic example, it worked. But it's obviously fraught with potential problems.
If another dynamically loaded BPL also uses TmyWidget then things get even more interesting. It seems to work, but it definitely doesn't feel ideal.
The main question is - how does one typically pass objects to and from the main application and DLLs or BPLs? I've never attempted it before and likely for a good reason, but I've got this idea that lends itself to this approach...
I'd imagine that the best approach is to serialize the object and pass those bytes over and deserialize it in the DLL/BPL with this process being mindful of potential version differences between the host and the dynamically loaded module but I was hoping the new SimpleSharedMem option might bring this new functionality without the overhead of serialization, but it seems to be not very useful unless you are strict in keeping the app and dll rebuilt on any shared code changes...but in this prototype, the app would stay fairly constant and the dynamically loaded modules would be changing frequently with functionality being added to TmyWidget. (The server app serves as the factory for building TmyWidget's based on client requests and the app would pass instances to the various modules for processing.)
...was curious how Delphi internally manages differences between classes defined in the app vs. the BPL
Delphi manages this by not allowing it. You can't have a unit with the same name in multiple packages at the same time: if you do, you get an error message saying something similar to Package XYZ already contains ABC (haven't seen that in a while...). Since the type name includes the unit name, you can't have the same type in two different packages. Unless it's a Interface defined by it's GUID, but that's a different story.
... how does one typically pass objects to and from the main application and DLLs or BPLs?
You don't pass objects to DLL, that's not a good idea. When you need to pass objects to a BPL, make sure the base class for that BPL is defined into an 3rd BPL.
Example. Polymorphic behavior for your TmyWidget is probably defined using some virtual methods. Make sure you have a TmyWidgetBase class that defines all of those virtual methods, derive all your TmyWidget's from that base class and pass around objects with the type TmyWidgetBase. Make sure the TmyWidgetBase class is in it's own Package.
When I attempted doing this I ended up with an tiny "bootstrap" exe and lot's of BPL's. Essentially all the logic was in BPL's, to facilitate passing objects around.
One of the projects I've worked on has successfully used a large number of runtime packages for more than a decade now so I'll share a few of my experiences dealing with packages.
As Cosmin pointed out different packages cannot contain the same units. If you use implicit linking, by adding a package to the requires clause of another package or by adding a package to the the Runtime packages list in Project Options the compiler will do the work for you and report one of the following error messages:
E2199: Packages '%s' and '%s' both contain unit '%s' (if your compiling project that depends on two packages containing the same unit)
E2200: Package '%s' already contains unit '%s' (if your compiling a package that contains a unit this is already contained in one of the packages it depends on)
If you are using explicit linking, using LoadPackage, a check will normally be attempted a runtime (though it can be circumvented) and raise an:
EPackageError: Cannot load package
'%s.' It contains unit '%s', which is
also contained in package '%s'
Resolving these errors isn't really all that difficult.
If you have two packages that both need to use a unit just let one of them contain the unit and the other one require the first.
If you have two packages that need to use each other's contained unit you'll have to move those units to an new package that both can depend on.
Implicitly linked packages have the advantage that you can directly access class definitions as though they were statically linked. Just add a unit to the uses clause of the unit you need to use it in. The compiler and the runtime environment take care of resolving everything.
Explicitly linked packages will need to rely on class registration in the initialization section.

How do I call Delphi functions in a bpl from an executable?

I have a Delphi application that I have written a fairly simple wrapper .exe for.
Basically, there was a dll that had a bunch of functions, one of which I would call iteratively once my wrapper did what it needed to. I am not in control of this dll file, and will never be.
Well, now this DLL is a BPL, and I'm not sure how to call functions within that file. Thanks in advance.
The easy way to use functions from a package is to "use" the unit that contains the function, call it as usual, and put the package on the list of your project's runtime packages. For that to work, there are a few requirements:
Your project must use the same Delphi version as was used to compile the package.
You must have access to the DCU file for the unit, or at least the DCP file for the package.
The package must exist in the operating system's search path when your program starts.
If you can't satisfy the third requirement, or if you don't want to have the package loaded all the time, then you can call LoadPackage for it instead. The way to make that work is to have another package that is loaded all the time. It will be used by both your project and the package you wish to load. The intermediate package will expose an interface (such as some registration functions, a variable, or a class) that the main package can use to tell the application what its functions are. You won't be able to "use" the main package's unit in your application directly.
If you can't satisfy the first two requirements, then there is the much harder way, which is also what you'd need to do if your application isn't written in Delphi or C++ Builder. Treat the package like an ordinary DLL. Load it with LoadLibrary. Use GetProcAddress to load its Initialize function, and then call it. (Remember that the calling convention is register, not stdcall.) Then load the address of the function you wish to call, keeping in mind that the name of the function has been mangled to include some unit and type information. Call the Finalize function before you call FreeLibrary. Check the source for LoadPackage and UnloadPackage; whether you need to call CheckForDuplicateUnits probably depends on whether you can satisfy requirement number 1.
A BPL is just a DLL with a few specific additions to it. You should have no trouble calling functions from it just like you did with the DLL, with one specific caveat: The BPL has to be built in the same version of Delphi as you're using. This can be a major drawback if you don't have the source code. If this is a problem for you, you should probably talk with whoever created it and ask them to make it back into a DLL.
A BPL can eliminate a lot of DLL problems. If you can statically link it, the border becomes all but transparent. If you have to load it dynamically, you need one DLL-style access function (usually one that returns an object or an interface) and some common type (interface) definitions. All that should be supplied by the maker of the BPL.

Resources