Jump to content

Remy Lebeau

Members
  • Content Count

    3060
  • Joined

  • Last visited

  • Days Won

    139

Everything posted by Remy Lebeau

  1. 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;
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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.
  7. 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.
  8. 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.
  9. 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.
  10. 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?
  11. 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.
  12. 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.
  13. 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.
  14. Remy Lebeau

    Uniqueness and Security of domain name

    What about requiring internal users to sign their EXEs with a company-issued certificate, and then having the DLL validate that when loaded?
  15. 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... ...
  16. 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.
  17. Remy Lebeau

    Is there codec for FMX, Android Bitmap ?

    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.
  18. 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().
  19. 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;
  20. Remy Lebeau

    32bit bitmap

    Oh yeah, I keep forgetting that TBitmap does have some alpha-channel support built-in.
  21. Remy Lebeau

    working with new APIs

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

    32bit bitmap

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

    TaskMessageDlg('.... behind form?

    Yes - if the dialog was not being assigned that Form's HWND as its owner window (in Win32 API terms, not VCL terms). A dialog can never appear behind its owner window, it stays on top of the owner. By default, VCL dialog components typically use the HWND of the currently active TForm that has focus, otherwise they may use the MainForm's HWND, or even the TApplication's hidden HWND. So, if the owner of the dialog is not the Form you are expecting, that would cause the symptom you describe. The main thing to know about TaskMessageDlg() is that it is just a wrapper. Internally, it performs a few system checks to decide whether to use the Win32 TaskDialogIndirect() function vs the VCL's own TMessageForm class. So, for instance, if you wanted to work around this issue, and you knew your app was always running on systems that support TaskDialogs, you could use the VCL's TTaskDialog component (or even TaskDialogIndirect() directly), explicitly passing it your intended Form's HWND as the dialog owner, eg: procedure TMyForm.DoSomething; begin // TaskMessageDlg(...); TaskDialog1.Title := ...; TaskDialog1.Text := ...; ... TaskDialog1.Execute(Self.Handle); end; procedure TMyForm.DoSomething; var config: TASKDIALOGCONFIG; begin // TaskMessageDlg(...); config.cbSize := sizeof(config); config.hwndParent := Self.Handle; config.pszWindowTitle := ...; config.pszMainInstruction := ...; config.pszContent := ...; ... TaskDialogIndirect(config, ...); end;
  24. Remy Lebeau

    Install recent Delphi versions on Windows XP

    Until recent years, I did all of my Delphi development work (for Indy, for beta testings, etc) solely in XP VMs. It wasn't until Embarcadero forced newer Delphi installers to fail on XP that I had to start using Win7 in new VMs. Even my day-job work (well, until my main projects reached EOL last month) included a physical XP box and an XP VM as my sole environment for all of my C++Builder work. XP is a fairly small, easily-installed, very stable OS to develop on, when you don't have to target new OS features (and even then, I still targeted new OS features while writing code on XP). And if an app runs good on XP, it is likely to run good on modern Windows, too.
  25. Remy Lebeau

    Hashing Street Addresses ?

    I can tell you from personal experience that parsing US addresses is not easy or fun. I have an application I worked on, which includes a feature to gather real-time address data from various PBX systems and then standardize the components of those addresses into a database table, similar to what you have shown above. That project has over 150 parsers for all kinds of different ways that PBXs format their address data. And when a customer happens to identify a new PBX/format that is not covered by the existing parsers, it usually takes me about 3 days to identify the new format (which is rarely documented), write a parser for it, and test it against not only the formatted data that is expected, but also all kinds of corner-case formatting variations that inevitably crop up in production. Because people can't seem to input similar addresses with any amount of reliable consistency. Or even stupid things, like putting the Town name in the Address1/2 field, or the County in the Town field, or even the whole address as a single text in the Address1 field. It happens.
×