Jump to content

Remy Lebeau

Members
  • Content Count

    2343
  • Joined

  • Last visited

  • Days Won

    95

Everything posted by Remy Lebeau

  1. Indy uses blocking sockets and synchronous I/O. The client's OnDisconnected event is fired when the *client* disconnects on its end. If the server disconnects first, there is no real-time notification of that. The client will notify your code only when the client tries to access the connection and gets an error from the OS, at which time it will raise an exception to your code, such as EIdConnClosedGracefully, etc. So, if you want timely notification of a remote disconnect, you need to actively send/read data. If your code is not using the connection for lengths of time, use a timer to poll for incoming data periodically. Or use a reading loop in a worker thread.
  2. Remy Lebeau

    End of licence Comunity edition.

    Per the CE FAQ: https://www.embarcadero.com/products/delphi/starter/faq
  3. Remy Lebeau

    Could not load OpenSSL library.

    I don't know, I thought they were done maintaining a copy, but I guess not. The readme on that mirror says:
  4. Remy Lebeau

    String literals more then 255 chars

    Influenced by C# (like several features in Delphi have been): https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-11.0/raw-string-literal
  5. It is working fine for me. Yes. My hosting provider did perform maintenance on my site's server yesterday, so maybe they messed something up, or have already resolved the issue.
  6. Remy Lebeau

    Could not load OpenSSL library.

    OK, well... The fulgan mirror has been decommissioned for some time now, the GitHub repo is the official spot now.
  7. But at least we get some of that back via Embarcadero MVP!
  8. Remy Lebeau

    Could not load OpenSSL library.

    Those are the same DLLs that used to be on the indy.fulgan.com site.
  9. Remy Lebeau

    Allowed to install CE on two PCs?

    Yes, per the EULA: The EULA explains several restrictions on the Community Edition, but this is not one of them.
  10. Remy Lebeau

    ProcessMessages doesn't seem to update controls

    The Delay option was added in 10.4 Sydney, so it is a relatively recent addition (2020).
  11. Remy Lebeau

    ProcessMessages doesn't seem to update controls

    BCB6 predates Visual Styles and VCL Styles and basically most modern UI practices. For example, when UI changes are animated by the OS or Styles, it would thus take multiple paint cycles to fully animate the complete state change, and ProcessMessages() or Update() may not (likely will not) catch all of the paint cycles in a single call. You can mitigate that somewhat by using thread pools, for instance. There is nothing in UI programming that says like "state change is finished and the UI for it is fully drawn". Not even back in BCB6's day. Although back then, drawing was much simpler, and usually completed in 1 paint cycle. That may not be the case anymore in modern UIs. That is not really a factor of the property itself, but in the UI rendering behind the scenes, so more oriented to differ between OS versions rather than compiler/VCL versions. Update() only processes paint messages that are already pending in the message queue. But you also need Invaliidate() to generate those messages in the first place, if they are not already generated for you. So that is where Repaint() comes into play, to signal that a new paint cycle is needed if it hasn't already been signaled.
  12. Remy Lebeau

    ProcessMessages doesn't seem to update controls

    Yes, it does. But it can only handle messages that are already in the message queue (or synthetically generated by the queue) at the moment it is called. It does not process future messages. And the behavior you describe sounds like the actions are being delayed such that ProcessMessages() doesn't see all of them right away, which is why calling ProcessMessages() multiple times produces better results. You really should not be using ProcessMessages() in this manner in the first place. Break up your code logic using TThread::ForceQueue() or a TTimer or equivalent to allow control to flow back into the main message loop so it can continue to process new messages while your desired time interval is waiting to elapse. For example, using TThread::ForceQueue(): void __fastcall TForm1::Button1Click(TObject *Sender) { Button1->Enabled = false; Button2->Enabled = false; Button3->Enabled = false; TThread::ForceQueue(nullptr, &Step2, 3000); } void __fastcall TForm1::Step2() { Button1->Enabled = true; Button2->Enabled = true; Button3->Enabled = true; TThread::ForceQueue(&Step3, 3000); } void __fastcall TForm1::Step3() { //... } // alternatively void __fastcall TForm1::Button1Click(TObject *Sender) { Button1->Enabled = false; Button2->Enabled = false; Button3->Enabled = false; TThread::ForceQueue(nullptr, [this](){ Button1->Enabled = true; Button2->Enabled = true; Button3->Enabled = true; TThread::ForceQueue(nullpr, [](){ //... }, 3000); }, 3000); } Or, using TTimer: void __fastcall TForm1::Button1Click(TObject *Sender) { Button1->Enabled = false; Button2->Enabled = false; Button3->Enabled = false; //Timer1->Interval = 3000; Timer1->Tag = 1; Timer1->Enabled = true; } void __fastcall TForm1::Timer1Timer(TObject *Sender) { if (Timer1->Tag == 1) { Button1->Enabled = true; Button2->Enabled = true; Button3->Enabled = true; //Timer1->Interval = 3000; Timer1->Tag = 2; } else { //... Timer1->Enabled = false; Timer1->Tag = 0; } } // alternatively... void __fastcall TForm1::Button1Click(TObject *Sender) { Button1->Enabled = false; Button2->Enabled = false; Button3->Enabled = false; //Timer1->Interval = 3000; Timer1->OnTimer = Step2; Timer1->Enabled = true; } void __fastcall TForm1::Step2(TObject *Sender) { Button1->Enabled = true; Button2->Enabled = true; Button3->Enabled = true; //Timer1->Interval = 3000; Timer1->OnTimer = Step3; } void __fastcall TForm1::Step3(TObject *Sender) { //... Timer1->Enabled = false; Timer1->OnTimer = nullptr; }
  13. Remy Lebeau

    RTTI and not ordinary data type

    Can you show the actual code you are having trouble with? What is "Meta" defined as? What is it you are trying to do with "Meta", exactly? We can't really help you without more detail. Or maybe just re-think your design? Perhaps interfaces are more suitable? Hard to say without seeing what you are actually trying to do. I'm sure there are ways to accomplish that, but again, it really depends on what your data looks like and how you are using it.
  14. Remy Lebeau

    Installing Indy 10 on Delphi 7

    What @DelphiUdIT said. Indy 10 does have .dpk package files for Delphi 7, namely: IndySystem70.dpk IndyCore70.dpk IndyProtocols70.dpk dclIndyCore70.dpk dclIndyProtocols70.dpk
  15. Remy Lebeau

    TLS v1.3

    Did you download only the main code, or did you also download the PR #299 code on top of it? No ETA at this time. My understanding is that the code works as-is, but still needs to be updated/finalized to include design-time support, added to all of the supported packages, etc.
  16. Remy Lebeau

    looking for a "special" checkbox component

    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.
  17. Remy Lebeau

    looking for a "special" checkbox component

    If UI space is an issue, you could use a TComboBox with Style=csDropDownList instead, and just have 3 items to choose from.
  18. Remy Lebeau

    Call for Delphi 12 Support in OpenSource projects.

    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).
  19. Remy Lebeau

    Call for Delphi 12 Support in OpenSource projects.

    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...
  20. Remy Lebeau

    IdTCPServer IdTCPClient knowledge.

    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.
  21. 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.
  22. Remy Lebeau

    IdTCPServer IdTCPClient knowledge.

    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.
  23. Remy Lebeau

    Current alternatives for SMTP with TLS 1.3

    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.
  24. Remy Lebeau

    Returning a string from a bpl

    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.
  25. Remy Lebeau

    Using bold text in Listview

    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;
×