Jump to content

Remy Lebeau

Members
  • Content Count

    2337
  • Joined

  • Last visited

  • Days Won

    95

Everything posted by Remy Lebeau

  1. Remy Lebeau

    Delphi is 26 years old - Marco's blog

    And now we are drawing particles instead, my how far things have come
  2. Remy Lebeau

    [Rio][FMX]How to compile for iOS without an Apple device?

    The nice thing about this one is that it already provides support for RAD Studio, you don't have to do anything extra for that. I don't know if this one supports RAD Studio by default or not.
  3. Remy Lebeau

    TIdSSLIOHandlerSocketOpenSSL.MaxLineAction issue.

    No. GitHub provides an SVN interface. I was referring more to the fact that Indy doesn't use its own SVN server anymore now that it has migrated to GitHub: https://www.indyproject.org/2019/11/28/indy-svn-retiring-long-live-github/
  4. Remy Lebeau

    TIdSSLIOHandlerSocketOpenSSL.MaxLineAction issue.

    Instructions can be found here: https://web.archive.org/web/20200925081341/http://ww2.indyproject.org/Sockets/Docs/Indy10Installation.EN.aspx (the live page on Indy's main website is offline right now). Indy doesn't use SVN anymore.
  5. Remy Lebeau

    TIdSSLIOHandlerSocketOpenSSL.MaxLineAction issue.

    Yes. Is your project using VCL or FMX? VCL is compiled with OFF by default. FMX is compiled with ON by default. AFAIK, there is no IDE setting for Scoped Enums, they are controlled only via the {$SCOPEDENUMS} compiler directive. Should, yes. Indy's IdGlobal unit.
  6. Remy Lebeau

    TIdSSLIOHandlerSocketOpenSSL.MaxLineAction issue.

    First off, you DO NOT need to set the IOHandler's Destination, Host, and Port properties. Those are handled internally by Indy for you. As for the error, check if Scoped Enums is enabled. If so, you will have to fully-qualify the value by the enum's type name, eg: MaxLineAction := TIdMaxLineAction.maException; That being said, the MaxLineAction property defaults to maException anyway, so there is no need to set that value explicitly in this case.
  7. Undocumented intrinsic routines Undocumented Delphi routines Undocumented Delphi record alignment directive Just to name a few. I know there are other sites that document more, I just don't have the time to hunt them down.
  8. Remy Lebeau

    Create a Delphi Dll and load it with DllMain

    You already asked this on StackOverflow, and were basically told DON'T DO THIS, IT IS NOT SAFE! https://stackoverflow.com/questions/66233151/
  9. Remy Lebeau

    Comunicate with POS terminal (Ingenico)

    I already explained to you earlier why your use of the IOHandler's ReadLn() method is incorrect in this situation, and how to use ReadLn() (or WaitFor()) to read the POS's messages correctly. The ACK doesn't change what I said earlier. You would simply do an extra read with ACK, #6, as the line terminator instead. However, since the ACK is only 1 byte, the IOHandler's ReadByte() method would be more appropriate, especially if the POS can ever send a NAK instead of an ACK. You can use the IOHandler's Write(Byte) method for that. No, it is not sufficient by itself. I am not at a computer right now to write you working code, it will have to wait until tomorrow.
  10. Remy Lebeau

    caFree & ,Free??

    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.
  11. Remy Lebeau

    caFree & ,Free??

    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.
  12. Remy Lebeau

    Visual Control for selecting a date range

    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.
  13. Remy Lebeau

    ICS Whois Client

    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;
  14. Remy Lebeau

    ICS Whois Client

    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.
  15. Remy Lebeau

    Comunicate with POS terminal (Ingenico)

    There are POS systems that use serial-like protocols over TCP. That is what I'm thinking, too.
  16. Remy Lebeau

    Comunicate with POS terminal (Ingenico)

    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.
  17. 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.
  18. 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.
  19. 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.
  20. Remy Lebeau

    Casting pointer to TBytes

    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.
  21. 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.
  22. 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?
  23. Remy Lebeau

    Indy with Lazarus on Raspberry PI using IPV6 – problem

    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.
  24. 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.
  25. 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.
×