-
Content Count
2684 -
Joined
-
Last visited
-
Days Won
113
Everything posted by Remy Lebeau
-
It is perfectly safe to call Free() on a Form (or any other TComponent descendant) that has an Owner assigned. The Form/component will simply remove itself from its Owner's internal list, so that the Owner will not try to destroy it a 2nd time when the Owner is destroyed.
-
Yes, it is perfectly safe. Using Action=caFree in the Form's OnClose event will call the Form's Release() method, which is a *delayed* destruction. A CM_RELEASE message is posted to the Form's window. The Form is not actually destroyed until that message is processed at a later time. If the Form/window is destroyed before that happens, the message is discarded. Then something else in your project is freeing the Form prematurely. It is not your OnClose handler doing it. Run your code in the debugger and put a breakpoint in the Form's destructor or OnDestroy event, and look at the call stack to track down where the Form is being destroyed.
-
There is no standard multi-calendar component in the VCL, so you would have to look around for a 3rd party solution, or just use the VCL's standard TDateTimePicker component alongside the TMonthCalendar, TCalendarView, or TCalendarPicker components.
-
There is no standard format for WHOIS data. You are just going to have to check the retrieved data for multiple formats until you find one that matches. For example, I would suggest using a TStringList, eg: const FieldNames: array[0..2] of string = ('Registry Expiry Date', 'Expiry date', 'paid-till'); var sl: TStringList; I: Integer; expiry: string; begin sl := TStringList.Create; try sl.Assign(Memo1.Lines); for I := 0 to sl.Count-1 do begin sl[I] := TrimLeft(sl[I]); end; sl.NameValueSeparator := ':'; for I := Low(FieldNames) to High(FieldNames) do begin expiry := Trim(sl.Values[FieldNames[I]]); if expiry <> '' then Break; end; finally sl.Free; end; Edit3.Text := expiry; end;
-
Why are you asking this again? Why are you creating different accounts across the Internet just to ask the same question over and over? You have already been told several times before, in other forums (here and here), that the WHOIS protocol simply does not work the way you are asking for, regardless of which socket library you use to send the query. You have to retrieve the entire WHOIS data and parse it yourself to get the data you want from it, there is no other option.
-
Comunicate with POS terminal (Ingenico)
Remy Lebeau replied to boris.nihil's topic in Network, Cloud and Web
There are POS systems that use serial-like protocols over TCP. That is what I'm thinking, too. -
Comunicate with POS terminal (Ingenico)
Remy Lebeau replied to boris.nihil's topic in Network, Cloud and Web
Do you need to send an actual *hex-encoded string* for the <message> (ie, '303030...', bytes $33 $30 $33 $30 $33 $30 ...), or do you need to send actual binary bytes ($30 $30 $30 ...)? It makes a BIG difference. Do you have documentation for this POS protocol that you can share? Yes, you are using the wrong code. On the send, you should not be using WriteLn() at all, since there is no CRLF in this protocol. Use Write() instead. But whether you should use Write(string) or Write(TIdBytes) depends on what you are actually expected to send. On the receive, to use ReadLn() properly, you would need to specify ETX, #3, as the line terminator (or, you can use WaitFor(#3)), and then do a ReadByte() afterwards to read the checksum that follows the ETX. Otherwise, you would need to call ReadByte() in a loop until you encounter ETX (or, you could copy the code from ReadLn()/WaitFor() and adapt it for bytes instead of strings, not that hard). That is wrong. I have worked in the past with STX/ETX protocols and a few POS protocols, you definitely need to send the <STX>, <FS> and <ETX> as binary bytes, not as hex-encoded strings. Try something more like this: var s: string; s := #2#2'303030303030303030303031303030'#28'31303030'#28#28'2B30'#28'313931'#28#28#28#28#28#28#28#28#28#28#28'3030'#28'31'#12#28#28#28#28#28#28#28#28#3'2C'; IdTCPClient1.IOHandler.Write(s, IndyTextEncoding_8bit); Though I suspect the POS is actually expecting binary data for the message content, not hex-encoded strings, so try something more like this: var s: string; s := #2#2'000000000001000'#28'1000'#28#28'+0'#28'191'#28#28#28#28#28#28#28#28#28#28#28'00'#28'1'#12#28#28#28#28#28#28#28#28#3','; IdTCPClient1.IOHandler.Write(s, IndyTextEncoding_8bit); That is fine, if you change the reading according to what I said above. Though, if you send a request and are expecting an immediate reply, why use a timer at all? Just read the reply immediately after sending the request. -
Shutting down TidTCPServer (kbmMWTCPIPIndyServerTransport)causing Window Service timeout
Remy Lebeau replied to c0d3r's topic in Indy
Without seeing your TIdTCPServer code, there is no way to help you diagnose why your TIdTCPServer is freezing up. What you describe is a classic deadlock scenario, but there are so many different ways a deadlock could happen. -
Shutting down TidTCPServer (kbmMWTCPIPIndyServerTransport)causing Window Service timeout
Remy Lebeau replied to c0d3r's topic in Indy
That is why I keep asking you to SHOW YOUR CODE (or at least a small demo that reproduces the same issue). We can't tell you what is wrong if we can't see what you are actually doing. No. It is your responsibility to not write code that blocks the socket threads from terminating properly. -
Shutting down TidTCPServer (kbmMWTCPIPIndyServerTransport)causing Window Service timeout
Remy Lebeau replied to c0d3r's topic in Indy
Well, then that goes back to my earlier question - what do those methods actually look like? Because there is likely a deadlock happening inside one of them, preventing one or more socket threads from terminating correctly, thus causing the TIdTCPServer.Active property setter to hang waiting for it. -
Casting pointer to TBytes
Remy Lebeau replied to Jacek Laskowski's topic in RTL and Delphi Object Pascal
The only way to do that is to make the memory area MIMIC a real TBytes, by adjusting the size of the GetMem() allocation to include a fake TBytes header whose reference count is set to 1 or higher so the method doesn't try to free the allocated memory internally as it passes around and uses the TBytes. For example: type // these are defined only in the System unit's implementation, so // they need to be defined manually in your code... PDynArrayRec = ^TDynArrayRec; TDynArrayRec = packed record {$IFDEF CPUX64} _Padding: LongInt; // Make 16 byte align for payload.. {$ENDIF} RefCnt: LongInt; Length: NativeInt; end; var MemBlk: Pointer; MyByteArr: PByte; begin //GetMem(MyByteArr, DesiredSize); GetMem(MemBlk, SizeOf(TDynArrayRec) + DesiredSize); try PDynArrayRec(MemBlk).RefCnt := 1; PDynArrayRec(MemBlk).Length := DesiredSize; MyByteArr := PByte(MemBlk) + SizeOf(TDynArrayRec); // fill MyByteArr as needed ... Something(TBytes(MyByteArr)); // ... finally //FreeMem(MyByteArr); FreeMem(MemBlk); end; end; If that is not an option, and changing the method (or calling a different method) is also not possible, then your only remaining options are to either: - copy the GetMem() data to a temp TBytes, and then copy it back afterwards if needed. - change the GetMem() allocation to TBytes to begin with. -
Shutting down TidTCPServer (kbmMWTCPIPIndyServerTransport)causing Window Service timeout
Remy Lebeau replied to c0d3r's topic in Indy
You can't use TIdTCPServer without at least an OnExecute event handler, otherwise setting the Active property to true will raise an EIdTCPNoOnExecute exception. Are you suggesting that your OnExecute handler is blank, doing nothing at all? I'm not familiar with kbmMW, why are you using TIdTCPServer at all if it does nothing? I don't understand the rational here. As I said earlier, setting TIdTCPServer.Active to False will handle the disconnect of active client sockets, closing the listening sockets, and waiting for their threads to fully terminate. -
Shutting down TidTCPServer (kbmMWTCPIPIndyServerTransport)causing Window Service timeout
Remy Lebeau replied to c0d3r's topic in Indy
That usually happens if you cause a thread deadlock inside your TIdTCPServer event handlers. For instance, by syncing with the same thread that is trying to deactivate the server. The Active setter closes all open client sockets, AND waits for the owning threads to terminate. The server events are fired in the context of those threads. So, either don't perform thread-blocking syncs during server shutdown, or else do the server deactivatation in a separate thread so the syncs can be processed normally. TIdTCPServer shutdown hasn't really changed between Indy 9 and 10. Sure, the underlying architecture is a bit different, but the basics are still the same. Although, you might have been getting saved by the TIdTCPServer.TerminateWaitTime property in Indy 9, which defaulted to 5 seconds. If the server shutdown exceeded that timeout, an EIdTerminateThreadTimeout exception would be raised, maybe your service was crashing and you just didn't notice it before? That property doesn't exist in Indy 10. Can you show what your TIdTCPServer event handlers are doing? -
Indy with Lazarus on Raspberry PI using IPV6 – problem
Remy Lebeau replied to Drewsky's topic in Indy
By omitting the Binding.IP property, the server would bind to the wildcard IP ('::'), causing it to listen on all available IPv6 interfaces on the device. Are you sure that the IP address that you attempted to bind to ('fe80::b493:ca5a:af2a:a036') actually belongs to your device? If it does not, that would cause the bind error you were seeing. -
Micro optimization: String starts with substring
Remy Lebeau replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
Really? Ouch 😨 It used to be much simpler: function TStringHelper.StartsWith(const Value: string): Boolean; begin Result := StartsWith(Value, False); end; function TStringHelper.StartsWith(const Value: string; IgnoreCase: Boolean): Boolean; begin if not IgnoreCase then Result := System.SysUtils.StrLComp(PChar(Self), PChar(Value), Value.Length) = 0 else Result := System.SysUtils.StrLIComp(PChar(Self), PChar(Value), Value.Length) = 0; end; function StrLComp(const Str1, Str2: PWideChar; MaxLen: Cardinal): Integer; var I: Cardinal; P1, P2: PWideChar; begin P1 := Str1; P2 := Str2; I := 0; while I < MaxLen do begin if (P1^ <> P2^) or (P1^ = #0) then Exit(Ord(P1^) - Ord(P2^)); Inc(P1); Inc(P2); Inc(I); end; Result := 0; end; function StrLIComp(const Str1, Str2: PWideChar; MaxLen: Cardinal): Integer; var P1, P2: PWideChar; I: Cardinal; C1, C2: WideChar; begin P1 := Str1; P2 := Str2; I := 0; while I < MaxLen do begin if P1^ in ['a'..'z'] then C1 := WideChar(Word(P1^) xor $20) else C1 := P1^; if P2^ in ['a'..'z'] then C2 := WideChar(Word(P2^) xor $20) else C2 := P2^; if (C1 <> C2) or (C1 = #0) then Exit(Ord(C1) - Ord(C2)); Inc(P1); Inc(P2); Inc(I); end; Result := 0; end; Compared to StartsStr(), which (eventually) calls CompareString(): function StartsStr(const ASubText, AText: string): Boolean; begin Result := AnsiStartsStr(ASubText, AText); end; function AnsiStartsStr(const ASubText, AText: string): Boolean; begin Result := AnsiSameStr(ASubText, Copy(AText, 1, Length(ASubText))); // WHY Copy()? AnsiStrLComp() could have been used instead! end; function AnsiSameStr(const S1, S2: string): Boolean; begin Result := AnsiCompareStr(S1, S2) = 0; end; function AnsiCompareStr(const S1, S2: string): Integer; {$IFDEF MSWINDOWS} begin Result := CompareString(LOCALE_USER_DEFAULT, 0, PChar(S1), Length(S1), PChar(S2), Length(S2)) - CSTR_EQUAL; end; {$ENDIF MSWINDOWS} {$IFDEF POSIX} begin Result := UCS4CompareStr(UnicodeStringToUCS4String(S1), UnicodeStringToUCS4String(S2)); end; {$ENDIF POSIX} I don't have RTL/VCL source code past XE3, but I keep seeing people mention how bad it's getting in recent years. This is not good. -
Micro optimization: String starts with substring
Remy Lebeau replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
I find that hard to believe. But I have no way to test that myself right now. When Copy() or MidStr() are used to return a partial substring of a larger string, they dynamically allocate a new string and copy characters into it. Your tests are requesting smaller substrings. So there should be allocations being performed before any comparisons can be made. That would take more time. The only way those allocations should not be done is when the entire input string is being returned as-is, in which case the reference count for a non-constant gets incremented, and a constant gets returned as-is (no reference counting). This is even worse for StartsStr(), because it also uses Copy() internally, to chop the input string down to the size of the comparison string, and then compares the result to the comparison string. That is really unnecessary, as TStringHelper.StartsWith() proves. TStringHelper.StartsWith() simply calls SysUtils.StrL(I)Comp(), passing it pointers to the input strings, and the max length to compare. It compares the characters directly in the original memory without making any allocations at all. Which is the way it should be done. So I would have expected TStringHelper.StartsWith() to be much faster than the others, not in the middle. But then, I was looking at an older version (XE3). Maybe things have changed in recent years? Any comparison that avoids having to allocate new strings should be the fastest. -
What about requiring internal users to sign their EXEs with a company-issued certificate, and then having the DLL validate that when loaded?
-
I got bitten by an interface!
Remy Lebeau replied to Clément's topic in Algorithms, Data Structures and Class Design
You could do that, but that is not a very good design choice. You would have to remember to always update BOTH references when updating EITHER reference. That risks the two references getting out of sync. If you absolutely need access to TMyFrame members that are not accessible via interface methods (which kind of defeats the purpose of using interfaces), you can always type-cast an interface back to TMyFrame when needed (D2010+), eg: var Intf: ISomeInterface; Intf := TMyFrame.Create; ... Use (Intf as TMyFrame).SomeMember as needed... ... -
TIdTime: datetime-method is not returning in local time for Android and iOS
Remy Lebeau replied to philipp.hofmann's topic in Network, Cloud and Web
I have checked in the code as a branch on GitHub (https://github.com/IndySockets/Indy/tree/TTimeZone). Overall, there were 22 files affected by adding this new feature. -
No, FMX does not support the BMP format on Android by default. The documentation is quite clear on which formats are implemented on each platform: If you want to use BMP on Android, you can write your own class derived from TCustomBitmapCodec to handle that, and then register it with TBitmapCodecManager at runtime.
-
TIdTime: datetime-method is not returning in local time for Android and iOS
Remy Lebeau replied to philipp.hofmann's topic in Network, Cloud and Web
In IdGlobalProtocols.TimeZoneBias(), correct? I'm thinking of updating IdGlobal.OffsetFromUTC() to use TTimeZone as well. And since most platform implementations of TimeZoneBias() are just returning -OffsetFromUTC(), that means OffsetFromUTC() should return (+1 * (TTimeZone.Local.UtcOffset.TotalMinutes / 1440)), and TimeZoneBias() should return (-1 * (TTimeZone.Local.UtcOffset.TotalMinutes / 1440)), is that correct? Ideally, I would like to simplify TimeZoneBias() to just returning -OffsetFromUTC() on all platforms. I don't know why it singles out Unix on Delphi but not FreePascal. In which case, since OffsetFromUTC() and TimeZoneBias() are basically doing the same thing, just returning the inverse of each other, and all uses of TimeZoneBias() are only being used to adjust TDateTime values between local and UTC times, I'm thinking of just deprecating TimeZoneBias() and introducing a couple of new LocalTime<->UTC conversion functions in IdGlobal.pas to make things easier. Then I can make better use of TTimeZone.Local.ToUniversalTime() and TTimeZone.Local.ToLocalTime() in Delphi, and LocalTimeToUniversal() and UniversalTimeToLocal() in FreePascal. That would eliminate TimeZoneBias() completely, and leave only 2 remaining uses of OffsetFromUTC() - in IdGlobal.LocalDateTimeToGMT() and IdFTPCommon.MinutesFromGMT(). -
I got bitten by an interface!
Remy Lebeau replied to Clément's topic in Algorithms, Data Structures and Class Design
Which is perfectly fine. This is not a side effect of Supports() itself, but rather is due to your mismanagement of your interfaced object. Your TMyFrame class has reference counting enabled on it, but your fBaseClass member is not maintaining an active interface reference to the object, so the object has an initial reference count of 0 instead of 1, and then your cast to ISupportTask increments the reference count to 1 instead of 2, and then releasing that ISupportTask decrements the reference count to 0 instead of 1, freeing the object. As Dalija said, you need to change this line: fBaseFrame : TBaseFrame; To this instead: fBaseFrame : IInterface; And then this change this: procedure TForm22.FormDestroy(Sender: TObject); begin fBaseFrame.Free; end; To this instead: procedure TForm22.FormDestroy(Sender: TObject); begin fBaseFrame := nil; end; Or, simply get rid of the OnDestroy handler completely, since the fBaseFrame member will be finalized anyway then the TForm22 object is destroyed, releasing the reference for you. And, as David mentioned, you should change this: procedure TForm22.Button1Click(Sender: TObject); begin if Supports( fBaseFrameClass, ISupportTask ) then (fBaseFrame as ISupportTask).CheckTask; end; To this instead: procedure TForm22.Button1Click(Sender: TObject); var Task: ISupportTask; begin if Supports( fBaseFrame, ISupportTask, Task ) then Task.CheckTask; end; -
Oh yeah, I keep forgetting that TBitmap does have some alpha-channel support built-in.
-
You could use the library version of curl, libcurl, directly in your code. There is a Delphi language binding available for it. Or, you could simply convert the curl examples to equivalent HTTP/REST commands and then use whatever HTTP/REST client you want (ICS, Indy, TRESTClient, etc). Swagger is just a user-friendly HTML frontend for invoking REST commands. Any REST command the user can invoke on a Swagger page, an HTTP/REST client can also perform in code. Swagger is primarily meant for humans to use, not programs.
-
Because Windows doesn't have good support for 32bit alpha-channeled bitmaps to begin with. Good luck with that. What complications does PNG present to you? Are you working with VCL or FMX? The VCL's TPNGImage class is easy to use. FMX's TBitmap can be a bit difficult, depending on what you are trying to do with it. It would really help if you would explain the problem you are having exactly.