david berneda 50 Posted Friday at 04:07 PM (edited) Hi all ! Do you know if there is some global variable in the RTL that can be used to "register" a class or something? Or some neat trick? 😂 The need is, unit A needs to "talk" to unit B, but they cannot use them, and cannot use any unit C that could act as a common base. Main reason is A and B are units that form part of completely different products, different packages that cannot depend between them. Firemonkey has a nice "service" global registration via interfaces, I'm asking about the same concept in the RTL. regards ! david Edited Friday at 04:08 PM by david berneda Share this post Link to post
Remy Lebeau 1650 Posted Friday at 04:34 PM 12 minutes ago, david berneda said: Do you know if there is some global variable in the RTL that can be used to "register" a class or something? Or some neat trick? 😂 System.Classes.RegisterClass()? It is not clear what you are actually looking for. Please clarify. Can you provide an example of what you want to accomplish? 12 minutes ago, david berneda said: The need is, unit A needs to "talk" to unit B, but they cannot use them, and cannot use any unit C that could act as a common base. Maybe using custom window messages, or System.Messaging.TMessageManager ? 12 minutes ago, david berneda said: Main reason is A and B are units that form part of completely different products, different packages that cannot depend between them. Are they separate packages in the same process, or are they in separate processes? It makes a big difference depending on whether you have to cross the process boundary or not. 12 minutes ago, david berneda said: Firemonkey has a nice "service" global registration via interfaces, I'm asking about the same concept in the RTL. There is nothing like that framework in the common RTL. And it wouldn't make sense anyway if your two units can't share a common unit that defines what they need to share. Share this post Link to post
david berneda 50 Posted Friday at 05:15 PM Thanks for your help Remy ! In the same process (the ide at designtime). RegisterClass is not enough because both units should share a living instance. TMessageManager can be maybe a good solution, first unit listens and second unit asks, passing an integer address of an instance, then both units can cast it to a class. I'll try this ! Share this post Link to post
dummzeuch 1686 Posted Friday at 06:05 PM Shared memory, a named pipe, sockets or even a file comes to mind. Share this post Link to post
david berneda 50 Posted Friday at 06:43 PM Yep ! but I need a cross platform vcl / fmx way. TMessageManager is ideal and works since XE3 2012 Hope it works ! Share this post Link to post
Stefan Glienke 2159 Posted Friday at 10:04 PM I would assume that both products are from you, though? Then why not make both use a shared package that contains that shared global state? Share this post Link to post
david berneda 50 Posted Friday at 10:58 PM Ah ! That's exactly what I'd like to avoid, a shared package. Potential versionitis issues if A and B where compiled with a different version of the shared unit/package. Source code is not always available to recompile. Share this post Link to post
Uwe Raabe 2169 Posted Friday at 11:41 PM 33 minutes ago, david berneda said: Potential versionitis issues if A and B where compiled with a different version of the shared unit/package. 6 hours ago, david berneda said: both units should share a living instance. What about that living instance? Where is it declared and where does it live? Will interchanging it survive being compiled with different versions of A and B? Even being compiled with different compiler settings may cause a crash. Share this post Link to post
Dave Novo 58 Posted Saturday at 03:04 AM The vmt of all Delphi classes has vmautotable that is not used. I have patched a particular class vmt and in the autotable slot and stuck a reference to an object in there. So both of your units can check that reference on some class from the vcl that you pick. Then they can patch the class. I dont remember the patching code but if you cannot find it on the web let me know and I can dig it up. 1 Share this post Link to post
Kas Ob. 155 Posted Saturday at 07:00 AM 7 hours ago, david berneda said: Ah ! That's exactly what I'd like to avoid, a shared package. Potential versionitis issues if A and B where compiled with a different version of the shared unit/package. Source code is not always available to recompile. I am not a fan of interfaces, but they have their shining moment, this is one of them, if you introduced C and only used by interfaces then compiler, versioning and implementation should not matter, it will work always. Just take your time to implement it future proof as much as you can think of, don't overengineer it, just keep it simple with multiple interfaces instead of one if possible, also use versioned (numbered) interfaces (in the name) to indicate to yourself and as reminder that these interfaces should not be touched/altered, if in the future need a feature then implement new interface and keep the older. 1 Share this post Link to post
david berneda 50 Posted Saturday at 07:13 AM 7 hours ago, Uwe Raabe said: What about that living instance? Where is it declared and where does it live? Will interchanging it survive being compiled with different versions of A and B? Even being compiled with different compiler settings may cause a crash. The instance can be for example a simple component, and casting one of its children components in a safe way, ie: "as xxx". The global thing I'm after is, ideally a TObjectDictionary<String> where any unit can add its own object and other units can query and obtain it using for example a string key. This would work across packages, design and runtime, vcl / fmx, everywhere. initialization // unit B Globals.Add('MyKey', MyObject); // unit A if Globals.Exist('MyKey') then ... Then the issue is to cast the object to well known existing types to avoid the potential crash you describe. In my particular need, one unit "A" shows a dialog and looks for a global control that maybe another unit "B" has created. If that control exists, it is used in dialog A. So A and B don't know and don't use each other directly or indirectly, but the end result is for example a tabsheet created in B is displayed in A. Share this post Link to post
david berneda 50 Posted Saturday at 11:56 AM 12 hours ago, Uwe Raabe said: What about that living instance? Where is it declared and where does it live? Will interchanging it survive being compiled with different versions of A and B? Even being compiled with different compiler settings may cause a crash. The instance can be for example a simple component, and casting one of its children components in a safe way, ie: "as xxx". The global thing I'm after is, ideally a TObjectDictionary<String> where any unit can add its own object and other units can query and obtain it using for example a string key. This would work across packages, design and runtime, vcl / fmx, everywhere. initialization // unit B Globals.Add('MyKey', MyObject); // unit A if Globals.Exist('MyKey') then ... Then the issue is to cast the object to well known existing types to avoid the potential crash you describe. In my particular need, one unit "A" shows a dialog and looks for a global control that maybe another unit "B" has created. If that control exists, it is used in dialog A. So A and B don't know and don't use each other directly or indirectly, but the end result is for example a tabsheet created in B is displayed in A. Share this post Link to post
david berneda 50 Posted Saturday at 12:01 PM Grok also suggest using TMessageManager and provides this example code: unit UnitA; interface uses System.Messaging; procedure SendMyValue(const Value: Integer); implementation procedure SendMyValue(const Value: Integer); begin TMessageManager.DefaultManager.SendMessage(nil, TMessage<Integer>.Create(Value)); end; end. unit UnitB; interface uses System.Messaging; procedure StartListening; implementation var SubscriptionID: Integer; procedure MyHandler(const Sender: TObject; const M: TMessage); begin if M is TMessage<Integer> then begin ShowMessage('Value: ' + IntToStr(TMessage<Integer>(M).Value)); end; end; procedure StartListening; begin SubscriptionID := TMessageManager.DefaultManager.SubscribeToMessage(TMessage<Integer>, MyHandler); end; procedure StopListening; begin TMessageManager.DefaultManager.Unsubscribe(TMessage<Integer>, SubscriptionID); end; end. So, hope it works with more elaborated types instead of Integer (ie, TObject etc) There are many useful applications of this mechanism, with no dependencies. Specially 3rd parties can interact each other without the need of "requiring" each other packages, which is a versionitis nightmare. We could add some layer on top of this to make it easier and standard. Share this post Link to post
Pat Foley 54 Posted Saturday at 12:01 PM (edited) 5 hours ago, david berneda said: So A and B don't know and don't use each other directly or indirectly, but the end result is for example a tabsheet created in B is displayed in A. Use class names vs class types to avoid adding B in uses clauses. Existing controls and forms are renamed to fit existing inifile naming scheme. And as the controls loaded, if their parent is not found, they make one--example a Serie or line needs a Chart if not found we make a chart parenting it to the named form or control. So all Tee.* is only in mainform BOSS uses. letting the Boss code update the charts and legends parented elsewhere. Use ComponentClass and add "fixup" and parenting later for UI controls and List<TMyControl>for non-visual controls. A startup inifile is embedded in file to allow the program to run in "Demo" when inifile not found. Mainly to build menu and tree Navigation. But more importantly reduce the need for global variables by using 'names'. Then with empty uses clauses we stick in a DM needed to share Imagelist. and a Boss unit that reads in the controls needed. Edited Saturday at 12:24 PM by Pat Foley added purpose of Boss Share this post Link to post
Remy Lebeau 1650 Posted Saturday at 03:43 PM (edited) 22 hours ago, david berneda said: In the same process (the ide at designtime). 21 hours ago, david berneda said: Yep ! but I need a cross platform vcl / fmx way Why? You said you are creating IDE design-time packages. So they run in the IDE process, and the IDE is not itself a cross-platform application, it is a Windows-only VCL application. What am I missing? Can you provide more exact details of what you are actually trying to accomplish? Edited Saturday at 03:47 PM by Remy Lebeau Share this post Link to post
david berneda 50 Posted Saturday at 03:57 PM Yes the same vcl dialogs can be used outside the ide at runtime, and there are also fmx equivalent dialogs, with the same need and purpose. Share this post Link to post
david berneda 50 Posted Saturday at 06:17 PM 2 hours ago, Remy Lebeau said: What am I missing? Can you provide more exact details of what you are actually trying to accomplish? Edited 2 hours ago by Remy Lebeau Yes, I need a general mechanism to primarily allow embedding controls (vcl and fmx) into for example a tabsheet, like hooking when a form shows, it calls an event and that results in a new tab filled with stuff. No problem at all to do this when units are used in a traditional way. I've been doing that for 30 years now since D3 😆 The wish is to be able to do the same without units dependency, nor a common base unit or a common required package. This way users can install a product/packages/sources, and after that maybe install another product/packages/sources, and suddenly this new product dialogs appear at the first product. Share this post Link to post
Anders Melander 2087 Posted Saturday at 08:13 PM It sounds like you're about to reinvent [shudder] ActiveX. 2 Share this post Link to post