On my Delphi research list:
[Window Title] Error [Content] Error creating form: Root class not found: "". [OK]
I think it has to do with form inheritance, but a quick glance didn’t raise any warning signs.
–jeroen
Posted by jpluimers on 2019/10/03
On my Delphi research list:
[Window Title] Error [Content] Error creating form: Root class not found: "". [OK]
I think it has to do with form inheritance, but a quick glance didn’t raise any warning signs.
–jeroen
Posted in Conference Topics, Conferences, Delphi, Development, Event, Software Development | 1 Comment »
Posted by jpluimers on 2019/10/02
I got an interesting question a while ago: should an application terminate (anonymous) threads or not?
Started.WaitFor might not return (which you cannot get around this because all overloads of the CheckThreadError are non-virtual)A problem is that a thread might not execute unless you call WaitFor before Terminate is called. The reason is that the internal function ThreadProc does not start Execute if the thread is already terminated.
The
ThreadProcin theSystem.Classesunit is an ideal place to set breakpoints in order to see which threads might start.Other useful places to set breakpoints:
TAnonymousThread.ExecuteTExternalThread.Execute
Execute not being called by ThreadProc is a bug, but it is not documented because QC is gone (taking the below entry with it), it is not in QP and the docwiki never got updated.
Given QC has so much information, I am still baffled that Embarcadero took it down.
Sergey Kasandrov (a.k.a. serg or sergworks) wrote in [WayBack] Sleep sort and TThread corner case | The Programming Works about this bug and refers to WayBack: QualityCentral 35451 – TThread implementation doesn’t guarantee that thread’s Execute method will be called at all .
The really bad thing are the WayBack: QualityCentral Resolution Entries for Report #35451 Resolution “As Designed” implying the design is wrong.
In his post, sergworks implemented the Sleep sorting in Delphi. Related:
- WayBack: Genius sorting algorithm: Sleep sort
- [WayBack] Reddit – 4chan: Sleep sort : programming
- [WayBack] 4chan: Sleep sort : programming
- [WayBack] Sorting algorithms/Sleep sort – Rosetta Code
- [WayBack] What is the time complexity of the sleep sort? – Stack Overflow
Note that application shutdown is a much debated topic. Best is to do as little cleanup as possible: your process is going to terminate soon anyway. No need to close handles or free memory: Windows will do that for you anyway. See for instance:
- [WayBack] The old-fashioned theory on how processes exit – The Old New Thing
- [WayBack] Quick overview of how processes exit on Windows XP – The Old New Thing
- [WayBack] Changes to power management in Windows Vista – The Old New Thing
- [WayBack] Now that Windows makes it harder for your program to block shutdown, how do you block shutdown? – The Old New Thing
- [WayBack] A process shutdown puzzle – The Old New Thing
- [WayBack] A process shutdown puzzle: Answers – The Old New Thing
- [WayBack] A process shutdown puzzle, Episode 2 – The Old New Thing
- [WayBack] Why does Internet Explorer not call DLL_PROCESS_DETACH on my DLL when I call ExitProcess? – The Old New Thing
- [WayBack] When DLL_PROCESS_DETACH tells you that the process is exiting, your best bet is just to return without doing anything – The Old New Thing
- [WayBack] Why can’t I delete a file immediately after terminating the process that has the file open? – The Old New Thing
- [WayBack] During process termination, the gates are now electrified – The Old New Thing
- [WayBack] How my lack of understanding of how processes exit on Windows XP forced a security patch to be recalled – The Old New Thing
- [WayBack] The old-fashioned theory on how processes exit – The Old New Thing
- [WayBack] Some reasons not to do anything scary in your DllMain – The Old New Thing
Related to waiting:
Related to executing:
–jeroen
Posted in Conference Topics, Conferences, Delphi, Development, Event, Multi-Threading / Concurrency, Software Development, The Old New Thing, Windows Development | Leave a Comment »
Posted by jpluimers on 2019/09/26
This was a change between IE6 and IE7 on the default time-out decreasing from 3600 seconds to 30 seconds: [WayBack] Soap Delphi Client end with a timeout for a 1MB call – Stack Overflow.
If you want to increase the timeout, then use InternetSetOption. You can get the current value using InternetQueryOption.
In Delphi, THTTPReqResp.Send supports this by setting the various time out options right after creating the request:
Request := HttpOpenRequest(FInetConnect, 'POST', PChar(FURLSite), nil,
nil, nil, Flags, 0{Integer(Self)});
Check(not Assigned(Request));
{ Timeouts }
if FConnectTimeout > 0 then
Check(not InternetSetOption(Request, INTERNET_OPTION_CONNECT_TIMEOUT, Pointer(@FConnectTimeout), SizeOf(FConnectTimeout)));
if FSendTimeout > 0 then
Check(not InternetSetOption(Request, INTERNET_OPTION_SEND_TIMEOUT, Pointer(@FSendTimeout), SizeOf(FSendTimeout)));
if FReceiveTimeout > 0 then
Check(not InternetSetOption(Request, INTERNET_OPTION_RECEIVE_TIMEOUT, Pointer(@FReceiveTimeout), SizeOf(FReceiveTimeout)));
Related:
INTERNET_OPTION_CONNECT_TIMEOUT, INTERNET_OPTION_SEND_TIMEOUT , INTERNET_OPTION_RECEIVE_TIMEOUT.–jeroen
Posted in Conference Topics, Conferences, Delphi, Development, Event, SOAP/WebServices, Software Development | Leave a Comment »
Posted by jpluimers on 2019/09/25
Marjan Venema had a great answer at [WayBack] How to expose a Delphi set type via Soap – Stack Overflow.
–jeroen
Posted in Conference Topics, Conferences, Delphi, Development, Event, SOAP/WebServices, Software Development | Leave a Comment »
Posted by jpluimers on 2019/09/17
Since about a decade, TThread has a few overloaded [WayBack] Synchronize methods which all allow some specified synchronised method to run in the context of the main thread:
Any exceptions raised in that methods are caught using [WayBack] System.AcquireExceptionObject and re-raised in the calling thread.
If that happens, you loose a piece of the stack information. I knew that, but found out the hard way that it does because I had to hunt for bugs through inherited code written by people that did not know.
This was part of the stack trace that code would show during an exception:
Exception EAccessViolation at $004D732F: Access violation at address $00409174 in module ''.....exe''.
Read of address 80808080
StackTrace:
(000D632F){.....exe} [004D732F] System.Classes.TThread.Synchronize$qqrp41System.Classes.TThread.TSynchronizeRecordo (Line 14975, "System.Classes.pas" + 40) + $0
(000D6430){.....exe} [004D7430] System.Classes.TThread.Synchronize$qqrxp22System.Classes.TThreadynpqqrv$v (Line 15007, "System.Classes.pas" + 9) + $A
(005D6E61){.....exe} [009D7E61] IdSync.DoThreadSync$qqrp18Idthread.TIdThreadynpqqrv$v (Line 281, "IdSync.pas" + 21) + $6
(005D6E87){.....exe} [009D7E87] IdSync.TIdSync.SynchronizeMethod$qqrynpqqrv$v (Line 326, "IdSync.pas" + 2) + $8
Exception EAccessViolation at $00409174: Access violation at address $00409174 in module ''.....exe''. Read of address $80808080 with StackTrace
(00008174){.....exe} [00409174] System.@IsClass$qqrxp14System.TObjectp17System.TMetaClass + $C
The first exception has a different address than the one in the exception message.
Which means that you miss the whole stack path to the _IsClass call (the underlying method implementing the as keyword) that the actual exception was initiated at.
And yes: the $80808080 is the FastMM4 marker for freed memory, so this was a use-after-free scenario.
A simple wrapper like this using a central logging facility gave much more insight in the actual cause:
procedure RunLoggedMethod(AMethod: TMethod);
begin
try
AMethod();
except
on E: Exception do
begin
Logger.LogExceptionDuringMethod(E, AMethod);
raise; // mandatory to stay compatible with the old un-logged code
end;
end;
end;
Then call it like this inside a thread descendant:
Synchronize(RunLoggedMethod(MethodToRunInMainThread));
The old code was like this:
Synchronize(MethodToRunInMainThread);
This was quite easy to change, as I already had boiler code around exported DLL functions that had a similar construct (without the raise; as exceptions cannot pass DLL boundaries unless very specific circumstances hold).
Similar methods are needed to encapsulate procedure TIdSync.Synchronize(), procedure TIdSync.SynchronizeMethod, procedure TIdThread.Synchronize(Method: TThreadMethod) and [WayBack] Queue overloads:
–jeroen
Posted in Conference Topics, Conferences, Delphi, Development, Event, Software Development | 1 Comment »
Posted by jpluimers on 2019/09/11
An interesting question from a while back: [WayBack] delphi – Should a descendant class’ method’s variable that is identical to Self, have access to its ancestor’s protected methods? – Stack Overflow
In unit A:
TParent = class protected function DoSomething: TParent; end;In unit B:
TChild = class(TParent) public procedure DoAnotherThing; end; implementation procedure TChild.DoAnotherThing; begin DoSomething.DoSomething end;…
This won’t compile, throwing a
cannot access protected symbol TParent.DoSomething
The kicker here is that the error message makes you think you are operating in Self context, but you are not as you are calling DoSomething.DoSomething where only the first DoSomething is in your Self context, but the second .DoSomethingis in the context of any TParent instance trying to access a public identifier.
Stefan Glienke posted [WayBack] a more elaborate answer explaining some workarounds.
–jeroen
Posted in Conference Topics, Conferences, Delphi, Development, Event, Software Development | Leave a Comment »
Posted by jpluimers on 2019/08/29
via [WayBack] Writing solid code the NASA way. – Lars Fosdal – Google+, I bumped into [WayBack] How To Code Like The Top Programmers At NASA — 10 Critical Rules:
Do you know how top programmers write mission-critical code at NASA? To make such code clearer, safer, and easier to understand, NASA’s Jet Propulsion Laboratory has laid 10 rules for developing software.
The rules:
- Restrict all code to very simple control flow constructs – do not use goto statements, setjmp or longjmp constructs, and direct or indirect recursion.
- All loops must have a fixed upper-bound. It must be trivially possible for a checking tool to prove statically that a preset upper-bound on the number of iterations of a loop cannot be exceeded. If the loop-bound cannot be proven statically, the rule is considered violated.
- Do not use dynamic memory allocation after initialization.
- No function should be longer than what can be printed on a single sheet of paper in a standard reference format with one line per statement and one line per declaration. Typically, this means no more than about 60 lines of code per function.
- The assertion density of the code should average to a minimum of two assertions per function. Assertions are used to check for anomalous conditions that should never happen in real-life executions. Assertions must always be side-effect free and should be defined as Boolean tests. When an assertion fails, an explicit recovery action must be taken, e.g., by returning an error condition to the caller of the function that executes the failing assertion. Any assertion for which a static checking tool can prove that it can never fail or never hold violates this rule (I.e., it is not possible to satisfy the rule by adding unhelpful “assert(true)” statements).
- Data objects must be declared at the smallest possible level of scope.
- The return value of non-void functions must be checked by each calling function, and the validity of parameters must be checked inside each function.
- The use of the preprocessor must be limited to the inclusion of header files and simple macro definitions. Token pasting, variable argument lists (ellipses), and recursive macro calls are not allowed. All macros must expand into complete syntactic units. The use of conditional compilation directives is often also dubious, but cannot always be avoided. This means that there should rarely be justification for more than one or two conditional compilation directives even in large software development efforts, beyond the standard boilerplate that avoids multiple inclusion of the same header file. Each such use should be flagged by a tool-based checker and justified in the code.
- The use of pointers should be restricted. Specifically, no more than one level of dereferencing is allowed. Pointer dereference operations may not be hidden in macro definitions or inside typedef declarations. Function pointers are not permitted.
- All code must be compiled, from the first day of development, with all compiler warnings enabled at the compiler’s most pedantic setting. All code must compile with these setting without any warnings. All code must be checked daily with at least one, but preferably more than one, state-of-the-art static source code analyzer and should pass the analyses with zero warnings.
–jeroen
PS: twitter comment
“All code must compile with these settings without any warnings” – I absolutely agree with this. It really annoys me to find code which people have shipped which generates warnings. They’re there for a reason and should be fixed! 👍
Posted in Agile, Code Quality, Conference Topics, Conferences, Development, Event, Software Development | Leave a Comment »
Posted by jpluimers on 2019/08/28
Must watch when you do unit testing (no matter the environment): [WayBack] Structure and Interpretation of Test Cases : Kevlin Henney on Vimeo which he presented at Javazone 2018 [Archive.is]
via [WayBack] Kevlin Henney – Google+
–jeroen
Posted in Agile, Conference Topics, Conferences, Development, Event, Software Development, Unit Testing | Leave a Comment »
Posted by jpluimers on 2019/08/15
Stefan Glienke shared the TestInsight default JSON web-server location with me through chat; I like it!
Some endpoints:
{"SelectedTests":[]}{"SelectedTests":["Test.Timer.TTimerTest.TestBasic","Test.Timer.TTimerTest.TestDifferentIntervals","Test.Timer.TTimerTest.TestWakeUpTimer","Test.Timer.TTimerTest.TestTimeIsInRange","Test.Timer.TTimerTest.TestMilisecondsToNextTriggerTime"]}{"ExecuteTests":true, "ShowProgress":false}The mechanism for accessing this JSON server are implemented in the TestInsight.Client.pas
You can find the endpoint base URL in TestInsightSettings.ini which by default looks like this:
[Config] BaseUrl=http://WIN10-DELPHI:8102
–jeroen
Posted in Conference Topics, Conferences, Delphi, Development, Event, Software Development | Leave a Comment »
Posted by jpluimers on 2019/08/05
I am quite amazed that many web-sites fail to allow email addresses of the form x+y@z.domain.
This is called subaddressing and has been in the email addressing specs since ages.
Basically the only way to verify the validity of an email address is to send an email to it, and wait for it to be accepted or rejected.
Even the best regex will “have almost no false negatives”, which means they will reject valid email addresses.
Please read and implement these specs before rejecting email addresses you think might be invalid:
The RFC allows comments to be arbitrarily nested. A single regular expression cannot cope with this.
any ASCII graphic (printing) character other than the at-sign (“@”), backslash, double quote, comma, or square brackets may appear without quoting.
Sub-addressing
Some mail services allow a user to append a +tag qualifier to their e-mail address (e.g., joeuser+tag@example.com). The text of tag can be used to apply filtering. The text of the tag can also be used to help a user figure out which organization “leaked” the user’s email address to a spammer. However, some mail servers violate RFC 5322, and the recommendations in RFC 3696, by refusing to send mail addressed to a user on another system merely because the local-part of the address contains the plus sign (+). Users of these systems cannot use plus addressing. On the other hand, most installations of the qmail and Courier Mail Server products support the use of a dash ‘-‘ as a separator within the local-part, such as joeuser-tag@example.com or joeuser-tag-sub-anything-else@example.com. This allows qmail through .qmail-default or .qmail-tag-sub-anything-else files to sort, filter, forward, or run an application based on the tagging system established. Disposable e-mail addresses of this form, using various separators between the base name and the tag are supported by several email services, including Runbox (plus and minus), Google Mail (plus), Yahoo! Mail Plus (minus), and FastMail (plus). The name sub-addressing is the generic term (used for plus-addressing and minus-addressing) found in some IETF standards-track documents, such as RFC 5233.
Regexes Don’t Send Email
Don’t go overboard in trying to eliminate invalid email addresses with your regular expression. The reason is that you don’t really know whether an address is valid until you try to send an email to it. And even that might not be enough. Even if the email arrives in a mailbox, that doesn’t mean somebody still reads that mailbox. If you really need to be sure an email address is valid, you’ll need to send an email to it that contains a code or link for the recipient to perform a second authentication step. And if you’re doing that, then there is little point in using a regex that may reject valid email addresses.
A nice overview of people trying to answer with a regular expression, and comments indicating all those attempts fail in one way or the other is at [WayBack] regex – How to validate an email address in JavaScript? – Stack Overflow
–jeroen
Posted in Conference Topics, Conferences, Event, Internet, Power User | Leave a Comment »