Jump to content

Remy Lebeau

Members
  • Content Count

    2914
  • Joined

  • Last visited

  • Days Won

    130

Everything posted by Remy Lebeau

  1. This is absolutely a memory leak. You need to get rid of the 2nd TStringList.Create. But you didn't really explain HOW your code fails when you don't make that call. I see nothing in the code you have provided that can be freeing the CEItemStatModification.Modifications object prematurely. I do not see you using a JSON streaming library that saves your objects to a stream, or reads a stream into your objects (so why are you decorating your class fields with JSON attributes?), so I don't see what persistence has to do with anything. You are just going to have to debug your code to find out exactly what is happening. Put a breakpoint inside your constructor, make sure it is being called correctly. Put a breakpoint after the constructor, make sure the newly constructed object is still proper after being constructed. Put breakpoints before and after every step of the code that touches your objects, make sure the objects are still proper after every step. As soon as you find a deviation, STOP, repeat, and trace into the step that didn't act as expected.
  2. Remy Lebeau

    Delphi 10.3.3 - Indy from 0ba2688b Git "Hello World"

    Yes, that is what it does. The OnConnect, OnDisconnect, OnExecute, and OnException events are called in the context of the connection thread, yes. That is a bit broad to answer. It really depends on how and where they are being used. I'm assuming you are referring to performing per-thread preparations, right? Then it comes down to whether you are using Indy's default 1-thread-per-connection model, or using thread pooling instead. If the former, then the server's OnConnect and OnDisconnect events will usually suffice. If you do not assign any component to the server's Scheduler property, a TIdSchedulerOfThreadDefault component is created and assigned for you automatically when you activate the server. TIdSchedulerOfThreadDefault creates a new thread per connection, and frees each thread when the connections are closed. Thus the server's OnConnect and OnDisconnect events are called only once per thread. If the latter, then as connections are closed and threads get reused, the OnConnect and OnDisconnect events can be called for multiple connections on the same thread, so that wouldn't really be a good place to perform per-thread initializations/cleanups (per-connection, yes). A better option (at least for now) is to derive a new class from TIdThreadWithTask and have it override the virtual BeforeExecute() and AfterExecute() methods to perform per-thread initializations/cleanups as needed, and then you can explicitly assign a TIdSchedulerOfThreadPool component to the server's Scheduler property, and assign your Task class to the TIdSchedulerOfThreadPool.ThreadClass property. Technically, you could take the same Task approach with TIdSchedulerOfThreadDefault, too. But that would generally be overkill, but usable.
  3. Remy Lebeau

    Delphi 10.3.3 - Indy from 0ba2688b Git "Hello World"

    Closing the client socket while the server is active will still raise an exception on the server side, such as EIdConnClosedGracefully. The debugger can be configured to ignore that exception (IIRC, it already is by default). Deactivating the server while the client is still connected will close the socket connected to the client, and thus will usually also raise an exception on the server side if the OnExecute event is still blocked on a read/write operation. You have it backwards. The first triggering of the OnExecute will successfully read your initial text from the socket, the second triggering will not until you send more text.
  4. Remy Lebeau

    Delphi 10.3.3 - Indy from 0ba2688b Git "Hello World"

    That is because they have not been created and checked in yet.
  5. Remy Lebeau

    Delphi 10.3.3 - Indy from 0ba2688b Git "Hello World"

    This is normal behavior! Your client is sending only 1 line of text. The server's OnExecute event is looped, and so it tries to read 2 lines. There is no data for the 2nd read is receive, so it blocks the calling thread waiting for new data that will never arrive, until the client disconnects or the server is deactivated. At which time, the blocked read will then fail and raise a socket exception. Let it happen that way! The server will handle the exception for you and stop the thread that called the OnExecute event. You will see the exception only if you are running your code in the IDE's debugger (and have not configured the debugger to ignore the exception), or have assigned a handler to the server's OnException event. This is how Indy is designed to work! To help illustrate this better, you should move your client's IOHandler.Write() call into a button OnClick event, and then press it multiple times while the TIdTCPClient is connected to the server, like Kryvich described (though I would have used separate buttons for Connect, Disconnect, and Write()). You will see the ReadLn() call unblock with each button click. Then you should read Introduction to Indy. No. You already have it. It doesn't get much simpler than what you already have.
  6. Remy Lebeau

    Android 64bit and 32bit permissions

    Different build targets, different set of options. Same thing with Windows 32bit/64bit, iOS 32bit/64bit, OSX 32bit/64bit, etc.
  7. Remy Lebeau

    Android 64bit and 32bit permissions

    They roughly translate to: uygulamasinin telefon etmesine ve cagrilan yonetmesine izin verilsin mi? Allows the app to phone and manage the call? Telefon arama kayitlariniza erismek icin Permissions uygulamasina izin verilsin mi? Allow Permissions to access your phone call logs?
  8. Remy Lebeau

    SFTP and SSLFTP Are they the same?

    Interesting, I'll have to see how Qualys handles Indy's FTP server. If the TIdFTPServer.UseTLS property is set to utUseRequireTLS, the USER/PASS/XAUT commands return a '421 Service not available, closing control connection' response and close the connection if an 'AUTH SSL', 'AUTH TLS', 'AUTH TLS-C', or 'AUTH TLS-P' command has not been issued yet.
  9. Remy Lebeau

    SFTP and SSLFTP Are they the same?

    Because implicit SSL/TLS usage generally requires more ports to be opened, which system admins don't like to do. FTP is already bad enough with its port usage, what with Passive transfers and all. When explicit TLS commands began being introduced into protocols, it allowed existing server ports to be re-used for both TLS and non-TLS connections. So yes, explicit TLS takes more work to implement in code, but allows for more simplified system configuration.
  10. That code will not work. It needs to be more like this instead: function XorUtf(Str1, Str2: UTF8String): UTF8String; var I: Integer; Ci: UTF8Char; begin for I := Low(Str1) to High(Str1) do begin Ci := Str1[I] xor Str2[I]; Result := Result + Ci; end; end; procedure TForm1.Button7Click(Sender: TObject); var S, T, D: UTF8String; begin S := '1234567891234567'; T := '0000000000000000'; D := XorUtf(s, t); end; Just note that the output of XorUtf() is not guaranteed to be a valid UTF-8 string if non-ASCII characters are present in the source strings, so UTF8String is really not an appropriate return type in this situation.
  11. I don't quite understand what you are referring to. Are you trying to point out the difference between 0-based string indexing on mobile platforms vs 1-based string indexing on desktop platforms? Because that difference is well documented by Embarcadero. And it is possible to continue using 1-based string indexing on mobile platforms, and to use 0-based string indexing on desktop platforms. And what is the ACTUAL PROBLEM you are having with that? You are not making yourself clear.
  12. Remy Lebeau

    Stable remote control using Indy TCP/IP

    Not when using blocking sockets, no. There are no asynchronous events in that situation. A remote disconnect is reported only when an I/O operation fails due to the peer having already closed the connection on their end. A peer disconnecting gracefully is typically discovered as a read operation that returns 0 bytes rather than an error code. That is the condition that Indy uses to raise EIdConnClosedGracefully for a remote disconnect, so a read has to be performed to get that exception in most cases.
  13. Remy Lebeau

    Stable remote control using Indy TCP/IP

    Client side or server side? On the client side, OnDisconnect is only fired when Disconnect() is called, it is not an asynchronous event. Indy is largely synchronous only, so it is hard to get this exception without do something to trigger it. Server side might make more sense, since Indy servers generally run a continuous reading thread, so the exception is more likely to happen on that side, but just let the server handle it internally for you.
  14. Remy Lebeau

    SFTP and SSLFTP Are they the same?

    SFTP (FTP over SSH) and FTPS (FTP over SSL/TLS) are not the same thing, and are not interchangeable. So make sure you are using the correct terminology. Which technology do you really need to deal with - SFTP or FTPS? TSSLFTPClient supports FTPS, not SFTP. As TSSLFTPClient is an ICS component, you should ask questions about it in the ICS sub-forum instead of this VCL sub-forum. I don't know if ICS has an SFTP client or not.
  15. Remy Lebeau

    Stable remote control using Indy TCP/IP

    Nope. Now THAT would be a stupid thing for it to do If you close the socket yourself, it should just close, no mess, no fuss. Any read/write operation performed on the socket AFTER you or the peer has closed the connection will fail with EIdConnClosedGracefully.
  16. Is anybody using Indy on Android/iOS in Delphi (not in FreePascal)? If so, can you please try to compile the current master branch from Indy's GitHub repo in Delphi and tell me if the IdStackVCLPosix.pas unit compiles or fails? Thanks.
  17. Remy Lebeau

    Is anybody using Indy on Android/iOS in Delphi?

    Thanks Dave
  18. Remy Lebeau

    Stable remote control using Indy TCP/IP

    I disagree that it should be removed. It is a legitimate runtime error condition. Data was attempted to be read/sent over a socket after the socket was closed. If you choose to have your debugger ignore that exception, that is entirely up to you. Doesn't mean the exception itself doesn't have meaning or value.
  19. Remy Lebeau

    brcc32 and rc file format...

    Really? About Resource Files, looks pretty formal to me.
  20. Remy Lebeau

    Stable remote control using Indy TCP/IP

    That structure is incomplete for what I would have suggested for this kind of situation. See further below. The OnDisconnect event is not called until the Disconnect() method is called. You really shouldn't be using the OnDisconnect event in this situation, though. The TIdIOHandler.ReadLn() method does not raise an exception on timeout (see this and this). Instead, it sets the TIdIOHandler.ReadLnTimedOut property to True and returns a blank string, which you are not checking for. The Connected() method will return True if there is any unread data in the TIdIOHandler.InputBuffer that can continue to satisfy read operations without having to go back to the underlying socket to get more data. Once the InputBuffer is exhausted, only then does Connected() check the socket, and if the socket has been *gracfully* closed, or there is a reading error, THEN it returns False. Simply shutting off the WiFi does not close the socket connection *gracefully*, so it may take time for the OS to detect the TCP connection is dead and invalidate the socket so read/write operations can start reporting errors. If you find that the OS takes too long to report dead connections, you can enable TCP-level keep-alives on the underlying socket. Or simply use your own timeouts in your code and bail out manually if your timeouts elapse. Connected() should not be raising an exception. Although Connected() may perform a read operation, it should be ignoring any socket errors on that read. If that is not the case, then please file a bug report. Which version of Indy are you using exactly? Disconnect() calls Connected() internally if its optional ANotifyPeer parameter is True. When a socket read/write exception occurs, you don't really know the state of the communication anymore, so the only viable thing to do is to just close the connection immediately and not try to exchange any more data. Try calling Disconnect() with its ANotifyPeer parameter set to False (it is True by default). Because you weren't able to fully disconnect. You really shouldn't be using Connected() at all. Try something more like this instead: FClient.ConnectTimeout := 5000; FClient.ReadTimeout := 1000; while not Terminated do begin try FClient.Connect; except // handle connect error as needed... for I := 1 to 5 do begin if Terminated then Break; Sleep(1000); end; continue; end; try FClient.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8; while not Terminated do begin FData := FClient.IOHandler.ReadLn; if (FData = '') and FClient.IOHandler.ReadLnTimedOut then begin // handle timeout as needed... end else begin // handle data as needed... end; end; FClient.Disconnect; except FClient.Disconnect(False); // handle exception as needed... end; end;
  21. Remy Lebeau

    Null value for T

    Default() is a compiler intrinsic function, it is built right in to the compiler itself. That being said, I would suggest some tweaks to your code. There is no need to search for the key twice, that is just wasted overhead. Use the TryGetValue() method instead, which searches the key once and returns its value if found: Function TMyClass<T>.Get(Identifier: String): T; Begin If not list.TryGetValue(Identifier, Result) Then Result := Default(T); End; Alternatively, TryGetValue() sets the output value to Default(T) for you if the key is not found, so you don't need to do it explicitly at all: Function TMyClass<T>.Get(Identifier: String): T; Begin list.TryGetValue(Identifier, Result); End; But either way, this approach does not allow the caller to differentiate between a key that is actually missing vs a key that happens to have the same value as its default. So I would suggest returning a Boolean instead to tell the caller whether the key was found or not: TMyClass<T> = Class strict protected list: TDictionary<String, T>; public Function Get(Identifier: String, out Value: T): Boolean; End; Function TMyClass<T>.Get(Identifier: String; out Value: T) Boolean; Begin Result := list.TryGetValue(Identifier, Value); End;
  22. Failed HOW exactly? Please be more specific. I would need to see the actual call stacks. TIdTCPServer is a very different component than TIdUDPServer, which is a very component from TIdIPMCastServer. Again, I need SPECIFIC DETAILS, or I can't help you further. There should be no difference. Components created at design-time are created dynamically at runtime, just with the parent Form/Frame/DataModule as the Owner.
  23. Remy Lebeau

    Double entry on taskbar

    You are running 2 completely separate processes, and each one is creating its own TApplication window and a MainForm window, so of course there are going to be 2 separate Taskbar buttons, one for each process if those windows are visible. If you don't want the user to interact with the windows, simply hide them. Or, you can modify the window style of whichever window actually owns the Taskbar button (the TApplication window if TApplication.MainFormOnTaskbar=False, the MainForm window if TApplication.MainFormOnTaskbar=True) to remove the WS_EX_APPWINDOW style, then the window can't appear on the Taskbar even if the window is visible. For the TApplication window, you would have to use (Get|Set)WindowLong/Ptr(GWL_EXSTYLE) directly for that. For the TForm window, you can override its virtual CreateParams() method instead. That being said, on Windows 7 and later at least, you can group related windows with their own Taskbar buttons together into a single Taskbar button by assigning the same Application User Model ID to all of the windows. You can call SetCurrentProcessExplicitAppUserModelID() at the top of your DPR before any UI windows are created (you will likely have to set Application.MainFormOnTaskbar=True since the TApplication window gets created before the DPR code is run). Or you can apply a AppUserModelID on a per-window basis via SHGetPropertyStoreForWindow() and IPropertyStore::SetValue(PKEY_AppUserModel_ID). On a side note - Have a look at the FindCmdLineSwitch() function when searching for command-line parameters.
  24. Do you have the same error if you manually deactivate the TIdUDPServer BEFORE exiting your app? That would allow Indy to clean up everything in a more deterministic manner. The TIdUDPServer destructor will normally deactivate the server and clean up, but there is no call to that destructor in your stack trace. The stack trace suggests the RTL is freeing a UDP listener thread that is still actively running, but crashes when the TIdThread destructor tries to decrement the global IdThread.GThreadCount counter, which has probably already been finalized and freed by the RTL before the listener thread is freed. So you should deactivate the server beforehand to ensure there are no threads running when the RTL does its own cleanup.
×