4

I need to change the functionality in a component. What do you do when you get a "you can't override this" message, or you need to change the code in a private method ("method does not exist in base class" message), rendering it impossible to descend the component?

Glenn1234
  • 2,542
  • 1
  • 16
  • 21

2 Answers2

4

There are some (mainly hacky) options when it comes to modifying private methods or behavior therein:

  • modify the original source, recompile the unit and use the changed dcu as suggested here; never did this but I think this can cause you a good headache when your code uses the new dcu but other VCL code don't
  • often component behavior is controlled by numerous window messages - look if you can achieve your change by modifying the reaction on some of these messages; you can override the message handling methods (the ones with the message keyword) even if they are declared private and you can also replace the WndProc
  • you can use hacks like this one which is tinkering with casting
  • you could use some detour mechanism as described in the answers here

Or you can get another component.

Community
  • 1
  • 1
Heinrich Ulbricht
  • 10,064
  • 4
  • 54
  • 85
  • 1
    To your first point: I'd rather copy the unit, rename it and the classes I change, and remove everything I don't need (if necessary, "including" the original unit to define constants, types used, etc.). That avoids most of the headaches. Never modify the original sources, that is a maintenance nightmare. – Rudy Velthuis Aug 16 '11 at 22:04
  • I agree. This was more in the sense of "modify, compile, use dcu, undo modify". But your approach sounds even better. Just copy the unit and change it as needed. The downside still is that with frequently updated third-party components it also requires some maintenance. But if it's the VCL then the next bugfix/update will be the next version of Delphi, so nothing to worry about. – Heinrich Ulbricht Aug 16 '11 at 22:17
4

If I face that problem,

  • I first try to inherit from the component, or its CustomXXX ancestor and see if that fixes the problem. If that doesn't,
  • I go deeper, i.e. try to intercept the messages that come in. That can be done dynamically. If that turns out to be too deep, because the code that has to be built on that is too extensive, or if I still have to access items I can't access,
  • I try hacks. One hack is to copy the component and the dependent code to a new unit with a different name, rename the component and modify what needs to be modified.
  • Sometimes I only need to redo one or two methods to make my new behaviour possible.

Never forget to give the unit a different name and the component a different name too (possibly inheriting from the original component or one of its ancestors, so they remain in the same hierarchy). Do never modify the original sources and then recompile the VCL. That is a maintenance nightmare.

I am no fan of interposer classes, i.e. classes that get the same name but different behaviour than the original classes, inheriting from the original. Their functionality depends on the order of inclusion in the uses clause, and that seems rahter flaky to me. I can't recommend that.

But what I do greatly depends on the problem. I don't think one can (or should) give a blanket advice that covers all situations.

But my main advice: do not modify the original units, always put the new code in a new unit and use a new class name. That way the original and the modified versions can peacefully co-exist, also in the IDE.

Rudy Velthuis
  • 28,387
  • 5
  • 46
  • 94
  • 2
    As long as runtime packages are disabled, and you do not need to make any changes to the `interface` section, then you do not need to rename the unit or its classes. Just make a copy of the unit that local to your project, add the copy to your project, and then modify its `implementation` section as needed. It will override the VCL's native code during compiling. – Remy Lebeau Aug 16 '11 at 22:53
  • Sorry Remy, but NOT doing that is, IMO, bad practice and a maintenance nightmare. The renaming of the unit and the class is IMO the only graceful way to do this. It is technically not necessary, but not renaming is IMO bad practice. – Rudy Velthuis Aug 16 '11 at 23:04
  • 1
    Changing the unit and class names requires changing code that refers to them. It is not uncommon to maintain the original names so existing code does not need to be changed, especially if the intent is to change/fix internal behavior (ie, to fix RTL/VCL bugs). – Remy Lebeau Aug 17 '11 at 00:51
  • 1
    I'll side with Remy here. On occasions when I am testing things (although I have never done this in deployment) I just copy across a VCL source unit to a local directory, edit, and recompile. IMHO, renaming the class, when it is just the same class modified, IS the maintenance nightmare. If the "bug"/functionality is fixed in a later Delphi version then all you need to do is archive the unit and you are back to the original VCL. This wouldn't be possible if you rename units and classes. And it is easier to merge in new unit versions from future releases. – Misha Aug 17 '11 at 04:13
  • `Changing the unit and class names requires changing code that refers to them.` Yes, indeed, as this shows that you are using a modified component. Modifying the originals in the VCL is, IMO, a bad idea, as it makes your VCL incompatible with the VCL others have and it makes your code a maintenance nightmare. Any bug might be due to your modifications or to the original code. If you separate them, it is clear what happens. – Rudy Velthuis Aug 17 '11 at 06:43
  • @Misha: see my reply to Remy. Modifying the original is bad practice, IMO, as you have a different VCL as everyone else has. You, and any other maintainer of the code, ***should*** be able to see the component you use is not the original. If you modify the originals, you know this now, but do you also remember it in a year from now? – Rudy Velthuis Aug 17 '11 at 06:46
  • I recently had to fix multiple bugs in Menus.pas in D2010. The code for Vista themed menus is irredeemably broken. But it's not feasible to fix it by introducing a TMyMainMenu, TMyPopupMenu etc. since there are so many hooks into TMainMenu from other VCL classes. By far the cleanest approach is to put a modified Menus.pas in your project. Regarding knowing which version of the control is being used, in my experience you always want the fixed version! Why would you want the version with bugs? – David Heffernan Aug 17 '11 at 07:49
  • @Rudy, to be frank, who cares if I have a different VCL to everyone else? I am only interested in getting my code working - I don't realy care about others' code. And it would be kind of obvious that the VCL was modified by the presence of the unit in the project! If I worked on a project that used a modified VCL I wouldn't be interested at all in knowing whether a particular unit had changed if it all worked. – Misha Aug 17 '11 at 11:28
  • @David, Misha: Do whatever you like. IMNSHO, it is bad practice to modify the originals or copies of the originals. And if you don't rename them, you might get errors as well (XYZ uses a different version of ABC, etc.etc.). I never do that, and I always fared very well with the renamed components and units. Modifying the originals is in no way clean approach. On the contrary. -- FWIW, also read the license. They don't like it either. – Rudy Velthuis Aug 17 '11 at 12:20
  • @rudy When you get the uses a different version error the code doesn't build. I've never shipped when the code doesn't build. I agree that deriving a new class and not modifying the VCL source is to be preferred, if possible. However sometimes it is not possible. I always prefer shipping working solutions rather than adhering blindly to dogmatic rules. Very little in software is black and white. – David Heffernan Aug 17 '11 at 12:27
  • @David: I can't imagine where it is not possible to use a different class name and different unit. Modifying the VCL code is, IMO, a strict no-no. Even your TMyMainMenu, if it inherits from TMainMenu, should be usable, AFAICT. – Rudy Velthuis Aug 17 '11 at 18:55
  • OK then, how would you fix the broken hyperbolic trig functions in Math? Or how would you make MakeObjectInstance threadsafe for worker thread timers? What's more, inheriting from a class does not let you modify private methods or access private fields. – David Heffernan Aug 17 '11 at 19:15
  • Note that I never modify files under the delphi Source directory, that would be very bad practice. I copy the file in question to my project folder and include it in my project. That way I know I get the version with the fixes. But that's a last resort if deriving or hooking can't get the job done. – David Heffernan Aug 17 '11 at 19:24
  • I would fix the hyperbolic functions and give them different names and give the unit with my implementations a different name. I would write my own version of MakeObjectInstance, give it a different name and use that. Inheriting does indeed not let me use private methods or access private fields. That is the main reason to use "copy and paste inheritance", isn't it? I would still rename the class with the modified implementation. I would, however, probably make it inherit from the original class or its direct ancestor. – Rudy Velthuis Aug 17 '11 at 19:29
  • I understood you copy the original unit to your source directory and then modify it. I also rename it and give the classes a different name. For the few times I actually needed it, that worked fine for me and avoided any ambiguities. I usually removed the code that was not needed and included the original unit for any constants, types, enums, etc. required. I think everyone knows that doing that is a last resort measure. – Rudy Velthuis Aug 17 '11 at 19:31
  • My way ensures that nobody can call the wrong function or instantiate the wrong class because they have all been excised. That's the main benefit as others above have said. Everyone uses same code because we have revision control of course! I can easily see the small number of changes made when I need to reapply patches to next version of Delphi. Your approach is the maintenence nightmare. – David Heffernan Aug 17 '11 at 20:16
  • Also please can you use an @name in comments so that we get notified – David Heffernan Aug 17 '11 at 20:17
  • @David: sorry, got carried away and forgot to add the @ name tag. And my bloody OS X Lion keeps having problems with the wifi, and I think that was the other reason I forgot. – Rudy Velthuis Aug 17 '11 at 20:20
  • The advice here is frankly impractical, given by someone clearly lacking the real world experience necessary to understand the issues. *But my main advice: do not modify the original units, always put the new code in a new unit and use a new class name. That way the original and the modified versions can peacefully co-exist, also in the IDE.* Very often that's exactly what you don't want to do. If the original code is broken and simply does not work, why would we want to keep using it?! – David Heffernan May 27 '15 at 19:22
  • I don't see why you don't want to do that. If you are working with a hack, it should be very clear it is a hack (and copy-and-paste "inheritance" is always a hack), and that it can (and should) be removed as soon as the original issue is resolved. Well, renaming it appropriately is the best indication you can give. – Rudy Velthuis May 28 '15 at 10:54
  • FWIW, I assume you only use this hack to get around bugs in the original RTL/VCL/supplied code. If that is not the case, this is the wrong approach anyway, IMO. – Rudy Velthuis May 28 '15 at 10:58