Delphi: Codecompletion to override basemethods - delphi

In a class declaration, you can press Ctrl+Space to get a list of virtual methods in the baseclass that you can override.
This list seems to be very limited, though. Ex.
TMyBaseClass = class(TInterfacedObject)
protected
procedure mymethod; virtual;
end;
TMyClass = class(TMyBaseClass)
protected
{Ctrl+Space here...}
end;
In TMyClass, I get methods from TInterfacedObject and TObject, but not from TMyBaseClass. Why is that?
-Vegar
EDIT: Forgot my delphi version... I'm using 2007.

Because sometimes these IDE tools work and sometimes they don't (and afaik this is not a problem that is unique to Delphi/RAD Studio).
For example, if you have other edits outstanding which mean that the IDE cannot adequately parse your source up to the current insertion point, then it is likely that such things will be "broken".
In such cases I personally try a quick Ctrl+F9 to see if there are compilation errors that I perhaps may not be aware of. 9 times out of 10 there are, and fixing those then fixes the code insight behaviour.
Recreating your example verbatim in a new unit, I get "mymethod" in my code completion list as expected, so is it possible that in your actual case that your base class and your derived class are actually separated by other code, perhaps even in separate units, and that that other code contains errors "between" the two declarations at the time at which you are trying to invoke code completion?
As a more general point, ime it's better to treat such things as "nice to haves when they work" but to try to develop habits and practices that don't rely on them. In this case the only substitute is knowledge of the methods to be overridden, which isn't much help I admit.

Related

Empty Functions - Placeholder?

so I was just looking through the Code of our inventory management system
and I saw some Snippet's of one of my Coworkers, all he does in some Functions is
simply open them and insert a command in there for example
procedure TWerkStF.TBBtnStatiClick(Sender: TObject);
begin
inherited;
//
end;
so i am wondering when you should do something like that and when is it usefull, are there some benefits?
This is not useful, and has no benefits. Such a function can, and should, be removed.
The function in the question, by using inherited, simply searches in the super classes for a function of the same name, and if one is found calls it. If one is not found then no action is performed. As a rule, such a function, one that only calls inherited, does not modify the behaviour of the program. You can remove it without changing behaviour.
We use such functions/procedures most times to tell other Co-Workers where they should not change anything, the reason for that is because some events are just not fitting the needs of some Modules, therefore we "warn" each other with empty Functions.
Sounds wiered, i know.

Is there an easy way to work around a Delphi utf8-file flaw?

I have discovered (the hard way) that if a file has a valid UTF-8 BOM but contains any invalid UTF8 encodings, and is read by any of the Delphi (2009+) encoding-enabled methods such as LoadFromFile, then the result is a completely empty file with no error indication. In several of my applications, I would prefer to simply lose a few bad encodings, even if I get no error report in this case either.
Debugging reveals that MultiByteToWideChar is called twice, first to get the output buffer size, then to do the conversion. But TEncoding.UTF8 contains a private FMBToWCharFlags value for these calls, and this is initialized with a MB_ERR_INVALID_CHARS value. So the call to get the charcount returns 0 and the loaded file is completely empty. Calling this API without the flag would 'silently drop illegal code points'.
My question is how best to weave through the nest of classes in the Encoding area to work around the fact that this is a private value (and needs to be, because it is a class var for all threads). I think I could add a custom UTF8 encoding, using the guidance in Marco Cantu's Delphi 2009 book. And it could optionally raise an exception if MultiByteToWideChar has returned an encoding error, after calling it again without the flag. But that does not solve the problem of how to get my custom encoding used instead of Tencoding.UTF8.
If I could just set this up as a default for the application at initialization, perhaps by actually modifying the class var for Tencoding.UFT8, this would probably be sufficient.
Of course, I need a solution without waiting to lodge a QC report asking for a more robust design, getting it accepted, and seeing it changed.
Any ideas would be very welcome. And can someone confirm this is still an issue for XE4, which I have not yet installed?
I ran into the MB_ERR_INVALID_CHARS issue when I first updated Indy to support TEncoding, and ended up implementing a custom TEncoding-derived class for UTF-8 handling to avoid specifying MB_ERR_INVALID_CHARS. I didn't think to use a class helper.
However, this issue is not just limited to UTF-8. Any decoding failure of any of the TEncoding classes will result in a blank result, not an exception being raised. Why Embarcadero chose that route, when most of the RTL/VCL uses exceptions instead, is beyond me. Not raising an exception on error caused a fair amount of issues in Indy that had to be worked around.
This can be done pretty simply, at least in Delphi XE5 (have not checked earlier versions). Just instantiate your own TUTF8Encoding:
procedure LoadInvalidUTF8File(const Filename: string);
var
FEncoding: TUTF8Encoding;
begin
FEncoding := TUTF8Encoding.Create(CP_UTF8, 0, 0);
// Instead of CP_UTF8, MB_ERR_INVALID_CHARS, 0
try
with TStringList.Create do
try
LoadFromFile(Filename, FEncoding);
// ...
finally
Free;
end;
finally
FEncoding.Free;
end;
end;
The only issue here is that the IsSingleByte property for the newly instantiated TUTF8Encoding is then incorrectly set to False, but this property is not currently used anywhere in the Delphi sources.
A partial workaround is to force the UTF8 encoding to suppress MB_ERR_INVALID_CHARS globally. For me, this avoids the need for raising an exception, because I find it makes MultiByteToWideChar not quite 'silent': it actually inserts $fffd characters (Unicode 'replacement character') which I can then find in the cases where this is important. The following code does this:
unit fixutf8;
interface
uses System.Sysutils;
type
TUTF8fixer = class helper for Tmbcsencoding
public
procedure setflag0;
end;
implementation
procedure TUTF8fixer.setflag0;
{$if CompilerVersion = 31}
asm
XOR ECX,ECX
MOV Self.FMBToWCharFlags,ECX
end;
{$else}
begin
Self.FMBToWCharFlags := 0;
end;
{$endif}
procedure initencoding;
begin
(Tencoding.UTF8 as TmbcsEncoding).setflag0;
end;
initialization
initencoding;
end.
A more useful and principled fix would require changing the calls to MultiByteToWideChar not to use MB_ERR_INVALID_CHARS, and to make an initial call with this flag so that an exception could be raised after the load is complete, to indicate that characters will have been replaced.
There are relevant QC reports on this issue, including 76571, 79042 and 111980. The first one has been resolved 'as designed'.
(Edited to work with Delphi Berlin)
Your "global" approach is not really global - it relies upon the assumption that all the code would only use one and the same instance of TUTF8Encoding. The same instance where you hacked the flags field.
But it would not work if one obtain TUTF8Encoding object(s) by other means than TEncoding.GetUTF8, for example in XE2 another method - TEncoding.GetEncoding(CP_UTF8) - would create a new instance of TUTF8Encoding instead of re-using FUTF8 shared one. Or some function might run TUTF8Encode.Create directly.
So i'd suggest two more approaches.
Approach with patching the class implementation, somewhat hacky. You introduce your own class for the sake of obtaining new "fixes" constructor body.
type TMyUTF8Encoding = class(TUTF8Encoding)
public constructor Create; override;
end;
This constructor would be the copycat of TUTF8Encoding.Create() implementation, except for setting the flag as you want it ( in XE2 it is done by calling another, inherited Create(x,y,z) so u would not need an access to the private field ) instead.
Then you can patch the stock TUTF8Encoding VMT overriding its virtual constructor to that new constructor of yours.
You may read Delphi documentation about "internal formats" and so forth, to get the VMT layout. You would also need calling VirtualProtect (or other platform-specific function) to remove protection from VMT memory area before patching and then to restore it.
Examples to learn from
How to change the implementation (detour) of an externally declared function
https://stackoverflow.com/a/1482802/976391
Or you may try using Delphi Detours library, hopefully it can patch virtual constructors. Then... it might be an overkill here to use that rather complex lib for that single goal.
After you hacked the TUTF8Encoding class do call the TEncoding.FreeEncodings to remove the already created shared instances (if any) if any and thus trigger recreating the UTF8 instances with your modifications.
Then, if you compile your program as a single monolithic EXE , without using runtime BPL modules, you just can copy the SysUtils.pas sources to your application folder and then to include that local copy into your project explicitly.
How to patch a method in Classes.pas
There you would change the very TUTF8Encoding implementation as you see fit in the sources and Delphi would use it.
This brain-deadly simplistic (hence - equally reliable) approach would not work though if your projects would be built to reuse rtlNNN.bpl runtime package instead of being monolithic.

How to debug a (possible) RTL problem?

I'm asking this because I'm out of good ideas...hoping for someone else's fresh perspective.
I have a user running our 32-bit Delphi application (compiled with BDS 2006) on a Windows 7 64-bit system. Our software was "working fine" until a couple weeks ago. Now suddenly it isn't: it throws an Access Violation while initializing (instancing objects).
We've had him reinstall all our software--starting all over from scratch. Same AV error. We disabled his anti-virus software; same error.
Our stack tracing code (madExcept) for some reason wasn't able to provide a stack trace to the line of the error, so we've sent a couple error logging versions for the user to install and run, to isolate the line which generates the error...
Turns out, it's a line which instances a simple TStringList descendant (there's no overridden Create constructor, etc.--basically the Create is just instancing a TStringList which has a few custom methods associated with the descendant class.)
I'm tempted to send the user yet another test .EXE; one which just instances a plain-vanilla TStringList, to see what happens. But at this point I feel like I'm flailing at windmills, and risk wearing out the user's patience if I send too many more "things to try".
Any fresh ideas on a better approach to debugging this user's problem? (I don't like bailing out on a user's problems...those tend to be the ones which, if ignored, suddenly become an epidemic that 5 other users suddenly "find".)
EDIT, as Lasse requested:
procedure T_fmMain.AfterConstruction;
begin
inherited;
//Logging shows that we return from the Inherited call above,
//then AV in the following line...
FActionList := TAActionList.Create;
...other code here...
end;
And here's the definition of the object being created...
type
TAActionList = class(TStringList)
private
FShadowList: TStringList; //UPPERCASE shadow list
FIsDataLoaded : boolean;
public
procedure AfterConstruction; override;
procedure BeforeDestruction; override;
procedure DataLoaded;
function Add(const S: string): Integer; override;
procedure Delete(Index : integer); override;
function IndexOf(const S : string) : Integer; override;
end;
implementation
procedure TAActionList.AfterConstruction;
begin
Sorted := False; //until we're done loading
FShadowList := TStringList.Create;
end;
I hate these kind of problems, but I reckon you should focus on what's happening recently BEFORE the object tries to get constructed.
The symptoms you describe sound like typical heap corruption, so maybe you have something like...
An array being written to outside bounds? (turn bounds checking on, if you have it off)
Code trying to access an object which has been deleted?
Since my answer above, you've posted code snippets. This does raise a couple of possible issues that I can see.
a: AfterConstruction vs. modified constructor:
As others have mentioned, using AfterConstruction in this way is at best not idiomatic. I don't think it's truly "wrong", but it's a possible smell. There's a good intro to these methods on Dr. Bob's site here.
b: overridden methods Add, Delete, IndexOf
I'm guessing these methods use the FshadowList item in some way. Is it remotely possible that these methods are being invoked (and thus using FShadowList) before the FShadowList is created? This seems possible because you're using the AfterConstruction methods above, by which time virtual methods should 'work'. Hopefully this is easy to check with a debugger by setting some breakpoints and seeing the order they get hit in.
You should never override AfterConstruction and BeforeDestruction methods in your programs. They are not meant for what you're doing with them, but for low-level VCL hacking (like reference adding, custom memory handling or such).
You should override the Create constructor and Destroy destructor instead and put your initialization code here, like such:
constructor TAActionList.Create;
begin
inherited;
// Sorted := False; // not necessary IMHO
FShadowList := TStringList.Create;
end;
Take a look at the VCL code, and all serious published Delphi code, and you'll see that AfterConstruction and BeforeDestruction methods are never used. I guess this is the root cause of your problem, and your code must be modified in consequence. It could be even worse in future version of Delphi.
Clearly there is nothing suspicious about what TAActionList is doing at time of construction. Even considering ancestor constructors and possible side-effects of setting Sorted := False indicate there shouldn't be a problem. I'm more interested in what's happening inside T_fmMain.
Basically something is happening that causes FActionList := TAActionList.Create; to fail, even though there is nothing wrong in the implementation of TAActionList.Create (a possibility is that the form may have been unexpectedly destroyed).
I suggest you try changing T_fmMain.AfterConstruction as follows:
procedure T_fmMain.AfterConstruction;
begin
//This is safe because the object created has no form dependencies
//that might otherwise need to be initialised first.
FActionList := TAActionList.Create;
//Now, if the ancestor's AfterConstruction is causing the problem,
//the above line will work fine, and...
inherited AfterConstruction;
//... your error will have shifted to one of these lines here.
//other code here
end;
If an environment issue with a component used by your form is causing it destroy the form during AfterConstruction, then it's the assignment of the new TAActionList.Create instance to FActionList that's actually causing the AV. Another way to test would be to first create the object to a local variable, then assign it to the class field: FActionList := LActionList.
Environment problems can be subtle. E.g. We use a reporting component which we discovered requires that a printer driver is installed, otherwise it prevents our application from starting up.
You can confirm the destruction theory by setting a global variable in the form's destructor. Also you may be able to output a stack trace from the destructor to confirm the exact sequence leading to the destruction of the form.
Our software was "working fine" until a couple weeks ago... suddenly become an epidemic that 5 other users suddenly "find".) :
Sounds like you need to do some forensic analysis, not debugging: You need to discover what changed in that user's environment to trigger the error. All the more so if you have other users with the same deployment that don't have the problem (sounds like that's your situation). Sending a user 'things to try' is one of the best ways to erode user confidence very quickly! (If there is IT support at the user site, get them involved, not the user).
For starters, explore these options:
*) If possible, I'd check the Windows Event Log for events that may have occurred on that machine around the time the problem arose.
*) Is there some kind of IT support person on the user's side that you can talk to about possible changes/problems in that user's environment?
*) Was there some kind of support issue/incident with that user around the time the error surfaced that may be connected to it, and/or caused some kind of data or file corruption particular to them?
(As for the code itself, I agree with #Warran P about decoupling etc)
Things to do when MadExcept is NOT Enough (which is rare, I must say):
Try Jedi JCL's JCLDEBUG instead. You might get a stack traceback with it, if you change out MadExcept for JCLDEBUG, and write directly the stack trace to the disk without ANY UI interaction.
Run a debug-viewer like MS/SysInternals debugview, and trace output things like the Self pointers of the objects where the problems are happening. I suspect that somehow an INVALID instance pointer is ending up in there.
Decouple things and refactor things, and write unit tests, until you find the really ugly thing that's trashing you. (Someone suggested heap corruption. I often find heap corruption goes hand in hand with unsafe ugly untested code, and deeply bound UI+model cascading failures.)

Delphi 2006 duplicate function names

is there a way to make the Delphi compiler recognize and report duplicated function names (especially defined in different Units that the currently compiled unit uses)?
There isn't. It would sure be nice if there was, though. You can get around this by using the unit name of the function you'd like to invoke:
uses
unit1, unit2;
procedure DoSomethingFromUnit1;
begin
unit1.DoSomething;
end;
Try submitting this to QC as a feature request. It would be a useful thing to have.
From the sight of the compiler, duplicate function names are not duplicate if they reside in different scopes. Finding these ambiguities (in sight of the programmer) is supported quite well by Pascal Analyzer.
This is one of those things that makes "WITH" so dangerous.
CodeHealer does a good job of reporting when a method or identifier is hiding another one with the same name but a different scope.

TFrame inheritance refactoring

Yet another TFrame IDE-registered-component question from me. Thanks for all the help, fellow programmers. : )
Playing around with Darrian's TFrame inheritance suggestion here:
Specifics:
Basically, I have a TFrame-based component that I've registered to the IDE, and it has worked wonderfully. I'm now developing a few "sister" components which will share a great deal of the existing component's non-visual functionality and properties. It makes sense, then, to move a lot of that to a parent/superclass which both the new and the old components can then inherit from.
What is the best way to "refactor" TFrame inheritance in this way? (This may apply to TForm-class descendants too, not sure). What are the caveats and things to watch out for?
Example:
I tried, for example, creating a new TFrame, with nothing on it, and calling that frame TMyBaseFrame. Then modified the class definition of my existing component (Let's call it TMyFrameTreeView) to inherit from that rather than TFrame.
It compiled fine, but when I tried dropping it on a form, I got "ClientHeight not found" (or "ClientHeight property not found"), and it wouldn't drop on the form. Deleting ClientHeight and ClientWidth from the related DFM wreaked havoc, and they ended up replaced upon resizing anyway. I noticed ExplicitHeight and ExplicitWidth in descendent classes, and am thinking that relates to property-value overrides from inherited values, but am not sure. Recreating an entirely new frame via New -> Inherited Items, and then copying everything over, hasn't yielded great results yet either.
Final Note
I realize this could get messy quickly, with streaming DFM files and multiple generations of descendants, etc.... which is part of why I'm asking for the overall "things to look out for" conceptual aspect, but also giving a specific real-world simpler version of the problem as well (which seems to me, ought to be doable).
I've created a little test package to hack around in learning attempts, and am learning a great deal, but it's slow-going, and any guidance/insight from you Delphi "Jedi Masters" out there would be MOST appreciated. : )
Answer update later:
Both of the answers below were helpful. As well, creating a "Base Frame Class" which has NO changes from the normal TFrame, and THEN inheriting from that before adding any properties, methods, etc. seems to stabilize the inheritance streaming tremendously. Not sure why, but so far it has.
In addition to changing base class of TMyFrameTreeView to TMyBaseFrame change the first word in the dfm file for TMyFrameTreeView from object to inherited.
I'm now developing a few "sister"
components which will share a great
deal of the existing component's
non-visual functionality and
properties. It makes sense, then, to
move a lot of that to a
parent/superclass which both the new
and the old components can then
inherit from.
What is the best way to "refactor"
TFrame inheritance in this way?
The crux of your text above perhaps is "component's non-visual functionality". So, in this case, IMHO it's best to separate the visual and non-visual layers.
So, perhaps it's better to use a decorator:
TMySharedEngine = class(Whatever)
property LinkedFrame: TFrame;
property P1;
property P2;
...
procedure Proc1;
procedure Proc2;
... //etc.
end;
and in your 'sister' frames to use instances of it:
var
TMyFrame1 = class(TFrame)
...
FDecorator: TMySharedEngine;
...
public
property MySharedPart: TMySharedEngine read FDecorator;
constructor Create(AOwner: TComponent); override;
...
end;
constructor TMyFrame1.(AOwner: TComponent); override;
begin
inherited;
FDecorator:=TMySharedEngine.Create; //ok, ok do not forget to Free it .Destroy
FDecorator.LinkedFrame:=Self;
...
end;
OTOH, if you want to use your approach you can use Visual Form Inheritance (as Darian suggested) or (more flexible) you can do it by hand: Create, using the IDE the following frames: TBaseFrame, TChildFrame1, TChildFrame2 ... etc. Now go on TChildFrame1's unit and change by hand it's class definition from TChildFrame1 = class(TFrame) to TChildFrame1 = class(TBaseFrame). Compile. It should work. It's recommended though that when you'll do this trick TBaseFrame to be empty in order to avoid possible small quirks (feature collisions etc.)
HTH.

Resources