-
Content Count
3505 -
Joined
-
Last visited
-
Days Won
115
Everything posted by Lars Fosdal
-
Record constants that are actually constant?
Lars Fosdal replied to Lars Fosdal's topic in RTL and Delphi Object Pascal
BDS doc has degenerated badly over the years, and it is often incomplete, inaccurate, or outdated. Logically, at least in my head, a type const declared with {$WRITEABLECONST OFF} is immutable and hence should be possible to use as any other const. If that is impossible - I should at least be able to follow the pattern of the following const _000000FF = Integer(255) s = String('C'); and declare const r = rec(a:'Foo'; b:'Bar'); but alas... Your workaround works, but presents two problems: - String constants as a reference are fragile. Renaming through the refactoring methods may very well break the link. - I use attributes to parameterize a large number of properties per class, and that would mean a lot of extra code. To me, it is the attribute Create that is the problem. It doesn't behave the same as other class Create ctors. There are probably compiler tech reasons - but valid or not - the behavior is frustrating. program Test; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils; type rec = record a: string; b: string; class function Create(const aa,ab: string): rec; static; end; const crec: rec = (a:'X'; b:'Y'); type TTestClass = class private r: rec; public constructor Create(const arec: rec); procedure Test(const arec: rec); end; { TTestClass } constructor TTestClass.Create(const arec: rec); begin r:= aRec; end; procedure TTestClass.Test(const arec: rec); begin r := aRec; end; { rec } class function rec.Create(const aa, ab: string): rec; begin Result.a := aa; Result.b := ab; end; begin var TC := TTestClass.Create(crec); try try TC.Test(crec); // typed constant allowed TC.Test(rec.Create('X','Y')); TC.Test((a:'X'; b:'Y')); // BARF - IMO this needs to be supported TC.Test(rec:(a:'X'; b:'Y')); // BARF except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; finally TC.Free; Write('Press Enter'); Readln; end; end. -
Academically you may be correct, but empirically it simply works. I have several hundred installations that have worked for many years and keep on working, and zero complaints that it ever failed. The apps/services check the file version against a repository and if it finds a change, - deletes any *.old.* copies that it finds - copies the new file to a .new temp file, - renames itself to *.old.<handle> - renames the new temp file to the ordinary name - Calls Restart...OnExit and exits. Voila: App / Service is running the new version.
-
@David Heffernan I found restarting a service from itself to be troublesome since you still are in the context of the running service. Issuing a net start just at the time that you exit the service works well. The exception handling is probably a cargo cult remnant. I'd rewrite for CreateProcess for the application, but it works, so I won't fix it.
-
Refer to Form Control without using the Form unit?
Lars Fosdal replied to Mike Torrettinni's topic in VCL
I had a similar problem where a lot of code was referring to the class name of forms to do navigation, and hence including the various form classes all over the place to get the class name. I ended up making a unit, something like this: unit NavigationHelp; interface type TNavLink = record MainForm: string; ReportForm: string; QueryForm: string; end; const NameNotSet = 'Name not set'; {$WRITEABLECONST ON} const NavLink: TNavLink = ( MainForm: NameNotSet; ReportForm: NameNotSet; QueryForm: NameNotSet ); implementation end. and in the init section of my various form units, I updated the constant to the actual class name unit MainFormU; interface uses NavigatorHelp; type // ... implementation // ... initialization NavLink.MainForm := TMainForm.ClassName; end. You could apply the same pattern to form and control handles and set the handles in the respective OnFormShow routines. type THandleRef = record MainForm: THandle; CustomButton: THandle; end; const HandleRef: THandleRef = ( MainForm: 0; CustomButton: 0 ); ... TMainForm.FormShow(Sender: TObject); begin HandleRef.MainForm := Self.Handle; HandleRef.CustomButton := CustomButton.Handle; end; That is a fairly simple "brute force" method to eliminate circular refs. -
Some old code of mine. Awaiting ridicule for the use of ShellExecute 😛 FDCRelauncher https://pastebin.com/YCiqiNAq CustomDebugOut https://pastebin.com/GM7DE18L
-
Best way to check if an internet SMTP server is available?
Lars Fosdal replied to Ian Branch's topic in General Help
I prefer to defer the actual sending to a regular mail client. Then it gets sent whenever mail is actually working. -
The last 8-9 years I've had the luxury of pretty much staying current with the Delphi versions. We usually skip the first releases like 10.2 or 10,3 and migrate when the 10.2.1 or 10.3.1 arrive. This has become a little harder as the so-called minor upgrades now also have a lot of changes and even new stuff., but we tend to wait for the first "fix pack". That allows the new versions mature for a while so that TMS, EurekaLog, FastReports, and others can work out any new quirks, and the bleeding edge crowd can draw first blood, but as I said, we do aspire to move on to the latest version as soon as it seems tried and tested. Back in the day, skipping several versions often raised headaches when finally deciding to migrate as you had to deal with multiple incompatibilities or breaking changes, so I much prefer the current approach. To be honest, in some cases I actually wish that EMBT would be bolder and more often introduce breaking changes to tidy up old sins. On the topic of unit dependency cycles, I tend to view them as a design weakness. It is a quick workaround for properly designing with DI and other tools that eliminate unit inter-dependencies. Yet, working with code that has evolved over a long time, they can be hard to eliminate as they are so deeply ingrained. We have one particular unit that causes a compiler internal error whenever we change something in its interface section and do a compile in the IDE. At that point only a Build All resolves the problem.
-
We ended up setting up the connections in code, rather than using connection definitions. procedure TPSDDatabase_FD.CreateFireDACConnections; const OSAuthent = 'No'; begin if not Assigned(FConnection) then begin OnConnectionCreate; FConnection := TFDConnection.Create(nil); FConnection.DriverName := FireDriverLink.BaseDriverId; FConnection.Params.Values[MSSQLParam.Server] := Trim(FHost); FConnection.Params.Values[MSSQLParam.Database] := Trim(FDatabaseName); FConnection.Params.Values[MSSQLParam.OSAuthent] := OSAuthent; FConnection.Params.Values[MSSQLParam.User_Name] := Trim(FUserName); FConnection.Params.Values[MSSQLParam.Password] := Trim(FPassword); FConnection.Params.MonitorBy := Self.MonitorBy; if DisableMARS then FConnection.Params.Values[MSSQLParam.MARS] := 'No'; FConnection.Params.Values[MSSQLParam.ApplicationName] := AppInfo.Summary+';tid=' + IntToStr(GetCurrentThreadId); FConnection.LoginPrompt := False; FConnection.FormatOptions.OwnMapRules := True; with FConnection.FormatOptions.MapRules.Add do begin SourceDataType := dtDateTimeStamp; TargetDataType := dtDateTime; end; with FConnection.FormatOptions.MapRules.Add do begin SourceDataType := dtDate; TargetDataType := dtDateTime; end; FConnection.OnLogin := FDConnectionLoginEvent; FConnection.OnError := FDConnectionErrorEvent; FConnection.OnLost := FDConnectionLostEvent; FConnection.OnRestored := FDConnectionRestoredEvent; FConnection.OnRecover := FDConnectionRecoverEvent; end; end;
-
Mileage or Distance traveled. And, IMO, although the origin of mileage is miles traveled, today it is fine to use a mileage measured in km - if that is the local distance unit. In Norwegian, the word "kilometerstand" is used, meaning the current standing of the kilometer counter in the odometer.
-
How to get the Currency Symbol in multiple platforms
Lars Fosdal replied to John Kouraklis's topic in Cross-platform
https://restcountries.eu offers various free REST APIs for retrieving lists of country-specific info. Pretty nice. AFAIK, there are no cross-platform libs to get country-specific info. Ugly workaround for your apps: Let your user specify the locale. Alternatively, implement for each platform. Windows https://docs.microsoft.com/en-us/windows/desktop/api/winnls/nf-winnls-getlocaleinfoa Android https://developer.android.com/reference/java/util/Locale iOS https://developer.apple.com/documentation/foundation/nslocale/1643060-countrycode Linux No std way as far as I know. -
Passing back a string from an external program
Lars Fosdal replied to dummzeuch's topic in Windows API
Depends on the dictionary, I guess. Race conditions rarely flag as errors, but cause inconsistent or erratic data, possibly leading to wrongful processing later on. In this case, at least one of the four parties would be denied access (due to exclusive write lock and - depending on the programmmer - exclusive read) to the file and hence should have/raise awareness of a problem. But, whatever. -
Blocking the Windows Screen Saver in Delphi
Lars Fosdal replied to dummzeuch's topic in Tips / Blogs / Tutorials / Videos
It turns NumLock off/on/off (or on/off/on) within less than 30 ms every 60 seconds, so I don't think it will bother you much. -
Blocking the Windows Screen Saver in Delphi
Lars Fosdal replied to dummzeuch's topic in Tips / Blogs / Tutorials / Videos
Our laptops are under company governance, so we can't prevent the session from locking as it is dictated by the policies. In addition, the idle time is really short. So - we wrote LockBlock which basically simulates toggling the numlock every 60 seconds. That prevented the auto locking, but it also prevented the screensaver from kicking in when the dektkop was locked, so I added handling of lock/unlock to disable the fake keystrokes when it was locked. Super simple UI: 60 second interval, starting at 8 in the morning, lasting for 8 hours - with an add an extra hour button for off work hours lock blocking. Project source attached. LockBlock.zip -
Passing back a string from an external program
Lars Fosdal replied to dummzeuch's topic in Windows API
I'd call that a concurrency issue, rather than a race condition? SharedFileName := '%temp%\MyFileName.ext.' + GetCurrentProcessId.ToString; -
If you don't document it - how do you expect to maintain backward compatibility? My code relies on REST.Json - ObjectInstance := TJson.JsonToObject<T>(aJsonString) ; - aJsonString := TJson.ObjectToJsonString(ObjectInstance, [joIgnoreEmptyStrings, joIgnoreEmptyArrays, joDateIsUTC, joDateFormatISO8601]); - TJSONInterceptor / JsonReflectAttribute for instructing the converter to drop TDateTime properties that have 0 as value. What are the equivalents in System.Json ?
-
I wish the various Json classes were better documented. http://docwiki.embarcadero.com/Libraries/Rio/en/REST.JsonReflect is particularly poorly documented with regards to marshalling, interceptors and converters. I have long been wondering if the TJson.JsonToObject<T>(aJsonString) can be made to handle mixed type arrays {"list": [5, "text", {"prop": "value"}]} by injecting converters, but it seems impossible - but then again - the above mentioned tools are undocumented.
-
Passing back a string from an external program
Lars Fosdal replied to dummzeuch's topic in Windows API
What about a memory mapped file? An idealist kind of solution could perhaps be a message queue system like f.x. RabbitMQ (https://www.rabbitmq.com/) -
Also, https://github.com/tananaev/rootless-logcat Same caveat.
-
https://bitbucket.org/mlopatkin/android-log-viewer/src/default/ Caveat: I have not tried it myself
-
Good design for "file was just saved" message
Lars Fosdal replied to Mike Torrettinni's topic in General Help
Another option would be to specify that file should be opened after creation already at the point where you decide to make the file in the first place. -
Good design for "file was just saved" message
Lars Fosdal replied to Mike Torrettinni's topic in General Help
Yes, that could work well, IMO. -
Good design for "file was just saved" message
Lars Fosdal replied to Mike Torrettinni's topic in General Help
I'd make a generic "application log" where I would log the different actions, and for each action type - offer the possible options when viewing the log. Whenever something is logged, I'd have a "New Item" counter (Think "number of unread") somewhere in the UI - f.x. on a button that opens the log. I also strongly dislike modal dialogs, but when I need that file, I do need a mechanism to find it. -
SDTimes Industry Watch: The developer transformation
Lars Fosdal posted a topic in Project Planning and -Management
https://sdtimes.com/softwaredev/industry-watch-the-developer-transformation/ -
Very slow access to class parameters by properties
Lars Fosdal replied to ŁukaszDe's topic in Algorithms, Data Structures and Class Design
As David says - if the setter and getter don't do anything but assignments, you can eliminate them. property X: Integer read pX write pX; property Y: Integer read pY write pY; -
The joys of inherited problems. I am looking for a pattern or advice on how to best allocate a set of records to a specific requestor when there are multiple parallel requests, and there are multiple parallel request viewers. In practice: Allocate a set of deliveries to a user, when there can be multiple users requesting deliveries in parallel, and multiple users viewing the deliveries in parallel. The current SQL can give deadlocks both on allocations and on viewing, so something is definitively not right, and I need to improve on it. There is SO, where it all is an exercise in asking the question right... Are there other good discussion sites?