Jump to content

Remy Lebeau

Members
  • Content Count

    3060
  • Joined

  • Last visited

  • Days Won

    139

Everything posted by Remy Lebeau

  1. Actually, it can. When TIdIOHandler reads data from a socket, it reads everything that is available on the socket at that moment, and puts the data in its InputBuffer for read methods to then pull from. So, if there are multiple messages in flight on the connection, the IOHandler could read multiple messages at a time (or even portions of messages). Whether or not this causes problems for your code, I couldn't say for sure, since you did not show your actual code, only pseudo-code. IN THEORY, it should be OK, but I don't want to speculate without seeing your real code. But even with your pseudo-code, there is still potential for the Receive Thread to receive messages other than KeepAlives. If the main thread performs a read and it times out (network lag, etc), the message it was waiting on will not be read in the main thread, so it still will be waiting, and the Receive Thread could then pick it up. Depending on how the messages are formatted, and how you are reading them, there is even potential for the main thread to read only a portion of a message (it could receive some bytes, and then time out on others), and then the Receive Thread could read the remaining portion of the same message. It is really hard to diagnose your issue without actual details about your protocol, and seeing your actual code. If you happen to send that message near/exactly at the KeepAlive time, the server could send a KeepAlive message before it has a chance to see your new message. And then your main thread would end up reading the KeepAlive first, and if you unlock at that time then the Receive Thread would read the response that the main thread was expecting. To avoid that, you would need to have logic in the main thread that loops reading messages inside the lock until a non-KeepAlive message is received. That likely indicates the data the main thread is waiting for has actually being read and discarded by the Receive Thread, such as if you are getting your threads out of sync with each other, causing them to read each other's messages (or worse, partial messages). Unsolicited messaging and command/response messaging don't play well together in the kind of design you have chosen. That is why I suggest you re-write the logic to the kind of design I suggested earlier. No, you would receive the KeepAlive message. Why would you expect to receive garbage? Unless you mean the processing of the KeepAlive message as a response would be interpreted as garbage in the context of your requested operation. Then yes, that could happen. But you definitely don't want actual garbage appearing on your protocol, that would corrupt everything, and you would have to disconnect and start over. The only way that can happen is if either 1) nothing is being sent at all from the server (unlikely, but use a packet sniffer to verify), or 2) the data is being read and discarded somewhere else before you have a chance to read it in the main thread. Data doesn't just disappear unexpectedly. Yes, but then you would have to detect that KeepAlive message and perform another read to receive the actual response message you were expecting the first time. Again, please show your ACTUAL CODE in order to troubleshoot that. I can't answer that without seeing your ACTUAL PROTOCOL and your ACTUAL CODE.
  2. You have two threads that are wanting to read at the same time, and write at the same time. This is not a good design choice. It is a race between the threads, you don't know which thread will actually receive which message, so BOTH threads have to be ready to reply to KeepAlives, and BOTH threads have to be ready to handle non-KeepAlive replies. And worse, it is possible that one thread may read bytes that the other thread is expecting. It is just not a good situation to be in. I would suggest a completely different approach. Have 2 worker threads - 1 thread whose sole task is to read ALL incoming packets, and 1 thread whose sole task is to write ALL outgoing packets. When the reading thread receives a message, if the message is a KeepAlive then the reading thread can ask the writing thread to send a reply, otherwise the reading thread can dispatch the message for processing as needed (which probably shouldn't be done in the main thread, if you can avoid it). When the main thread (or any other thread) wants to send a message, it can ask the writing thread to send it. This way, you don't need to synchronize the reads and writes anymore. Only synchronize the requests for sending messages. Reading thread: Reader.Execute while not Terminated receive data (no timeout) if message type is keepalive create ack message Writer.Send(message) else dispatch message for processing Writing thread: Writer.Send(message) queue.lock try queue.add(message) signal.set finally queue.unlock end Writer.Execute while not Terminated if signal.wait(timeout) try queue.lock try make copy of queue queue.clear signal.reset finally queue.unlock end; send messages in copy finally copy.clear end; Main thread: Main.DoSomething create xml message Writer.Send(message) Main.DataReceived(data) parse data if should send next message create xml message Writer.Send(message)
  3. Remy Lebeau

    Android JInputStream wrapper?

    You don't need to know the file size for that. Simply stop reading when the stream's various read() methods return -1 indicating EOF has been reached. But, if you absolutely did need to know a file's size, then use the provided Uri to open a JFile to the file, and then you can call its length() method. You can create a JInputStream for a JFile using Android's FileInputStream class.
  4. Remy Lebeau

    Is this C++ builders new FORUM ???

    There is no longer an OFFICIAL forum provided by Embarcadero for Delphi/C++Builder. But, this is one of many UNOFFICIAL forums, yes. See https://blogs.embarcadero.com/community/ for other sites that Embarcadero has provided links to. The old old Delphi/C++Builder forums were migrated to Idera's community site awhile back ago, but just a couple of months ago Embarcadero shut down their forum on that site. They have decided that they don't want to compete with 3rd party forums anymore. They have had a long history of producing sub-par forum software, server failures/outages, etc, so they have finally given up trying to run their own forum. Gone for good, except for whatever may have been cached by 3rd party archiving sites.
  5. Sounds like an issue that needs to be reported to Embarcadero.
  6. Remy Lebeau

    Try-Finally-end; & Exit??

    No, see: http://docwiki.embarcadero.com/RADStudio/en/Exceptions_(Delphi)#Try...finally_Statements
  7. I can't answer that. And? Did you try using a newer version of OpenSSL? 1.0.2q is not the latest (I'm guessing you are using the libraries from Indy's GitHub repo?). The last version in the 1.0.2 series was 1.0.2u. I don't have Android binaries for that version, but I'm sure you can find them if you search around. For supporting the OpenSSL 1.1.x series, there is this SSLIOHandler instead, but I don't know if it works on Android or not. Do you have libssl.so in the same folder? Are they from the same OpenSSL version? Are you calling Indy's IdOpenSSLSetLibPath() function at program startup?
  8. Modern Android versions simply don't support OpenSSL anymore. They use BoringSSL instead, which Indy does not support. And on some (not all) Android devices, BoringSSL gets used even if the app tries to use OpenSSL dynamically. So, unless you statically build OpenSSL directly into your app binary, using OpenSSL on Android is a hit-or-miss endeavour. What does Indy's WhichFailedToLoad() function report when the error occurs? Also, does Indy's OpenSSLVersion() function report anything?
  9. Remy Lebeau

    Can the width of TaskMessageDlg be set?

    Not according to the documentation those links point to, they don’t. You can install a thread-local hook from SetWindowsHookEx() or SetWinEventHook() before calling TaskDialogIndirect()/TTaskDialog.Execute(), to intercept the dialog’s HWND when it is actually created, and then you can do whatever you want to it.
  10. Remy Lebeau

    How to completely hide application from taskbar (on launch)?

    It is for both: https://devblogs.microsoft.com/oldnewthing/20031229-00/?p=41283 Unlike VCL, FMX does not allow you to customize the creation of a Form’s HWND, so there is no option to remove the WS_EX_APPWINDOW style up front. Not without modifying FMX’s source code, as you have discovered. Is there a Unit1 already present in the source folder, if not in the Project? When creating a new unit, the IDE looks at existing units in the folder and chooses the highest unused sequential number. The best way to avoid creating the taskbar button at all is to not create any top-level HWNDs that have the WS_EX_APPWINDOW style. Something FMX tries to prevent you from doing.
  11. Remy Lebeau

    How to completely hide application from taskbar (on launch)?

    https://stackoverflow.com/questions/16768986/how-to-hide-firemonkey-application-button-from-taskbar-xe4 https://stackoverflow.com/questions/53765725/hide-taskbar-button-in-fmx-on-windows
  12. Remy Lebeau

    Internet time sync - with some timeout

    Starting a new elevated process can be done using the Win32 API ShellExecute/Ex() with the "runas" verb. Instantiating an elevated COM object can be done using the COM Elevation Moniker.
  13. Would one of these work for you (not sure whether UtcOffset returns positive or negative for, say, UTC-08:00)? Result := TTimeZone.Local.UtcOffset.TotalMinutes / 60 / 24; or: Result := -1 * (TTimeZone.Local.UtcOffset.TotalMinutes / 60 / 24); This would avoid having to query the system clock at all. I'm thinking of updating IdGlobal.OffsetFromUTC() to use TTimeZone internally when available, in which case TimeZoneBias() under Delphi UNIX can just be: Result := -OffsetFromUTC; Like it is on all other platforms.
  14. Remy Lebeau

    Internet time sync - with some timeout

    That would require moving the routine into a separate process or COM object that can then run elevated.
  15. I assume, then, that gettimeofday() is failing? That might be related to this: https://github.com/IndySockets/Indy/issues/245 Thanks. I'll look into incorporating TTimeZone into Indy. There are quite a number of code fragments that can make use of it. What issue is that? Is it an Indy issue, or just a Delphi issue?
  16. Remy Lebeau

    Internet time sync - with some timeout

    But WHERE is it hanging exactly? You need to determine that. Is it on the actual transmission of the request packet? On the receiving of the response packet? On the setting of the PC clock? Your code is calling only 1 method (SyncTime), but that method does multiple things internally, so there are multiple points of failure. Have you tried to use a packet sniffer, like Wireshark, to make sure the SNTP request is actually being sent, and an SNTP response is being received? That is because SNTP runs over UDP, so there is no connection, like you are thinking of. A UDP socket can be "connected" to a remote peer by assigning the peer's IP address statically to the UDP socket, thus sends/receives via that socket will interact only with that particular IP. But that is optional. Your code is not calling the TIdSNTP.Connect() method to establish such a static association, which is why you are not seeing the OnConnected/OnDisconnected events being fired. By default, TIdSNTP will simply write a datagram to the network and then wait for a responding datagram to come back from any peer to TIdSNTP's sending port, which is usually fine in most cases.
  17. That means Indy's IdGlobal.OffsetFromUTC() and/or IdGlobalProtocols.TimeZoneBias() function is returning a time zone offset of 0.0. So, either the function(s) are failing to query the OS for the correct time zone, or the time zone is actually being reported as UTC. You are going to have to debug into Indy's code to see which is actually the case. No. The local time zone is always queried dynamically. It just happens to be coming back as offset 0 in your situation. I can't answer that without more details about what exactly is failing.
  18. Remy Lebeau

    Main Form appearing issue..

    That is wrong. The PopupParent should be the AudioForm instead, since it is the one invoking the print dialog. There is only 1 good reason to ever do that - if you want the AudioForm to be a top-level window (acting as a sibling of the MainForm, so either Form could appear on top of the other at any time) with its own button on the Taskbar. Otherwise, without that code, the AudioForm would by default be owned by the TApplication window (when Application.ShowMainFormOnTaskbar=false) or the MainForm window (when Application.ShowMainFormOnTaskbar=true), and thus would not have a Taskbar button, and would always appear on top of its owner, never underneath it.
  19. Remy Lebeau

    New to Json

    No, it is not, as the code in question is not creating any new JSON objects, only reading them. The framework will do all of the necessary creations for you while parsing the JSON string. It is not necessary in this example at all. If you were creating a new JSON document, THEN you would have to create new objects as needed. Only the root object returned by ParseJSONValue() needs to be freed explicitly. The rest of the objects in the document are owned by the root object and will be freed automatically when the root object is freed.
  20. Remy Lebeau

    How to .Free a non-modal form?

    No. You have to reset it manually. Unless the parent form needs to reset its FNonModalForm variable, in which case you would need to move the OnClose handler to the parent form instead, eg: procedure TForm1.Button1Click(Sender: TObject); begin if FNonModalForm = nil then begin FNonModalForm := TForm2.Create(Self); FNonModalForm.OnClose := NonModalFormClosed; end; FNonModalForm.Show; end; procedure TForm1.NonModalFormClosed(Sender: TObject; var Action; TCloseAction); begin FNonModalForm := nil; Action := caFree; end;
  21. Remy Lebeau

    Using Indy with Xero API

    Do you mean your WebBroker server is sending back a 200 response to your clients? That is likely because you are not reporting any error to WebBroker, so it thinks the client's request is successful. If TIdHTTP receives a HTTP "400 Bad Request" response, it will raise an EIdHTTPProtocolException exception, unless you explicitly tell it not to. But, you are swallowing ALL exceptions raised by TIdHTTP, so WebBroker won't know about them. Your exception handler should either re-raise any caught exception, or at least populate WebBroker's Response with an appropriate error code and content. Does the body content of Xero's response say WHY the request is bad? I suspect it is because of the issue below... That is not what BasicAuthentication means. Setting that property to False tells TIdHTTP that it is not allowed to fallback to using HTTP's "BASIC" authentication scheme if a more appropriate scheme is not selected, after TIdHTTP has looked at a response's "WWW-Authenticate" header, matched it to any registered TIdAuthentication classes, and fired its On(Select)Authorization events to allow you to customize the handling. Since you are using the TIdHTTP.Request.CustomHeaders property to provide authentication credentials manually, you are responsible for ensuring the proper formatting of that data. Which, if Xero is really expecting BASIC authentication, as your code suggests, then your credential data is NOT being formatted properly. That could easily cause a 400 error. BASIC does not use url-encoding at all. And even if it did, your formatted string would still be malformed, as it lacks the required 'Basic ' prefix. For proper BASIC formatting, you would need to change this: tempString := StringReplace(UrlEncode(sClientID + ':' + sClientSecret),#$D#$A, '', [rfReplaceAll]); To this instead: // TIdEncoderMIME is Indy's Base64 encoder tempString := 'Basic ' + TIdEncoderMIME.EncodeString(sClientID + ':' + sClientSecret, IndyTextEncoding_UTF8); But, if Xero really expects BASIC authentication, you don't need to use the CustomHeaders property for that, as TIdHTTP has built-in support for BASIC. Simply set BasicAuthentication to True, and then set the TIdHTTP.Request.UserName and TIdHTTP.Request.Password properties as needed. Let TIdHTTP handle the formatting of the request's "Authorization" header for you: my_http.Request.BasicAuthentication := True; my_http.Request.UserName := sClientID; my_http.Request.Password := sClientSecret;
  22. Remy Lebeau

    Retrieving gmail email with TIDPop3

    POP3 will work for Gmail, in that you can connect and download emails, but it just may not work the way you are expecting a typical POP3 server to behave. See: Read Gmail messages on other email clients using POP Gmail’s POP3 behavior
  23. Remy Lebeau

    Retrieving gmail email with TIDPop3

    Why POP3 and not IMAP? GMail's POP3 implementation is a bit of an oddball, it does not exhibit standard POP3 behaviors. This is described in GMail's POP3 documentation. This is why I question your choice to use POP3 to begin with. More precisely, it tells you that you have 274 emails that have not been downloaded via POP3 yet. Once you have downloaded a Gmail email via POP3, you can't access it again via POP3, even if it still exists in the Inbox. In your Gmail settings, you have to specify what you want Gmail to do with emails that have been downloaded via PO3.
  24. Remy Lebeau

    Creating custom file groupings

    The IDE just knows that PAS/CPP and DFM files are related to each other, so it groups them together automatically based on matching unit/file names. AFAIK, there is no registration/API you can use to do this kind of grouping for other types of files.
  25. "Best" is a bit subjective. What you describe could be the EASIEST way, but not necessarily the BEST or FASTEST way. An alternative that doesn't involve spawning a separate process or using any file I/O would be to use NtQuerySytemInformation(SystemHandleInformation) instead to find open handles that belong to your process, and then resolve which files they refer to. See Enumerating opened handles from a process. Yes, it is more work to code for, but it may be much more efficient in the long run.
×