-
Content Count
2684 -
Joined
-
Last visited
-
Days Won
113
Everything posted by Remy Lebeau
-
What, 1 click to open the list, and 1 click to select? I really don't think that is much of an issue. But if it is, then just use TRadioGroup/3xTRadioButton instead.
-
If UI space is an issue, you could use a TComboBox with Style=csDropDownList instead, and just have 3 items to choose from.
-
Call for Delphi 12 Support in OpenSource projects.
Remy Lebeau replied to Tommi Prami's topic in Delphi Third-Party
I also use {$LEGACYIFEND ON} in Indy for XE4+ versions. Helps the code remain backwards compatible with earlier versions by forcing the compiler to not accept {$ENDIF} to close {$IF}. The compiler can run into issues when you have nested {$IF} and {$IFDEF} blocks but don't use {$IFEND} consistently, which can trigger error E2029, as mentioned in the {$LEGACYIFEND} documentation (and I've run into it before). -
Call for Delphi 12 Support in OpenSource projects.
Remy Lebeau replied to Tommi Prami's topic in Delphi Third-Party
I don't remember the specific version, but I know there was a time when the compiler had problems with the evaluation order of {$IF} and {$IFDEF}, I think that was around the early Kylix days, so would have probably been around Delphi 6-8. But that is just a guess... -
IdTCPServer IdTCPClient knowledge.
Remy Lebeau replied to limelect's topic in Network, Cloud and Web
Again, the PeerPort alone is typically not unique enough to identify clients. You are better off assigning your own custom IDs to the TIdContext objects when clients connect/login, eg: type TMyContext = class(TIdServerContext) UserID: string; end; var Clients: TIdThreadSafeStringList; procedure TMyForm.FormCreate(Sender: TObject); begin Clients := TIdThreadSafeStringList.Create; IdTCPServer1.ContextClass := TMyContext; IdTCPServer1.Active := True; end; procedure TMyForm.FormDestroy(Sender: TObject); begin IdTCPServer1.Active := False; Clients.Free; end; procedure TMyForm.IdTCPServer1Connect(AContext: TIdContext); var UserID: string; List: TStringList; begin UserID := ...; // read user ID from client List := Clients.Lock; try if List.IndexOf(UserID) <> -1 then begin AContext.Connection.Disconnect; Exit; end; List.AddObject(UserID, AContext); TMyContext(AContext).UserID := UserID; finally Clients.Unlock; end; end; procedure TMyForm.IdTCPServer1Disconnect(AContext: TIdContext); begin if TMyContext(AContext).UserID <> '' then Clients.Remove(TMyContext(AContext).UserID); end; And then you can search your Client's list for your user IDs whenever you need, and then you'll have access to the TIdContext object. Just be careful accessing it if it happens at the same time that the client disconnects. That's why if you intend to send data from outside of the server's own threads, it is best to add a queue to the TIdContext and have the OnExecute event send the queue when safe to do so. -
Class function vs constructor for object creation
Remy Lebeau replied to Michael Taylor's topic in Algorithms, Data Structures and Class Design
Standard strings are compiler-managed types. They are created dynamically and are also reference-counted, so the compiler needs to be able to manage and release them correctly. Don't just let them leak. Otherwise, use fixed-length character arrays if you really don't want to allocate their memory dynamically. Instances of a Class are created dynamically and must be Destroy'ed/Free'd to avoid memory leaks. Otherwise, use a Record instead of a Class. -
IdTCPServer IdTCPClient knowledge.
Remy Lebeau replied to limelect's topic in Network, Cloud and Web
It is not impossible. You simply send the desired data using the IOHandler of the TIdContext object that represents the client on the server. But, you do have to be VERY careful when sending unsolicited (ie, non-requested) data from the server to a client. If you are not careful and send the unsolicited data incorrectly, you can easily corrupt your TCP communication. You need to make sure the client is not trying to request data at the same time that you are sending unsolicited data, or the client might misinterpret the data. Most protocols tat support unsolicited data design their messages in such a way that clients can identify what messages are unsolicited and what messages are responses to requests. Is this particular client EVER requesting data from the server? Or does the server ALWAYS initiate the sending? It makes a BIG difference in how you design your protocol and manage it in your code. So it is kind of difficult to answer your question definitively without knowing more about your communication needs. TCP is a 1:1 connection between client and server. There is no broadcasting. The data will be sent only to the specific client that you send it to. Are there ANY requests being sent from this client to the server? If yes, then you need to synchronize your sends so that unsolicited data does not overlap response data. This is typically done by creating a per-client queue of outgoing data, and then having the server's OnExecute event send the calling client's queue when it is safe to do so, ie when it is not servicing any requests. If no, then you can simply send data directly to the client whenever you want. There is no "peer number" in TCP. But, the server does have a unique socket handle (ie, TIdContext object) for each connected client. And you can always assign your own ID to each client, as well. You would simply send to whichever socket/context that you are interested in. This is typically handled inside the server's OnExecute event, using the TIdContext that is passed in as a parameter. But, depending on your needs, you can also search the server's Contexts list for the desired client and then send to it (if/when safe to do so). The PeerPort by itself is not adequate enough to uniquely identify clients. If clients connects to the server from different networks, they could use the same PeerPort. You would need the combination of PeerIP+PeerPort to uniquely identify each client. Better to just use the TIdContext objects instead, since they are already unique. Yes, it is. -
Current alternatives for SMTP with TLS 1.3
Remy Lebeau replied to LeusKapus's topic in Network, Cloud and Web
Indy's support for TLS 1.3 has already been implemented here, it just hasn't been merged into the main codebase yet. There is a difference. You are free to try the code for yourself and see if it meets your needs. Eventually, it will get merged in, I just couldn't tell you when. That doesn't mean you can't use it today. Other people are. -
UniqueString() would have done the same thing with less code, eg: s := GetPluginName; UniqueString(s); To make things easier on the caller, that should be done inside of the package function instead, eg: function GetPluginName: string; begin Result := 'test'; UniqueString(result); end; That makes sense if you are letting the compiler clear "tmp" AFTER the package has been unloaded, since "tmp" will be pointing at unloaded memory when the RTL tries to access it for cleanup, Calling UniqueString()/Copy() BEFORE the package is unloaded addresses that problem. Of course, you could also solve the problem by simply not returning a string literal from the function in the first place, or by ensuring the caller is completely finished using any data from the package before unloading it from memory.
-
Using bold text in Listview
Remy Lebeau replied to Bart Verbakel's topic in Algorithms, Data Structures and Class Design
Rather than using a separate class in the TListItem.Data property, it would be safer and more efficient to derive a new class from TListItem and add whatever members you want to it, and then return that class type from the ListView's OnCreateItemClass event. You can then type-cast any TListItem in the ListView to your custom class type when needed, and you don't have to worry about freeing them manually. For example: type TMyListItem = class(TListItem) public BoldText: Boolean; end; procedure TForm8.ListView1CreateItemClass(Sender: TCustomListView; var ItemClass: TListItemClass); begin ItemClass := TMyListItem; end; procedure TForm8.ListView1AdvancedCustomDrawSubItem(Sender: TCustomListView; Item: TListItem; SubItem: Integer; State: TCustomDrawState; Stage: TCustomDrawStage; var DefaultDraw: Boolean); begin if TMyListItem(Item).BoldText then Sender.Canvas.Font.Style := [fsBold]; end; ... var li := ListView1.Items.Add; TMyListItem(li).BoldText := True; Of course, in this particular example, there is a much simpler solution - just look at the data being drawn and act accordingly, eg: procedure TForm8.ListView1AdvancedCustomDrawSubItem(Sender: TCustomListView; Item: TListItem; SubItem: Integer; State: TCustomDrawState; Stage: TCustomDrawStage; var DefaultDraw: Boolean); begin if (SubItem > 0) and (StrToInt(Item.SubItems[SubItem-1]) < 0) then Sender.Canvas.Font.Style := [fsBold]; end; -
FMX form called from VCL form doesn't apply Top Most style to its child windows.
Remy Lebeau replied to vhanla's topic in FMX
Have you tried making the FMX form override its CreateHandle() method to apply the WS_EX_TOPMOST style? -
Which UDP components, exactly? Indy's? Someone else's? Please be more specific. Please show your actual code on both sides. The only way that makes sense is if the server is actually sending a $00 byte, OR, if you have pre-sized your Buf beforehand and ReceiveBuffer() is not filling it in (ie, is ReceiveBuffer() returning 0 bytes read as its return value?). Not without seeing your actual code. UDP is connection-less. There is no connection to reset.
-
issue with phone send verification code to firebase
Remy Lebeau replied to xorpas's topic in Databases
According to this documentation, the request requires a JSON body containing a recaptchaToken, iosSecret, iosReceipt, and phoneNumber, are you sending those values? That being said, I think you are using an older URL, you should be using the latest Identity Toolkit API located at https://identitytoolkit.googleapis.com instead: https://cloud.google.com/identity-platform/docs/reference/rest https://cloud.google.com/blog/products/identity-security/getting-started-with-identity-platform https://developers.google.com/identity/toolkit/migrate-identityplatform -
Playing with Windows Fibers by emulating Python Generators
Remy Lebeau replied to darnocian's topic in I made this
There are plenty of C macros that are translated into Delphi's Window SDK units. More likely, they were not translated simply because fibers are just not used by most developers, not even Microsoft: Fibers aren’t useful for much any more; there’s just one corner of it that remains useful for a reason unrelated to fibers -
I'm sure either one will be fast enough for your needs. Chess isn't very computationally heavy. The only way to know for sure is to benchmark them yourself and see which one suits your needs better.
-
https://docwiki.embarcadero.com/RADStudio/en/E2217_Published_field_'%s'_not_a_class_or_interface_type_(Delphi)
-
The signature of the event is documented: https://docwiki.embarcadero.com/Libraries/en/Data.DB.TDataSet.AfterEdit property AfterEdit: TDataSetNotifyEvent read FAfterEdit write FAfterEdit; https://docwiki.embarcadero.com/Libraries/en/Data.DB.TDataSetNotifyEvent TDataSetNotifyEvent = procedure(DataSet: TDataSet) of object;
-
Strange problem skipping loop with tTask
Remy Lebeau replied to Jud's topic in RTL and Delphi Object Pascal
Wouldn't TParallel.For() be more appropriate in this situation? procedure TForm2.RunButtonClick(Sender: TObject); const NumberOfParts = 1; begin TParallel.For(0, NumberOfParts - 1, procedure(counter: Integer) begin RunWork(counter); end ); // or simply: // TParallel.For(0, NumberOfParts - 1, RunWork); end; -
Can I use managed C# DLL in unmanaged Delphi application ?
Remy Lebeau replied to AndrewHoward's topic in General Help
Or Atozed CrossTalk, too. -
What is the recommended method of obtaining my devices IP address?
Remy Lebeau replied to JohnLM's topic in Cross-platform
Not related to the 127.0.0.1 issue - note that the code above will leak the returned TStringList on mobile platforms in Delphi 10.4 and later, as ARC is no longer being used for object lifetime management, so you need to Free() the list when you are done using it on all platforms, eg: procedure TForm1.btnGetRemyClick(Sender: TObject); var IPs: TStringList; begin IPs := GetLocalIpList2(''); try m1.Lines := IPs; finally IPs.Free; end; end; A better option is to pass in the target TStrings as a parameter and let the function add to it as needed, eg: procedure GetLocalIpList2(Name: string; IPList: TStrings); var ... begin ... IPList.BeginUpdate; try ... IPList.Add(...); ... finally IPList.EndUpdate; end; ... end; procedure TForm1.btnGetRemyClick(Sender: TObject); begin GetLocalIpList2('', m1.Lines); end; -
What is the recommended method of obtaining my devices IP address?
Remy Lebeau replied to JohnLM's topic in Cross-platform
You didn't say which code you used for which scenario. Indy's GetLocalAddressList() has a known issue where it reports only 127.0.0.1 on Android. -
while TStream_TryRead() do
Remy Lebeau replied to dummzeuch's topic in Tips / Blogs / Tutorials / Videos
Sadly, not only is -1 not documented, but it's also not always implemented. For example, THandleStream.Read() returns 0 on failure and end-of-file. Which makes error handling practically impossible for some stream types. That is what the ReadXXX() methods are meant for. Read() is the lower level method that they all use internally. -
while TStream_TryRead() do
Remy Lebeau replied to dummzeuch's topic in Tips / Blogs / Tutorials / Videos
Read() is not guaranteed to return as many bytes as requested. The documentation even says so. Use other ReadXXX() methods for that purpose instead. Since Read() can return fewer bytes without actually failing, breaking the loop on a lesser return is wrong if the intent is to read the whole stream. Only a return of -1 (error) or 0 (end of stream) should be the actual breaking condition. I prefer using 'repeat ... until False' because it avoids having to test the return value twice, eg: repeat BytesRead := Stream.Read(Buffer, BufSize); if BytesRead > 0 then ... until BytesRead <= 0; -
while TStream_TryRead() do
Remy Lebeau replied to dummzeuch's topic in Tips / Blogs / Tutorials / Videos
repeat BytesRead := Stream.Read(Buffer, BufSize); if BytesRead <= 0 then Break; // .. until False; -
What is the recommended method of obtaining my devices IP address?
Remy Lebeau replied to JohnLM's topic in Cross-platform
What you describe gets the local IPs of the device that your code is running it. It does not get the IPs of other devices on the local network. You don't need to use the IdStackXXX units directly in this example, just the base IdStack unit by itself will suffice. The correct way to use TIdStack methods is to call them on the global GStack object. Indy can instantiate the correct derived class for you based on the local OS platform, eg: uses IdGlobal, IdStack; function GetLocalIpList(Name: string): TStringList; var Lista: TIdStackLocalAddressList; temp: TIdStackLocalAddress; I: Integer; begin Result := TStringList.Create; try TIdStack.IncUsage; // instantiates GStack if needed... try Lista := TIdStackLocalAddressList.Create; try GStack.GetLocalAddressList(Lista); for I := 0 to Lista.Count-1 do begin temp := Lista[I]; if temp.IPVersion = Id_IPv4 then Result.add(temp.IPAddress); end; finally Lista.Free; end; finally TIdStack.DecUsage; // frees GStack if needed... end; except Result.Free; raise; end; end;