Jump to content

Remy Lebeau

Members
  • Content Count

    969
  • Joined

  • Last visited

  • Days Won

    40

Remy Lebeau last won the day on April 2

Remy Lebeau had the most liked content!

Community Reputation

567 Excellent

Technical Information

  • Delphi-Version
    Delphi XE2

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

  1. No, it is worse than that. TThread.Synchronize() puts the specified method pointer into a global thread-safe queue, along with a waitable event, then posts a signal to the main UI thread letting it know the queue is pending, and then waits on the queued event to be signaled. The main UI thread periodically iterates the queue (whenever it receives the posted signal, as well as whenever the main message loop enters an idle state, or user code calls Classes.CheckSynchronize() directly). All of the queued methods are called in order, and their events are signaled to unblock their waiting threads.
  2. Remy Lebeau

    TFileStream fmShare modes

    Sharing flags are used with the Mode parameter, not the Rights parameter. Creation and Sharing flags can be OR'ed together in the Mode parameter, eg: Result := TFileStream.Create(FileName, fmOpenReadWrite or fmShareDenyNone{, 0}); That was true in the old days when fmCreate was defined as $FFFF and thus could not be mixed with any other flags, but that is no longer the case. When the Rights parameter was introduced (which is not used on Windows when fmCreate is used), fmCreate was redefined as $FF00 so it can now be mixed with sharing flags, eg: fs := TFileStream.Create(FileName, fmCreate or fmShareDenyNone{, 0}); If no sharing mode is specified, fmShareCompat is the default, except in the specific case of the old fmCreate value ($FFFF) being used, in which case the default is fmShareExclusive instead for backwards compatibility. But either way, on Windows, fmShareCompat and fmShareExclusive mean the same thing - there is no sharing enabled on the file.
  3. No, when ABinding is bound to a wildcard IP like '::0'. Internally, TIdUDPServer uses recvfrom() to read packets, and the information you want is simply not available from recvfrom(). You would need to use recvmsg()/WSARecvMsg() instead (ie, via Indy's TIdStack.ReceiveMsg() method), which requires the IP_PKTINFO/IPV6_RECVPKINFO option be enabled on the socket for recvmsg() to report the receiving adapter's info on each packet read. TIdUDPServer does not support recvmsg(), though. You would have to alter its source code and recompile. Otherwise, you could use TIdUDPClient instead and run your own reading logic with it (despite their names, TIdUDPClient and TIdUDPServer are not true client/server components since UDP is connectionless, so their roles are a little more blurry than with TCP. You can use TIdUDPClient as a server, and TIdUDPServer as a client). Otherwise, you could simply enumerate the local IPs (ie via Indy's TIdStack.GetLocalAddressList() method) and create a separate Binding for each IP individually, and then ABinding.IP will have a meaningful value in the OnUDPRead event. This does mean that TIdUDPServer would need to allocate multiple sockets, though. No. Sending data out from a bound socket will use the IP of the adapter that the socket is bound to. If the socket is bound to a wildcard IP, then the OS will pick which adapter to use, based on its own routing tables for the destination IP. If you want to send out from a specific adapter, you need to either create and bind a separate socket (ie TIdUDPClient) to that adapter, and then send using that socket. use sendmsg()/WSASendMsg() (which Indy does not have a wrapper for), passing in a msghdr struct containing an IP_PKTINFO/IPV6_PKTINFO control message that specifies the desired source IP and interface index. See Setting the source IP for a UDP socket
  4. Almost. It would be more accurate to say that it binds to all local network adapters for the Binding's specified IPVersion. The IPVersion is set to IPv4 by default (see the ID_DEFAULT_IP_VERSION constant in IdGlobal.pas). You would need 2 separate Binding objects to bind to all IPv4 and IPv6 adapters at the same time, as TIdUDPServer would have to create separate IPv4 and IPv6 listening sockets (Indy does not support dual-stack sockets at this time: ticket #29). For example: IdUDPServer.DefaultPort := ...; with IdUDPServer.Bindings.Add do begin IP := ...; IPVersion := Id_IPv4; end; with IdUDPServer.Bindings.Add do begin IP := ...; IPVersion := Id_IPv6; end; If you don't create any Bindings at all, TIdUDPServer will create 1 or 2 default Binding objects for you, depending on whether the underlying platform allows separate IPv4+IPv6 sockets to be bound on all adapters on the same port at the same time (not all do, ie Linux and Android do not, but Windows does). The above IP is an IPv6 address, so I'm guessing you have not created an IPv6 Binding to listen on. You can't bind an IPv4-only socket to IPv6 adapters, and vice versa.
  5. Remy Lebeau

    Issues with Sleep(1) called in a loop

    And also: https://stackoverflow.com/questions/9518106/winapi-sleep-function-call-sleeps-for-longer-than-expected
  6. Remy Lebeau

    SetLength TBytes Memory Leak

    The call stack in that log also looks a little off. System._DynArraySetLength() does not call System._LStrFromPWCharLen(), and System._LStrFromPWCharLen() does not call System.DynArraySetLength(). System._DynArraySetLength() calls System.DynArraySetLength() directly. So, even though the code shown does call System._LStrFromPWCharLen() (on the RawByteString type-cast), that call should not be on the call stack anymore when System._ReallocMem() is called.
  7. Remy Lebeau

    Timer game delphi 7

    This would be easier: seconds:=seconds mod 60;
  8. Remy Lebeau

    GMail Inbox

    Sorry, I don't know what that means. UIDs are unique within a given folder, but not necessarily across folders. Multiple folders can refer to the same message. And UIDs are persistent only so long as the folder's UIDValidity value doesn't change. If you save a UID across sessions, you have to check if the folder's UIDValidity has changed before you can use the UID again. When you select a folder with TIdIMAP4.SelectMailBox(), its current UIDValidity value is stored in the TIdIMAP4.MailBox.UIDValidity property. Refer to RFC 3501 section 2.3.1.1 for more details. TIdIMAP4.SearchMailBox() returns message numbers, not UIDs. If you want UIDs, use TIdIMAP4.UIDSearchMailBox() instead. Otherwise, you can extract a UID from the result of TIdIMAP4.RetrieveHeader(), or pass message numbers from TIdIMAP4.SearchMailBox() to TIdIMAP4.GetUID().
  9. Remy Lebeau

    GMail Inbox

    Looks like you have extra spaces surrounding the 189. Do you get the same error if you remove them? Also, I found this, might be useful: How to remove a label from an email message from Gmail by using the IMAP protocol?
  10. Remy Lebeau

    Where I can find the SSL DLLs for Indy?

    What does Indy's WhichFailedToLoad() function report after the error occurs? Are you using 1.0.2u for BOTH DLLs? Do you have other versions of OpenSSL on your system? Try using SysInternals Process Monitor to make sure your app is actually attempting to load the correct DLLs you are expecting, and not some other DLLs. It should work, yes.
  11. Remy Lebeau

    GMail Inbox

    Have you tried using \Inbox instead, as shown in GMail's IMAP Extensions documentation? IdImap4.SendCmd('UID STORE ' + rMess.UID + ' -X-GM-LABELS (\Inbox)', ['OK','BAD','NO'], true); That being said, why are you using the SendCmd() method manually? TIdIMAP4 has a UIDStoreValue() method for sending a UID STORE command: IdImap4.UIDStoreValue(rMess.UID, sdRemove, 'X-GM-LABELS', '\Inbox');
  12. Remy Lebeau

    Build managed dll in Delphi

    That is what I would suggest, too. Granted, writing PInvoke wrappers is not always the easiest task, but it does work well if you get it right. I've written several DLLs (in C++) that are used in C# via PInvoke.
  13. Remy Lebeau

    caFree & ,Free??

    I don't need to read the help on it, I already know exactly what it is and how it works, thank you. That is written simpler as (frmSecondForm <> nil). But either way, checking for nil STILL won't solve the issue, because the frmSecondForm pointer is NOT being set to nil when the TfrmSecondForm object is destroyed, so ANY check for nil after destruction will NOT work in your example. The code you have shown earlier will STILL have undefined behavior even with the nil check added: procedure TfrmFormMain.btn_Calls_SecondFormClick(Sender: TObject); begin frmFormSecond := TfrmFormSecond.Create(nil); try frmFormSecond.ShowModal; Application.ProcessMessages; // <-- object destroyed here if TfrmFormSecond.rdgrpCloseOptions.ItemIndex = 0 finally // if object is destroyed above, UB happens below because frmFormSecond is a dangling pointer! if frmFormSecond.HandleAllocated then FreeAndNil(frmFormSecond); // or if (frmFormSecond <> nil) and frmFormSecond.HandleAllocated then FreeAndNil(frmFormSecond); end; end; And before you say it, yes you could fix that specific issue by setting the global frmSecondForm pointer to nil during destruction, like in the Form's OnDestroy event, eg: procedure TfrmSecondForm.FormDestroy(Sender: TObject); begin frmSecondForm := nil; end; procedure TfrmFormMain.btn_Calls_SecondFormClick(Sender: TObject); begin frmSecondForm := TfrmFormSecond.Create(nil); try frmSecondForm.ShowModal; Application.ProcessMessages; // <-- object destroyed here if TfrmFormSecond.rdgrpCloseOptions.ItemIndex = 0 finally if frmSecondForm.HandleAllocated then // <-- if object is destroyed above, UB here because frmSecondForm is now nil! FreeAndNil(frmSecondForm); // or if (frmSecondForm <> nil) and frmSecondForm.HandleAllocated then // <-- no more UB here! FreeAndNil(frmSecondForm); end; end; But, that won't solve the issue if someone decides NOT to use the global pointer to begin with, eg: procedure TfrmFormMain.btn_Calls_SecondFormClick(Sender: TObject); var frm: TfrmFormSecond; begin frm := TfrmFormSecond.Create(nil); try frm.ShowModal; Application.ProcessMessages; // <-- object destroyed here if TfrmFormSecond.rdgrpCloseOptions.ItemIndex = 0 finally // if object is destroyed above, UB happens below because frm is a dangling pointer! if frm.HandleAllocated then FreeAndNil(frm); // or if (frm <> nil) and frm.HandleAllocated then FreeAndNil(frm); end; end; To solve that, you would have to do something radical along the lines of the following: procedure TfrmSecondForm.FormDestroy(Sender: TObject); type PTfrmSecondForm = ^TfrmSecondForm; begin PTfrmSecondForm(Tag)^ := nil; end; procedure TfrmFormMain.btn_Calls_SecondFormClick(Sender: TObject); var frm: TfrmFormSecond; begin frm := TfrmFormSecond.Create(nil); try frm.Tag := NativeInt(@frm); frm.ShowModal; Application.ProcessMessages; // <-- object destroyed here if TfrmFormSecond.rdgrpCloseOptions.ItemIndex = 0 finally if frm.HandleAllocated then // <-- if object destroyed above, UB here because frm is nil! FreeAndNil(frm); // or if (frm <> nil) and frm.HandleAllocated then // <-- no more UB here! FreeAndNil(frm); end; end; But, no matter what, using HandleAllocated() to determine whether the Form object needs to be destroyed or not is just plain WRONG. Object lifetime and window lifetime are two completely different things. If you Create() a Form (or any object), you need to Free() it when you are done using it, if that is not already being done elsewhere (by an Owner, by an OnClose event, etc). No, because I don't need to run it (and besides, I can't run it, as I don't have a working compiler installed at the moment). Just by looking at the code, my many years of experience and deep understanding of the VCL's inner workings tell me EXACTLY how this code will behave.
  14. You likely copied an incompatible version of the OpenSSL DLLs. For instance, if you copied OpenSSL 1.1.x, as TIdSSLIOHandlerSocketOpenSSL supports only up to OpenSSL 1.0.2 (use this SSLIOHandler instead for 1.1.x). Or, if you copied 32bit DLLs for a 64bit app, or vice versa. What do Indy's OpenSSLVersion() and WhichFailToLoad() functions report?
  15. Remy Lebeau

    caFree & ,Free??

    This example is ABSOLUTELY WRONG and MUST NOT be used. Yes, you can use HandleAllocated() to check if a UI control's HWND exists without creating a new HWND, but you can't use HandleAllocated() to determine if the UI object itself exists. If a Form uses Action=caFree in its OnClose event to request itself be destroyed, and the resulting CM_RELEASE message gets processed (as this example is doing), the Form object will be destroyed, and any subsequent attempt to access its members, including HandleAllocated() (as this code is doing) will be undefined behavior, and likely will result in a crash.
×