-
Content Count
2985 -
Joined
-
Last visited
-
Days Won
134
Everything posted by Remy Lebeau
-
Find exception location from MAP file?
Remy Lebeau replied to A.M. Hoornweg's topic in RTL and Delphi Object Pascal
The *preferred* base addresses are in the map file, as well as in exe/dll PE headers, but it is not guaranteed that exe/dll modules will actually use those base addresses at runtime (typically, they will, but the OS is free to move them somewhere else). You can use the Win32 GetModuleHandle() function, or the RTL's SysInti.HInstance, to get the actual base address at runtime. -
Is it possible to dynamically generate an editor GUI based on a data type class?
Remy Lebeau replied to wuwuxin's topic in VCL
There are 3rd party Object Inspectors available, if you search around. Or, you could write your own editor using the VCL's TValueListEditor component and some manual RTTI logic. -
How can i retrive components from a dat file;
Remy Lebeau replied to azrael_11's topic in RTL and Delphi Object Pascal
That article is written for saving a single component to a file, not for saving multiple components. I would suggest saving the TForm itself rather than each of its child components individually. -
How to "correctly" pass an array to a C function
Remy Lebeau replied to wuwuxin's topic in RTL and Delphi Object Pascal
Why? The C code clearly names the parameter "num_elements", so it stands to reason that it expects an "element count", not a "byte count". A dynamic array is already a pointer, so you don't need to index into the array, PInteger(data) will suffice. -
No. IFDEF your code to call the relevant OS APIs directly (SHBrowseForFolder()/IFileOpenDialog on Windows, etc). Or else just make your own dialog/Form that iterates the filesystem (ie, via the System.IOUtils.TDirectory class) and display the contents as needed.
-
What is the best way to precisely time/trigger threaded events
Remy Lebeau replied to Yaron's topic in Windows API
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. -
TFileStream fmShare modes
Remy Lebeau replied to Mark Williams's topic in Algorithms, Data Structures and Class Design
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. -
TIdUDPServer listening to all local addresses
Remy Lebeau replied to caymon's topic in Network, Cloud and Web
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 -
TIdUDPServer to listen to all addresses of a system
Remy Lebeau replied to caymon's topic in Network, Cloud and Web
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. -
And also: https://stackoverflow.com/questions/9518106/winapi-sleep-function-call-sleeps-for-longer-than-expected
-
SetLength TBytes Memory Leak
Remy Lebeau replied to Hafedh TRIMECHE's topic in RTL and Delphi Object Pascal
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. -
This would be easier: seconds:=seconds mod 60;
-
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().
-
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?
-
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.
-
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');
-
Build managed dll in Delphi
Remy Lebeau replied to BastiFantasti's topic in RTL and Delphi Object Pascal
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. -
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.
-
How to Convert curl command line to Delphi code using IndyHttp or THttpClient.
Remy Lebeau replied to amit's topic in Network, Cloud and Web
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? -
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.
-
This is only dangerous if the code is using BOTH Action=caFree with Free() together, which makes no sense to do. Use only one or the other, not both.
-
Delphi 10.4+ uses traditional object lifetime management on all platforms (no more ARC on Android/iOS). So, any object you Create(), that does not have an Owner assigned, must be Free()'d.
-
Twsocket udp how to get the exact buffersize that received ? ?
Remy Lebeau replied to Skullcode's topic in VCL
Those other libraries are likely either reading the UDP data for you and then giving you each datagram's actual data in the event, or they have peeked the socket to determine the available bytes and then telling you that size, ICS doesn't do either of those, it is just notifying you that datagrams have arrived, but is not doing anything to gleam information about them to present to you. For comparison, Indy's TIdUDPClient (which is not event-driven) requires you to provide it with a pre-allocated buffer for it to receive bytes into. But TIdUDPServer (which is event driven) will read each datagram into an internal buffer and then fire an event giving you the actual bytes that were read into that buffer. So, there is room for different mentalities, depending on your needs. -
Unicode string - how element iterating?
Remy Lebeau replied to vfbb's topic in RTL and Delphi Object Pascal
Not really, but we did have multi-byte and multi-codepoint character sequences, which MSDN claims CharNextA() and CharNextW() do handle. But not well enough, in this case. As that article describes, you need .NET 5, which was just released 4 1/2 months ago, to handle Grapheme clusters correctly in things like enumeration, etc. -
Unicode string - how element iterating?
Remy Lebeau replied to vfbb's topic in RTL and Delphi Object Pascal
There is no such thing as a "normal graphic character" in Unicode. What you are thinking of as a "character" is officially referred to as a "grapheme", which consists of 1 or more Unicode codepoints linked together to make up 1 human-readable glyph. Individual Unicode codepoints are encoded as 1 or 2 codeunits in UTF-16, which is what each 2-byte Char represent. When a codepoint is encoded into 2 UTF-16 codeunits, that is also known as a "surrogate pair". That emoji is 1 Unicode codepoint: U+1F64F (Folded Hands) Which is encoded as 2 codeunits in UTF-16: D83D DE4F That will only allow you to determine the value of the 1st Unicode codepoint in the grapheme. But then you need to look at and decode subsequent codepoints to determine if they "combine" in sequence with that 1st codepoint. That emoji takes up 8 bytes, consisting of 4 UTF-16 codeunits: D83D DE4F DB3C DFFB Which decode as 2 Unicode codepoints: U+1F64F (Folded Hands) U+1F3FB (Emoji Modifier Type-1-2) And it gets even more complicated especially for Emoji, because 1) modern Emoji support skin tones and genders, which are handled using 1+ modifier codepoints, and 2) multiple unrelated Emoji can be grouped together with codepoint U+200D to create even new Emoji. For example: U+1F469 (Woman) U+1F3FD (Emoji Modifier Type-4) U+200D (Zero Width Joiner) U+1F4BB (Personal Computer) Which is treated as 1 single Emoji of a light-skined woman sitting behind a PC. Or, how about: U+1F468 (Man) U+200D (ZWJ) U+1F469 (Woman) U+200D (ZWJ) U+1F467 (Girl) U+200D (ZWJ) U+1F466 (Boy) Which is treated as 1 single Emoji of a Family with a dad, mom, and 2 children. See https://eng.getwisdom.io/emoji-modifiers-and-sequence-combinations/ for more details. Delphi itself only concerns itself with the encoding/decoding of UTF-16 itself (especially when converting that data to other encodings, like ANSI, UTF-8, etc). Delphi does not care what the UTF-16 data represents. Graphemes are handled only by application code that needs to be do text processing, glyph rendering, etc. Things that are outside of Delphi's scope as a general programming language. Most of the time, you should just let the OS deal with them. Unless you are writing your own engines that need to be Grapheme-aware. By using a Unicode library that understands the rules of Graphemes, Emojis, etc. Delphi has no such library built-in, but there are several 3rd party Unicode libraries that do understand those things.