Delphi 5: Ideas for simulating "Obsolete" or "Deprecated" methods? - delphi

i want to mark a method as obsolete, but Delphi 5 doesn't have such a feature.
For the sake of an example, here is a made-up method with it's deprecated and new preferred form:
procedure TStormPeaksQuest.BlowHodirsHorn; overload; //obsolete
procedure TStormPeaksQuest.BlowHodirsHorn(UseProtection: Boolean); overload;
Note: For this hypothetical example, we assume that using the parameterless version is just plain bad. There are problems with not "using protection" - which have no good solution. Nobody likes having to use protection, but nobody wants to not use protection. So we make the caller decide if they want to use protection or not when blowing Hodir's horn. If we default the parameterless version to continue not using protection:
procedure TStormPeaksQuest.BlowHodirsHorn;
begin
BlowHodirsHorn(False); //No protection. Bad!
end;
then the developer is at risk of all kinds of nasty stuff. If we force the parameterless version to use protection:
procedure TStormPeaksQuest.BlowHodirsHorn;
begin
BlowHodirsHorn(True); //Use protection; crash if there isn't any
end;
then there's a potential for problems if the developer didn't get any protection, or doesn't own any.
Now i could rename the obsolete method:
procedure TStormPeaksQuest.BlowHodirsHorn_Deprecatedd; overload; //obsolete
procedure TStormPeaksQuest.BlowHodirsHorn(UseProtection: Boolean); overload;
But that will cause a compile error, and people will bitch at me (and i really don't want to hear their whining). i want them to get a nag, rather than an actual error.
i thought about adding an assertion:
procedure TStormPeaksQuest.BlowHodirsHorn; //obsolete
begin
Assert(false, 'TStormPeaksQuest.BlowHodirsHorn is deprecated. Use BlowHodirsHorn(Boolean)');
...
end;
But i cannot guarantee that the developer won't ship a version without assertions, causing a nasty crash for the customer.
i thought about using only throwing an assertion if the developer is debugging:
procedure TStormPeaksQuest.BlowHodirsHorn; //obsolete
begin
if DebugHook > 0 then
Assert(false, 'TStormPeaksQuest.BlowHodirsHorn is deprecated. Use BlowHodirsHorn(Boolean)');
...
end;
But i really don't want to be causing a crash at all.
i thought of showing a MessageDlg if they're in the debugger (which is a technique i've done in the past):
procedure TStormPeaksQuest.BlowHodirsHorn; //obsolete
begin
if DebugHook > 0 then
MessageDlg('TStormPeaksQuest.BlowHodirsHorn is deprecated. Use BlowHodirsHorn(Boolean)', mtWarning, [mbOk], 0);
...
end;
but that is still too disruptive. And it has caused problems where the code is stuck at showing a modal dialog, but the dialog box wasn't obviously visible.
i was hoping for some sort of warning message that will sit there nagging them - until they gouge their eyes out and finally change their code.
i thought perhaps if i added an unused variable:
procedure TStormPeaksQuest.BlowHodirsHorn; //obsolete
var
ThisMethodIsObsolete: Boolean;
begin
...
end;
i was hoping this would cause a hint only if someone referenced the code. But Delphi shows a hint even if you don't call actually use the obsolete method.
Can anyone think of anything else?

How about something like
procedure TStormPeaksQuest.BlowHaldirsHorn; //obsolete
begin
if DebugHook > 0 then asm int 3 end;
// This method is Obsolete! Use XXXXX instead.
Abort; // Optional, makes method useless
// old code here . . .
end;
Kind of a compromise between an assertion and a showmessage. The developer just needs to hit F9 to continue. You could put in an Abort, and then the method would do nothing, and that would force them to switch methods and the break makes them aware of it.
Personally I would recommend upgrading to a newer version of Delphi. 2007 and 2009 are great releases and are really worth the upgrade.

What I've ended up using was a combination of opting into a system where you agree to not have any deprecated code, with output debug strings and breakpoints otherwise.
Like strict html, I've created a Strict define.
All the common units have the obsolete code defined out if Strict is defined. That way the developer has agreed that they will not have deprecated code in their project:
{$IFNDEF Strict}
procedure TStormPeaksQuest.BlowHaldirsHorn; overload; //obsolete
{$ENDIF}
procedure TStormPeaksQuest.BlowHaldirsHorn(UseProtection: Boolean); {$IFNDEF Strict}overload;{$ENDIF}
{$IFNDEF Strict}
procedure TStormPeaksQuest.BlowHaldirsHorn; //obsolete
begin
OutputDebugString(PAnsiChar('TStormPeaksQuest.BlowHaldirsHorn is deprecated. Use BlowHaldirsHorn(Boolean)'));
//Don't debugbreak without a debugger attached!
if DebugHook > 0 then
Windows.DebugBreak;
...
end;
So if the developer wants to have proper code, suffering having to perform code-changes when new things are deprecated, they can:
{$DEFINE Strict}
If not, then there will always be an OutputDebugString, and anyone with Debug View can see (even customers). It's funny to see commercial software (even Microsoft's) with output debug strings left over.
And finally, if there's a debugger attached, then they'll get a debug breakpoint out of nowhere. If anyone asks about, i can take that opportunity to make fun of them.

This doesn't exactly answer your question but it might provide an alternative solution. Could you not update the original function with a default value...
procedure TStormPeaksQuest.BlowHaldirsHorn(UseProtection: Boolean = False);
...so that legacy code compiles and behaves the same but the new functionality is available to new developers.

Why do you want to do this and why don't you want to upgrade the Delphi version?
Without the deprecated tag you really have no clean option to filter the use of deprecated methods. So it depend on where you want to make the concession:
renaming catches the error at compiletime (unless there is another method/function with the same name within scope).
all other methods are only caught at runtime. And this has a risk of slipping into the production code.
What you can do, is create a deprecated log. This won't piss off anybody, and it is no complete disaster if it enters production code. But if your tests have full coverage, you will catch all the culprits. You only have to check the log file after a (test)run.
And of course the best way, is to use grep to find all the occurences of the code and change it.

I'd agree with an optional parameter if it'll do the job. But I can think of situations were optional params won't fit. For instance I have moved functions into new units but kept the olds ones and marked them as deprecated.
I guess whatever solution you go with will also depend on the discipline of your team. Do they actively take notice and work to correct all the hints and warnings for their apps? Hopefully they do but I am shamed to admit that the team I work with (including myself) do not stay on top of all the hints and warnings. Every now and then I fix as many hints & warnings as time permits and we absolutely should fix warnings but in reality we've got to get the job done and are more focused on new features and deadlines.
So my point is, even if you could mark them as deprecated or give a similar hint/warning, do you feel your team will take the time to change their code anyway?

It's not a total solution since you can't differentiate whether they've used the method or not but if it's available in Delphi 5 but you could use the $MESSAGE compiler directive to emit a warning at compile time.

Nothing says "Fix me!" like a compiler break.
That said, I have a colleague who regularly modifies the signatures of 'common code' routines... and yes it is annoying!
My preferred (i.e. our team isn't quite there yet :/) approach is as follows:
All developers must be able to easily perform a full build of all projects on their own machines.
Whoever decides to change common code should bear responsibility for the consequences. I.e. Fix all affected code.
Granted, sometimes said developer might not be ideally suited to properly implementing and verifying all fixes.
But, by the same token the developrs imposed upon to "adopt the new contract" in their own code might not be clear on the subtelties of the new contract. I.e.
Is there anything special that needs to be done in order to use protection?
How is the use of protection implemented?
What are the concerns as to how it could break existing code?
This is a major reason why a comprehensive set of test cases is important.
Generally you want the ripple effect of all the changes applied as soon as possible. I.e. While the finer details of the change are fresh in the originating developer's mind - before attention shifts to something else!
It should be a rare occurance that the ripple effect is so vast (thousands of lines) that you want to apply the change over an extended period of time.
If this is the case, then implement a simple code-metric gathering tool integrated into your build process to report the number of outstanding instances.
I consider point 2 to be of key importance, not least because it emphasises the need for cooperation and communication within the team.
Going back to my opening statement, the sooner you catch and fix an error, the better.
Let the compiler report a break! -- It's the "earliest opportunity" error reporting mechanism we have.
That's my 2 coppers! :D

Related

How do I keep Delphi from automatically expanding folded code regions?

I widely use {$Regions} in my units, but sometimes the VCL editor takes annoying decisions to automatically expand all regions. Is there any way to tell the editor not
to expand regions that are collapsed unless I explicity do this by clicking on the + button?
Update from comments:
Unfolding occurs,
with nested procs/funcs. Introducing a new nested "procedure" header. As soon as you type the 'p' character, all other nested procs/funcs are unfolded. This occurss also if, for instance, by mistake, you delete the "end" of any nested proc/func (or anything of the kind).
with regions. If you start a comment with "{", the Region immediately below is unfolded. Starting a comment with "(*" provoques all Regions below to be unfolded and same applies to all procs/funcs under those Regions.
Unfortunately this is one of the problems of Delphi whis is present athleast since Delphi XE if not even from older versions and still hasn't been fixed.
The reason why code gets automaticaly unfolded is the fact that as soon as Error Insight system detects any syntax error in your unit it treats all the code below that poiunt as non-valid and thus expans all folded code below that point.
So far the only way that I know to avoid this is to disable the Error Insight. But that means you won't be warned about any potentional syntax errors untill you try to compile your program.
But I seriously hope Embarcadero will go and fix this soon as it makeswhole code folding system to be useless unles you have Error Insight disabled as you spend more time folding the autoexpanded code again that writing any new code.
EDIT: Steps to reproduce this (one way of reproducing it)
Create new application
Create two new even handlers for OnCreate and OnClose for your form or any two other methods.
procedure TForm1.FormCreate(Sender: TObject);
begin
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
//
end;
In case if you created even handlers make sure there is athleast some code in the lowest handler metrhod or athleast one commented line so that optimizer doesent automatically remove the "empy" event handler.
Now in top event handler write "Form." and you will se how bottom event handler will be automatically expanded.
You will see that at this point delphi desent even know when or where current method code block in which syntax error has been detected ends. And this also makes it imposible to properly detect any code blocks below that point.

Delphi : force unload injected module

i use this code to determine if a specific module has been injected to my application's process
(i use it to prevent some Packet Sniffer Softwares)
Var
H:Cardinal;
Begin
H:= GetModuleHandle('WSock32.dll');
if H >0 then FreeLibrary(H);
end;
the problem is when i call Freelibrary it do nothing !
i don't wanna show message then terminate the application i just want to unload the injected module silently
thanks in advance
Well, first of all I'll attempt to answer the question as asked. And then, I'll try to argue that you are asking the wrong question.
Modules are reference counted. It's possible that there are multiple references to this module. So, keep calling FreeLibrary:
procedure ForceRemove(const ModuleName: string);
var
hMod: HMODULE;
begin
hMod := GetModuleHandle(PChar(ModuleName));
if hMod=0 then
exit;
repeat
until not FreeLibrary(hMod);
end;
If you were paranoid you might choose to add an alternative termination of the loop to avoid looping indefinitely.
I don't really know that this will work in your scenario. For instance, it's quite plausible that your process links statically to WSock32. In which case no amount of calling FreeLibrary will kick it out. And even if you could kick it out, the fact that your process statically linked to it probably means it's going to fail pretty hard.
Even if you can kick it out, it seems likely that other code in your process will hold references to functions in the module. And so you'll just fail somewhere else. I can think of very few scenarios where it makes sense to kick a module out of your process with complete disregard for the other users of that module.
Now, let's step back and look at what you are doing. You are trying to remove a standard system DLL from your process because you believe that it is only present because your process is having its packets sniffed. That seems unlikely to be true.
Since you state that your process is subject to packet sniffing attack. That means that the process is communicating over TCP/IP. Which means that it probably uses system modules to carry out that communication. One of which is WSock32. So you very likely link statically to WSock32. How is your process going to work if you kill one of the modules used to supply its functionality?
Are you quite sure that the presence of WSock32 in your process indicates that your process is under attack? If a packet sniffer was going to inject a DLL into your process, why would it inject the WSock32 system DLL? Did you check whether or not your process, or one of its dependencies, statically links to WSock32?
I rather suspect that you've just mis-diagnosed what is happening.
Some other points:
GetModuleHandle returns, and FreeLibrary accepts an HMODULE. For 32 bit that is compatible with Cardinal, but not for 64 bit. Use HMODULE.
The not found condition for GetModuleHandle is that the return value is 0. Nowhere in the documentation is it stated that a value greater than 0 indicates success. I realise that Cardinal and HMODULE are unsigned, and so <>0 is the same as >0, but it really makes no sense to test >0. It leaves the programmer thinking, "what is so special about <0?"

Is considered a bad practice or exist any drawback using try/finally try/except instead of begin/end?

In many places in some Apps which I maintain , I've found code which uses a try/finally or try/except block in a for loop or if sentence avoiding the use of begin/end
Consider the next code (not production code, just a sample)
{$APPTYPE CONSOLE}
{$R *.res}
uses
Classes,
SysUtils;
Procedure TestNoBeginEnd;
var
i : Integer;
L1 : TStringList;
begin
for i := 1 to 10 do
try
L1:=TStringList.Create;
try
L1.Add('Bar');
L1.Add(IntToStr(i));
L1.Add('Foo');
finally
Writeln(L1.Text);
L1.Free;
end;
except
on E: Exception do
Writeln('Opps '+E.ClassName, ': ', E.Message);
end;
end;
begin
try
TestNoBeginEnd;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.
The Question, Is considered a bad practice, code smell or exist any drawback using a try/finally or try/except instead of begin/end in delphi?
UPDATE
I'm sorry by the silly sample code, just for clarify the try/finally and try/except doesn't pretend replace the begin/end , just to avoid to use it (the begin/end) when exist a case when the use of the try/finally or the try/except doesn't requires a begin/ end.
I personally don't see the need to add the extra begin..end around a try..finally/except..end, especially when there is little chance of code being added just before the try or certainly after the end.
That being said, the question would be more whether we need systematically a begin..end after a ..do or a ..then even when there is only a single instruction (in your case a try.. instruction).
It's mostly a question of style and habit, some people prefer to code defensively and systematically put a begin..end, others try to avoid unnecessary lines as much as possible.
When a fair number of lines are involved, I tend to add the begin..end to make the scoping more obvious.
I'd say, use good judgment and see what the chances are that someone (or you) modifying the code later could miss the intended scope.
First things first. Begin/End is not the same as Try/Etc. The latter first denotes a set of instructions, while the second is a single instruction (which contains possibly lots of instructions).
So they are like a hammer and ladder. Different tools for different
jobs.Sure you drive a nail with a ladder but it's not the most
efficient way of dealing with the problem.
Edit:
If I undertand the question (now that you've edit it) you're asking if one try/etc can be used with out a begin/end. Yes you can do this, because try/etc is a single statement. In fact I'd go as far as to say it's probably a good thing to avoid too much no use code.
Before edit:
So what does that mean? Well it means that if an error occurs in that function it's gets logged, but essentially ignored.
Unless there is really good exception handling and the code escalates exceptions or recovers correctly I'd say this was bad practise.
In your sample all the exception handler does is say Opps (oops?). What good is that? Sure it's probably only a sample and as such I'd examine the code to see what it really does. I beleive that the person who wrote this was trying to handle errors generally, but is actually making the reliability of the code far far worse using this pattern.
I use the general case of 'Exception Handlers are for Exceptional Cirmcumstances' where I can recover or I need to let someone/thing know. Mostly I find that no one can handle the error and the system has to degrade as per design. This is where the exception handlers will log safely and pass teh exceptions on.
The latter case I extend to all 'big boundaries in the system'. i.e. I'll collect exceptions at Service/API boundaries and log/wrap appropriately.
(note sometimes bad APIs use Exceptions for non exceptional circumstances, in this case you can safely add an exception handler to wrap the bad design).
Your edit on the question radically changed the question. You should have changed the title at least, and right now I wonder if the question is salvageable at all (-1).
That said, when addressing your original question:
A Try/Finally or Try/Except statement should never be used when a Begin/End statement would suffice. Besides the fact that Try-statements add (tiny or even unnoticeable) extra instructions to the compiled code, they imply code necessities which aren't there:
for I := 0 to Count - 1 do
try
Inc(Rect.Left, Shift);
finally
Inc(Rect.Right, Shift);
end;
Just not do it.
There is nothing wrong with omitting the begin end when a try finally/except wraps the same code in a block for you. Just remember that try-blocks have more overhead than a begin-block. This overhead is usually negligible, but I have found it to make meaningful differences when in loops, specially loops in methods that are referenced repeatedly.
Taking the above details in mind, your example is somewhat poor due to the fact that it adds unneeded overhead repeatedly in the loop. Whenever you can move try blocks out of a loop, it's a good thing. In your example if you MUST have the ability to catch an exception for each loop iteration, the below code would be more efficient, if not, move the except block out also.
Procedure TestNoBeginEnd;
var
i : Integer;
L1 : TStringList;
begin
L1:=TStringList.Create;
try
for i := 1 to 10 do
try
L1.Clear;
L1.Add('Bar');
L1.Add(IntToStr(i));
L1.Add('Foo');
Writeln(L1.Text);
end;
except
on E: Exception do
Writeln('Opps '+E.ClassName, ': ', E.Message);
end;
finally
L1.Free;
end;
end;

How to filter Delphi 2010 compiler output (hints)?

I'm trying to get rid of some hints(*) the Delphi compiler emits. Browsing through the ToolsAPI I see a IOTAToolsFilter that looks like it might help me accomplish this through it's Notifier, but I'm not sure how to invoke this (through what xxxServices I can access the filter).
Can anyone tell me if I´m on the right track here? Thanks!
(*) In particular, H2365 about overridden methods not matching the case of the parent. Not so nice when you have about 5 million lines of active code with a slightly different code convention than Embarcadero's. We've been working without hints for months now, and we kinda miss 'm. :-)
Even if you could query BorlandIDEServices for IOTAToolsFilter, that interface isn't going to help you do what you're asking. That interface was introduced as part of a mechanism for adding additional build tools (compilers, etc.) to the IDE (before the IDE used MSBuild). It allowed you to write a custom "filter" to handle output from a particular build tool, but it would not let you apply a filter to one of the built-in tools (like the delphi compiler).
The reason the Supports(BorlandIDEServices, IOTAToolsFilter, OTAToolsFilter) call fails in Delphi2010 is that once MSBuild support was added to the IDE, the old way of adding build tools to the IDE was disabled, and the BorlandIDEServices interface no longer supported IOTAToolsFilter.
The declaration of IOTAToolsFilter should probably have been marked deprecated in ToolsAPI.pas (or least it should have been mentioned in the source code comment that it is no longer supported).
As far as your desire to filter a particular hint, I'm not aware of a way to do that via the ToolsAPI. It seems like a reasonable thing that can be added to IOTAMessageServices (the ability to enumerate, filter, and possibly change the messages in the IDE's Message View). I would enter a request in QualityCentral for that.
Also, please vote for QC #35774 (http://qc.embarcadero.com/wc/qcmain.aspx?d=35774), as if that were implemented, you would not need to use the ToolsAPI for this sort of thing.
According to http://docwiki.embarcadero.com/RADStudio/en/Obtaining_Tools_API_Services it should be possible to access it directly using BorlandIDEServices, eg:
var
OTAToolsFilter: IOTAToolsFilter;
begin
if Supports(BorlandIDEServices, IOTAToolsFilter, OTAToolsFilter) then
ShowMessage('supports IOTAToolsFilter')
else
ShowMessage('IOTAToolsFilter NOT supported');
end;
However this doesn't return the desired interface in Delphi 2010 (you'll get the not supported message), so there's either an error in the documentation, or an error in BorlandIDEServices not returning the correct interface.

How do I turn specific Delphi warnings and hints off?

In CodeGear Delphi 2007, how can I turn specific warnings and hints off? I am attempting to turn off H2077 - Value assigned to 'varname' never used.
You are not able to disable specific hints like you can with warnings. Hints are those things that would not have any potential adverse affects on your runtime code. For instance, when you see the hint "Value assigned to 'varname' never used" it is merely a suggestion for something you should probably "clean up" in your code, but it won't cause any potential runtime errors (other than your own logic errors, of course :-). Hints are always best addressed by tweaking the code.
Warnings, on the other hand, are those things that could possibly cause unintended runtime behaviors and really should be addressed. For instance, using a variable before assigning a value to it is clearly a case of an uninitialized variable and that can lead to "bad things." In the vast majority of times, warnings should be addressed by "fixing" the code. Even then, in certain circumstances you may deem the warning as a "false positive" and are certain the code is functioning correctly. In those cases, you can disable a specific warning. Disabling all warnings is dangerous.
Hints? No specific.
You'll have to disable them all:
{$HINTS OFF}
Warnings?
{$WARN _name_of_warning_ OFF|ON|ERROR}
Check here for a full list
Why don't you instead change the code so the hint goes away? Those hints are usually pretty accurate. And if you really feel that the line of code (I'm guessing some variable initialization or other) is useful to the reader of your code even if it is irrelevant to the compiler, you can replace it with a comment.
What Lars said. Also, you can get the complete list of warnings and their current settings by pressing CTRL-O twice. It'll dump a list at the top of the current unit. You can look through there to find the one you need to change. Just remember to delete the list later, or people looking at the code later on will hate you. ;)
To remove a hint for a line of code that has the:
H2077 Value assigned to '%s' never used
You can wrap it in:
{$HINTS OFF}
//...
{$HINTS ON}
For example, from the buggy Vcl.ComCtrls.pas:
procedure TTrackBarStyleHook.Paint(Canvas: TCanvas);
//....
begin
if not StyleServices.Available then Exit;
{$HINTS OFF}
Thumb := ttbTrackBarDontCare; //value assigned to 'Thumb' never used
{$HINTS ON}
//...
end;
Note: Any code released into public domain. No attribution required.

Resources