-
Content Count
1148 -
Joined
-
Last visited
-
Days Won
106
Everything posted by Dalija Prasnikar
-
Your code can be simplified in several ways. I will write each step separately, so you can more easily adapt your code if you need additional functionality beyond the code shown here. First, as I mentioned in previous post, you don't need to write getter and setter functions at all, if you just read or write field in those and noting more. First optimization would be removing all that redundant code. This is not just speed optimization, but also makes cleaner code that is easier to read and follow. This code is functionally completely equivalent to your declaration and you would create and initialize the form the same way you do now. TTracksAddEditForm = class(TForm) private FsMode: string; FiAlbum: integer; FsTrack: string; public property sMode: string read FsMode write FsMode; property iAlbum: integer read FiAlbum write FiAlbum; property sTrack: string read FsTrack write FsTrack; end; Your next question was whether you need to have both getters and setters. If you are just setting property, then you don't need getter. Similarly, if you have read only property that you will just read, then you don't need setter. So next optimization would be following. There is no difference in how you create and initialize your form. TTracksAddEditForm = class(TForm) private FsMode: string; FiAlbum: integer; FsTrack: string; public property sMode: string write FsMode; property iAlbum: integer write FiAlbum; property sTrack: string write FsTrack; end; procedure TAudioForm.btnInsertTrackClick(Sender: TObject); begin TracksAddEditForm := TTracksAddEditForm.Create(Self); TracksAddEditForm.sMode := 'Insert'; TracksAddEditForm.iAlbum := Albums.FieldByName('Index').AsInteger; TracksAddEditForm.sTrack := TracksView.FieldByName('Title').AsString; TracksAddEditForm.Show; end; But, the best way to initialize any class that you will construct through code (without bringing in dependency injection) would be declaring all required information as parameters to the constructor, Such code would also require different code when constructing form. TTracksAddEditForm = class(TForm) private FsMode: string; FiAlbum: integer; FsTrack: string; public constructor Create(AOwner: TComponent; const AsMode: string; AiAlbum: integer; const AsTrack: string); reintroduce; end; procedure TAudioForm.btnInsertTrackClick(Sender: TObject); begin TracksAddEditForm := TTracksAddEditForm.Create(Self, 'Insert', Albums.FieldByName('Index').AsInteger, TracksView.FieldByName('Title').AsString); TracksAddEditForm.Show; end; The advantage of passing all required data as parameters during construction process is that you cannot accidentally forget to initialize some required field. If some fields are optional, then you can stick to initializing through properties, but using simplified examples before this last one.
-
Getters and Setters are methods called when you get (read) or set (write) property. In Delphi you don't need to have getter and setter methods. Properties can be directly connected to a field. You use getters or setters when you want to run additional code besides directly reading or writing some field value. Following is basic property declaration without any getters or setters. When you access property, compiler just directly reads or writes from associated field. TFoo = class protected FValue: Integer; published property Value: Integer read FValue write FValue; end; Following is property declaration with setter method (you can have any combination you need and you don't need to declare both methods if you just need one of them) TFoo = class protected FValue: Integer; procedure SetValue(AValue: Integer); published property Value: Integer read FValue write SetValue; end; Because getters and setters are methods, they will be a bit slower than directly using a field. If your setter is just setting a field and does nothing more, then you don't really need it. Same goes for getter. If it just returns the field then it is not needed. procedure TFoo.SetValue(AValue: Integer); begin FValue := AValue; end; If for instance you don't want to allow setting Value to zero, you might have following code in a setter. procedure TFoo.SetValue(AValue: Integer); begin if AValue <> 0 then FValue := AValue; end; You can even raise an exception if you set property to invalid value or similar. The only time when you would need to use getters and setters that just read or write to a field, is when your property needs to be accessible through interface. Interfaces require that getters and setters are methods.
-
Can you please post that as Q/A on Stack Overflow. It will be more easily found there.
-
Is loading a resourcestring not threadsafe?
Dalija Prasnikar replied to Perpeto's topic in RTL and Delphi Object Pascal
Loading resource strings should be thread-safe. Issue probably happens because you are loading string resource to a shared string variable. Without seeing your code it is impossible to tell exactly. -
I don't think you can avoid that 'with' has many pitfalls and replacing code that does not use 'with' with code using 'with' is not something I would recommend doing. You have to ask yourself what is the purpose of FreeAndNil? In the case of local variable the reason (regardless whether we consider it valid or not) is to prevent accidental access of the object instance after it has been released. In such cases we hope that accessing nil reference would crash immediately revealing the obvious bug in the code. However, when you use 'with' you no longer have explicit reference to the object that you can accidentally access outside the 'with' block. Yes, if you are really sloppy, you can still call Free first and that call some other code if call to Free is not the last thing you do in the 'with' block. Main point is compiler hides the reference, there is nothing you can nil here, and compiler will certainly not access the instance outside the 'with' block by mistake.
-
Exception in TCustomSmtpClient.Destroy;
Dalija Prasnikar replied to Kyle_Katarn31's topic in ICS - Internet Component Suite
Calling Destroy in destructor without having nil check first is definitely incorrect code that can lead to memory leaks as destructor needs to be able to handle partially constructed instances. That is why Free is commonly used as it does not require sprinkling destructors with nil checks before calling Destroy. But, very likely this bad code in the destructor is not the primary cause of the issues you are seeing and that there is more going on. If the destructor is at fault, then this means that the instance was not properly constructed and that construction raised an exception. Otherwise the core problem is in some other code we don't see here. -
Retrieving data from REST async call
Dalija Prasnikar replied to DavidOne's topic in Network, Cloud and Web
This is appropriate solution. Code that runs inside those anonymous methods will run in the context of the main thread (this is defined by first boolean parameter passed to ExecuteAsync - if you pass False there then that code will run in the context of the background thread). Because it runs in the context of the main thread it will be thread-safe the same way the same way your original function is thread-safe if you run it from the main thread. The only thing you need to pay attention to, is that RESTRequest, RESTClient, and RESTResponse used for making asynchronous request can only handle single request at the time in thread-safe manner. That means, you cannot run UserRESTRequest.ExecuteAsync again while the previous request is not yet completed. Once the request completes, you can reuse those REST components to make another request. -
There are two separate concerns here. First, main application code block can exhibit some weird behavior for global variables declared there, including inline variables. If you want to make proper test of some behavior, I suggest wrapping the code in additional procedure. Second, inline variables had (and maybe still have) some problems and sometimes using them does not work as expected. If you encounter an issue, usually solution is to declare local variable instead of inline. Your text case does not work correctly in 10.3.3 when code runs in main code block. If moved to the procedure, then it behaves as it should. In 10.4.2 and and 11.2 it works correctly in all scenarios, so the issue was fixed in the meantime.
-
They route license increase to Sales and Renewals, and of course, Sales will try to sell you a subscription, but AFAIK you don't have to purchase it and they will bump your license count. Still, this is confusing practice and sometimes you need to be persistent.
-
Retrieving data from REST async call
Dalija Prasnikar replied to DavidOne's topic in Network, Cloud and Web
What you want to do is impossible in Delphi (without bringing bad coding practice like Application.ProcessMessages in the process) You cannot have function that will return the result of some asynchronous operation. Period. Asynchronous REST request has events that will run when request is successfully or unsuccessfully finished. In those events you can add logic that needs to run as the result of the operation. procedure TMainForm.ButtonClick(Sender: TObject); begin RESTRequest.ExecuteAsync( procedure begin Memo.Lines.Add(RESTResponse.Content); end, True, True, procedure(Error: TObject) begin Memo.Lines.Add(Exception(Error).Message); end); end; Another way, by running request in task or another thread uses similar approach. In such case you would transform your function into a procedure and pass callback (procedure, method or anonymous method) that will run within thread after the request is completed. If you need to run that code in the context of the main thread you can use TThread.Synchronize or TThread.Queue -
Bug report is now available to public: Cannot expand columns or rows while expand style is in fixed size https://quality.embarcadero.com/browse/RSP-39228
-
That is known (reported issue). There is some problem with migrating or applying Welcome screen layout after migration. When you launch IDE click Edit Layout on Welcome Screen. Reset Welcome Screen to default layout and then adjust it again the way you like it. Next time you start IDE it should run normally.
-
There are some scenarios where which helper will be visible does not work as expected. Last helper in scope is not always visible https://quality.embarcadero.com/browse/RSP-38448
-
How to free PythonEngine in background thread?
Dalija Prasnikar replied to DennisTW's topic in Python4Delphi
I don't use Python4Delphi so I cannot accurately tell what can and what cannot be done with it in context of threads. But there is one thing definitely wrong in your code and that is constructing TMemo in the context of the background thread. VCL controls can only ever be used from the main thread. Because how that memo control is connected to the Python engine sounds like it is TComponent based. Just being TComponent based does not necessarily mean that it cannot be used in the background threads, but in such cases all related components must belong to the same thread. If we know that TMemo must be used in the main thread, then if you connect it to the Python engine implies that such configured Python engine also must be constructed and used from the main thread. Just because you managed to "make it work" without obvious error (if you ignore the leak), does not mean that this kind of setup actually works. -
Flicker in VCL Styles can have many root causes depending on the controls used and their settings. It is hard to tell what exactly causes it here and how to fix it. It seems like here it is caused by a form (or whatever control you have as a main container) clearing its canvas with white (default) color before painting dark style. First step would be to check all combinations of various settings on both form and other controls (especially if you have another container control that host other controls) (you may want to create simple test project for that) like ParentBackground, DoubleBuffering,... Also setting explicit background color to the dark one could help but that would also require adjusting StyleElements and removing seClient from them. If that does not work then another way would be to start playing with WM_ERASEBKGND messages in a container control (frame or panel) that is not styled. Fix mentioned in RSP-24415 has some issues on Windows 11 and non styled controls like combo boxes (I haven't tested with styled ones) that are not properly refreshed and they need their repainting and erasing tweaked. It is possible that you will be able to find some satisfactory fix by simple property changes and figuring out proper combination for that form, but it is also possible that fix would require more complicated hacks.
-
Right... my brain is fried...
-
This code is not 100% correct, though. If constructor fails, Free will be called on an uninitialized reference. In order for it to work properly, IdHTTP should be initialized to nil before calling constructor. begin IdHTTP := nil; IdHTTP := TIdHTTP.Create(nil); ... Edit... forget about that.... brain fart... if the exception is raised IdHTTP.Free will never be called.
-
Custom Component : onDestroy delphi creates endless error messages
Dalija Prasnikar replied to gioma's topic in VCL
Weak references are integral part of ARC as memory management system. Because ARC was not main memory management system in classic compiler and was just an "add on", most code didn't require them or could use pointers that are equivalent of unsafe attribute. Support for weak references in classic compiler was added only after they were added on mobile ARC compiler which requires weak references in order to have functional memory management system. Proper coding pattern for ARC memory management would indeed require using weak attribute in the similar circumstances (code) OP had. In other words, if we would rewrite RTL and VCL to follow ARC rules then the OP original code would be the correct one, and we would not need nor have TComponent notification system. -
Custom Component : onDestroy delphi creates endless error messages
Dalija Prasnikar replied to gioma's topic in VCL
Weak attribute only works on interface references, not on object references. In your case, with TComponent reference, compiler just ignores weak attribute. -
Interfaces - Time to face my ignorance.
Dalija Prasnikar replied to AlanScottAgain's topic in RTL and Delphi Object Pascal
If you are having trouble managing objects memory, then using interfaces is a bad option as using them requires even more knowledge. Yes, they can manage memory for you, but there is so much fine print attached I wouldn't recommend it as a starting point. Your drawing object list class also has some unnecessary code as it inherits from generic TObjectList that already automatically handles all that. I have simplified your code and added some basic workflow. uses System.Generics.Collections; type TDrawingObjectType = (doChild, doBackground); TDrawingObjectsList = class; //Forward Declaration TDrawingObject = class private FObjectType: TDrawingObjectType; FDrawingObjectsList: TDrawingObjectsList; public constructor Create(AObjectType: TDrawingObjectType); destructor Destroy; override; property ObjectType: TDrawingObjectType read FObjectType write FObjectType; property DrawingObjectsList: TDrawingObjectsList read FDrawingObjectsList; end; TDrawingObjectsList = class(TObjectList<TDrawingObject>) public procedure DrawDraw(ACanvas: IskCanvas); procedure LoadDrawingObjects; end; procedure BuildList(List: TDrawingObjectsList); var Root: TDrawingObject; implementation constructor TDrawingObject.Create(AObjectType: TDrawingObjectType); begin FDrawingObjectsList := TDrawingObjectsList.Create(True); end; destructor TDrawingObject.Destroy; begin FDrawingObjectsList.Free; inherited; end; procedure BuildList(List: TDrawingObjectsList); var Item: TDrawingObject; begin List.Clear; // build list Item := TDrawingObject.Create(doChild); // set Item data // ... // Add Item to List List.Add(Item); Item := TDrawingObject.Create(doChild); // set Item data // ... // Add Item to List List.Add(Item); end; initialization Root := TDrawingObject.Create(doBackground); BuildList(Root.DrawingObjectsList); finalization Root.Free; end. FDrawingObjectsList list will handle lifetime of any drawing object added to the list, so you don't have to deal with them. Please note that you cannot add same drawing object to multiple lists as it will be released twice, causing exception. Next, FDrawingObjectsList belongs to its drawing object instance and as such it should be constructed in constructor and destroyed in destructor of TDrawingObject. Nobody else should free that list nor assign anything to its reference there is no reason for it to be writeable property. Constructing/destroying part is the simplest and safest possible, there are other more complex coding patterns that involve lazy initialization, but they require more code and there is more room for bugs. Unless you need to preserve memory as much as possible, you don't need to deal with more complicated code. And if needed you can easily add that later on. There is additional mistake in your destructor declaration. You are missing override directive. Without it destructor will never be called and you will have memory leaks. I have created global Root object and populated it in initialization section. You don't have to make it a global, this is just to illustrate how to work with Root object and how to populate its data. BuildList takes TDrawingObjectsList as a parameter and populates existing list without constructing new one. if the list already has some items it will be cleared - you can omit clearing the list if you know you will populate it only once, or you can clear it in outside code before calling BuildList. I am not going to comment on the interface part as it would be a really long write. -
https, post, login, get data from https site within android and .exe from delphi code
Dalija Prasnikar replied to frankie1's topic in Network, Cloud and Web
There are some examples here: https://docwiki.embarcadero.com/RADStudio/Sydney/en/Using_an_HTTP_Client var Client: THTTPClient; Response: IHTTPResponse; begin Client := THTTPClient.Create; try Response := Client.Get('https://httpbin.org/get'); ... finally Client.Free; end; This is not just https. Specific example would depend on what kind of request server expects and what kind of response it returns. If the server uses REST architecture there is another set of classes work with REST https://docwiki.embarcadero.com/RADStudio/Sydney/en/Tutorial:_Using_the_REST_Client_Library_to_Access_REST-based_Web_Services -
https, post, login, get data from https site within android and .exe from delphi code
Dalija Prasnikar replied to frankie1's topic in Network, Cloud and Web
You don't have to use OpenSSL. Delphi has own HTTP classes that support https protocol through OS provided security layer. Unless you have other specific reasons to use Indy, it is easier to use RTL classes (or components) from System.Net namespace than using Indy and fiddling with OpenSSL https://docwiki.embarcadero.com/Libraries/Sydney/en/System.Net -
FreeAndNil() - The Great Delphi Developer Debate
Dalija Prasnikar replied to AlexBelo's topic in Tips / Blogs / Tutorials / Videos
Of course. If the reference and the object is valid longer than the threads using such object, then there is no problem. But there is no need for FreeAndNil then, too. Nor locking. But that only works if that specific point can be moved outside threads or if you can use locking mechanism within threads. I am saying "can use locking" because locking is not always viable solution. You might have situation where such point is not fixed, and shared object instance through multiple interface references may be better choice as the object is valid as long as some thread is using it and you don't need to use locks. Obviously it is developer's fault for using inappropriate code in some scenario. I don't know from where you have pulled "blaming the language" because nobody did that. -
FreeAndNil() - The Great Delphi Developer Debate
Dalija Prasnikar replied to AlexBelo's topic in Tips / Blogs / Tutorials / Videos
Yes, it is a different "program". as automatic memory management requires not only changes in class declarations, but also in how they are used. fixing existing code and making it thread-safe, requires locking mechanism. There are other ways to ensure that the reference is valid while it is being used, but shared counter and atomic increments and decrements are not sufficient to achieve that. I mentioned interfaces, because automatic memory management allows different approach to a problem, and in some scenarios leads to simpler and more maintainable code, that does not require locking mechanisms that blocks other threads. Whether such approach is viable in particular scenario is another question, but we were not talking about exact code and without exact code you can only talk in very broad and general terms. When creating new reference, you don't do that from the background thread, you take existing strong reference and assign it to another one from the context of a thread that holds that strong reference so it cannot become invalid while you are assigning it to another reference. Then you pass that new reference to a new thread. As long as you have strong reference that will not be niled by any other thread, you can safely create new ones from that one. What you cannot do is assigning and nilling the same reference from different threads. No need for any kind of tricks. Another solution for initial problem, would be not calling FreeAndNil from any thread and waiting for all threads to finish before instance is released. But again, without knowing exact code, it is hard to say what is the best and proper solution. I am not sure what you mean with the last sentence. -
FreeAndNil() - The Great Delphi Developer Debate
Dalija Prasnikar replied to AlexBelo's topic in Tips / Blogs / Tutorials / Videos
While we wait for the video, I have written some additional explanation about FreeAndNil thread safety https://dalijap.blogspot.com/2022/07/freeandnil-debate-thread-safety.html