Jump to content

Remy Lebeau

Members
  • Content Count

    2349
  • Joined

  • Last visited

  • Days Won

    95

Everything posted by Remy Lebeau

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. Remy Lebeau

    Is anybody using Indy on Android/iOS in Delphi?

    Thanks Dave
  7. 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.
  8. Remy Lebeau

    brcc32 and rc file format...

    Really? About Resource Files, looks pretty formal to me.
  9. 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;
  10. 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;
  11. 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.
  12. 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.
  13. 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.
  14. Remy Lebeau

    Need help adding namespaces to SOAP

    Note without seeing the actual code and a log of the actual TCP communications. Probably the XML was malformed in a way the server didn't like, so it forcibly closed the connection. Though an HTTP 504 response code is a Gateway Timeout error, it is meant to be used only by gateways, not by servers. So maybe there is an intermediate proxy between your client and the target server, and the proxy experienced a problem? Really hard to diagnose something like this without any context or details.
  15. Remy Lebeau

    Memory leak in UnicodeString to string conversion

    Then I don't see how there can possibly be a leak in the code you have shown. Something else is going on. You are just going to have to debug the code for yourself to see exactly where the real leak is. What you described earlier suggests that the compiler is not freeing the 'Recognition: string' variable when your Parallel.For() procedure exits, and that is simply not how the compiler is supposed to work. That is not necessary. As I stated earlier, you can assign a WideString directly to a String, and vice versa. The compiler will handle everything for you, as WideString and String are both managed types that the compiler knows how to convert between, and free when they go out of scope.
  16. Remy Lebeau

    Memory leak in UnicodeString to string conversion

    Then you are going to have to ask the library author to change it, because this implementation is broken. IRecognitionResult is derived from IDispatch, which is an OLE interface. Pascal strings simply are not compatible with OLE - period. The Pascal wrapper for the interface MUST use WideString instead, which uses the OLE BSTR string type.
  17. I can't really comment on the multicast issue, especially on Android. But if you are really doing multicasting, why are you using TIdUDPServer? That is not a multicast-enabled component. Indy has separate TIdIPMCastClient and TIdIPMCastServer components specifically for multicast. For UDP, assuming a plain vanilla network broadcast is in fact, being used, are you setting up the TIdUDPServer.Bindings collection at all prior to activating the server? One problem I do see is your use of TThread.Queue() captures the ABinding object, which is no longer valid once the TIdUDPServer is freed. Delphi anonymous procedures capture variables, not values. You should use TThread.Queue() more like this instead: procedure TForm3.QueueLog(const AMsg: string); begin TThread.Queue(nil, procedure begin Log(AMsg); end ); end; procedure TForm3.QueueTestConnection(const APeerIP: string); begin TThread.Queue(nil, procedure begin TestConnection(APeerIP); end ); end; procedure TForm3.IdUDPServer1UDPRead(AThread: TIdUDPListenerThread; const AData: TIdBytes; ABinding: TIdSocketHandle); var Data: string; begin Data := TEncoding.Default.GetString(AData); QueueLog('Incoming broadcast message from: ' + ABinding.PeerIP); if SameText(Data, 'mytriggerphrase') then begin QueueTestConnection(ABinding.PeerIP); end; end;
  18. Remy Lebeau

    Bug in Delphi string behavior?

    Doesn't matter. The Delphi compiler still manages WideString instances for you. It knows to use the Windows memory manager instead of the RTL memory manager for allocating and freeing WideString data. Doesn't make WideString any less of a managed type compared to other string types.
  19. Remy Lebeau

    Memory leak in UnicodeString to string conversion

    That type-cast is not necessary. Assigning a String (or a WideString) to a String is handled automatically by the compiler. This is not the source of your leaking. Did you change the recognition_text property to return a WideString instead of a String, like I suggested 8 months ago? What does the implementation of Get_text() actually look like?
  20. Remy Lebeau

    Bug in Delphi string behavior?

    It should, as WideString is a managed type. It would be more accurate to say that it does not occur for non-managed types, like integers, etc.
  21. Remy Lebeau

    What's the best common folder for...

    Then CSIDL_COMMON_APPDATA/FOLDERID_ProgramData is appropriate.
  22. Remy Lebeau

    SHOpenFolderAndSelectItems 64-bit

    Then you need to grep the RTL source files to find it, or just add the declaration to your own code. The *debugger* doesn't care about DLL reference, so I assume you mean the *compiler* instead? Of course it can. Every Win32 API DLL can be, Microsoft ships both 32bit and 64bit versions of them.
  23. Remy Lebeau

    SHOpenFolderAndSelectItems 64-bit

    Why not simply declare the function yourself in your own code? function SHOpenFolderAndSelectItems(pidlFolder: PCIDLIST_ABSOLUTE; cidl: UINT; apidl: PCUITEMID_CHILD_ARRAY; dwFlags: DWORD): HRESULT; stdcall; external 'shell32.dll';
  24. Remy Lebeau

    Quote of the Day...

    TIdQOTD implements the "Quote of the Day" protocol described in RFC 865. It connects to a QOTD server on port 17, grabs all of the raw bytes the server provides until the server closes the connection, and then returns the raw bytes as a string. But you are not connecting to a real QOTD server on "quotes4all.net". When you changed the TIdQOTD.Port property from 17 to 80, you are connecting to an HTTP server on "quotes4all.net" instead, and as such it requires an HTTP client rather than a QOTD client. You are not seeing any data being returned because TIdQOTD is not requesting any data, and eventually the HTTP server closes the connection when it does not receive a valid HTTP request. In Alister's demo, he connects to real QOTD servers for "alpha.mike-r.com" and "quotes4all.net" on port 17 only, not on port 80. The QOTD server on "alpha.mike-r.com" still works (I just tried it), but it does not appear that there is a QOTD server running at "quotes4all.net" on port 17 anymore (or maybe it is just offline right now, who knows. Alister's demo is over a decade old, after all). So, if you want to use TIdQOTD, you need to connect to a real QOTD server, like the one at "alpha.mike-r.com". If you want to use "quotes4all.net" then you need to use TIdHTTP instead of TIdQOTD, eg: uses ..., IdHTTP; var HTTP: TIdHTTP; Quote: string; begin HTTP := TIdHTTP.Create; try Quote := HTTP.Get('http://quotes4all.net'); finally HTTP.Free; end; end; Just know that you are going to get more data than you are expecting, since quotes4all's HTTP server delivers content in HTML format, which you will have to parse afterwards to extract the text you want.
×