Jump to content

Remy Lebeau

Members
  • Content Count

    2684
  • Joined

  • Last visited

  • Days Won

    113

Everything posted by Remy Lebeau

  1. That is because the old Quality Portal (quality.embarcadero.com) has been replaced with a new system (qp.embarcadero.com).
  2. Remy Lebeau

    TNetHTTPRequest Unicode Mapping Issue

    Because you are explicitly giving the server permission to send compressed response, even though by default IHttpResponse DOES NOT support compressed responses. So, you are likely getting a compressed response in binary format, but IHTttpResponse does not decompress it, and then you try to convert the compressed data into a String, which fails, You need to use the TNetHTTPClient.AutomaticDecompression property to enable handling of "gzip" and "deflate" compressions. In general, DO NOT manipulate the "Accept-Encoding" header manually, unless you are prepared to decode the response manually (ie, by receiving it as a TStream and decompressing it yourself). Just because a BROWSER sends that header (and browsers do support compression) does not mean YOU should send it. TNetHTTPClient will manage the "Accept-Encoding" header for you. It will allow "gzip" and "deflate" compression if the AutomaticDecompression property enables them. Similarly, Indy's TIdHTTP does the same thing. It supports "gzip" and "deflate" compressions, and will set the "Accept-Encoding" accordingly, if you have a Compressor assigned to it.
  3. Remy Lebeau

    Converting images to JSON and Send to Server

    How so, exactly? It is just a string field, JSON doesn't care what its content is. When you get the string back, simply parse it to extract the base64 substring after the comma, and then decode that substring to get the raw bytes that you can then save to a file. IOW, just reverse the code that created the string. For example: procedure Base64ToImage(const Base64String, FilePath: string); var FileStream: TFileStream; Bytes: TBytes; begin Bytes := TNetEncoding.Base64.DecodeStringToBytes(Base64String); FileStream := TFileStream.Create(FilePath, fmCreate); try FileStream.WriteBuffer(Pointer(Bytes)^, Length(Bytes)); finally FileStream.Free; end; end; procedure LoadBase64JsonToImage; var JSONObject: TJSONObject; Base64String: string; JSONString: string; JSONFile: TStringList; begin JSONFile := TStringList.Create; try JSONFile.LoadFromFile('image_base64.json'); JSONString := JSONFile.Text; finally JSONFile.Free; end; JSONObject := TJSONObject.ParseJSONValue(JSONString) as TJSONObject; try JSONString := JSONObject.GetValue('image_data').Value; finally JSONObject.Free; end; Base64String := Copy(JSONString, Pos(',', JSONString)+1, MaxInt); Base64ToImage(Base64String, 'path/to/image.jpg'); end; You are not supposed to save the entire string to a file. Just as the string wasn't produced entirely from a file to begin with, but from a text and a file.
  4. VER360 and RTLVersion12x are specific to the D12.x series and won't be declared anymore once D13.x+ is released. So this is not future-proof.
  5. Remy Lebeau

    Update New Indy / TLS1_3 / FireUI Live

    The current version in GitHub is 10.6.3.5. It should be. Note that the Indy packages that ship with Delphi are compiled with LIBSUFFIX enabled, whereas the GitHub version is not, so if you do install the GitHub version then you will have to update your project's references to Indy packages, but other than that, everything else should be the same.
  6. Remy Lebeau

    Update New Indy / TLS1_3 / FireUI Live

    Indeed, the IdHL7.pas source file appears to be missing in D12. Delphi ships with the Indy packages preinstalled, and the TIdHL7 component was re-written and re-enabled in Indy's GitHub repo a year prior to D12's release, and it is enabled in the D12 version that is tagged in GitHub. So I would think it should be present in the Indy version that shipped with D12. But, oddly, TIdHL7 is still disabled in Embarcadero's copy of IdRegister.pas. I wonder why. To get HL7 working, you will have to remove the preinstalled version and install the latest GitHub version: https://github.com/IndySockets/Indy/wiki/Updating-Indy
  7. The Win32, Win64, and clang compilers use different debuggers. And as everything is slowly moving towards being entirely clang/LLVM, the old debuggers just don't keep up with the latest debugging info that is being generated, which causes weird side effects.
  8. Remy Lebeau

    Indy 10 android problem

    If you are asking how to know whether a given device can be connected to by your TCP client, then the only way to determine that is to actually attempt to connect to the device's TCP server and see if it succeeds or fails. At the very least, you might try simply pinging each IP to see if it is on the network, but that won't tell you whether the TCP server can be connected to. If you have access to change the device's code, then you might add a UDP server that runs alongside the TCP server, and then you can send a UDP broadcast across the network subnet and see which UDP servers respond.
  9. Remy Lebeau

    Indy 10 android problem

    I still don't understand what you are asking for, exactly.
  10. Remy Lebeau

    Indy 10 android problem

    You are going to have to elaborate on that. What are you trying to check, exactly?
  11. Remy Lebeau

    Indy 10 android problem

    The first thing I notice is that you are calling Connected() across thread boundaries, Don't do that. Connected() performs a socket read in the calling thread, so it could interfere with a read operation in another thread. You shouldn't be calling Connected() at all. Certainly not right after a successful Connect() (which will raise an exception if it fails), and not right before a Disconnect() (which will close the socket and doesn't care if the socket was connected). Another thing I notice is that you are not serializing access to your processing data. TThread.Queue() runs asynchronously, so it is possible that you might receive a new message and overwrite your variables before an earlier queued HandleData() call has been processed. I also wonder why you are creating a new Task to stop the reading thread and reconnect, but are using the main thread to start the reading thread? Your use of multiple threads is odd to me. I would suggest a different design. I would move the (re)connect logic into the reading thread itself, where the thread runs a loop that connects, reads until done, and then disconnects, repeating until stopped. If the IP needs to change, then cache the new IP and ask the thread to reconnect at its earliest convenience. That way, the thread alone decides when it is safe to read and when it is safe to disconnect and reconnect, notifying the main thread of any status changes as needed. Try something more along the lines of this: // set ReceiveThread.Loop = True // start the thread when you are ready to connect the 1st time // stop the thread when you are ready for the final disconnect public ServerHost: String; ServerHostChanged: Boolean; procedure TMainForm.ReceiveThreadRun(Sender: TIdThreadComponent); begin try if (TCPSocket.Host = '') or ServerHostChanged then begin TCPSocket.Host := ServerHost; ServerHostChanged := False; end; try TCPSocket.Connect; except on E: Exception do begin ... for var I := 1 to 5 do begin if not ReceiveThread.Active then Break; Sleep(1000); end; Exit; end; end; try ... while ReceiveThread.Active and (not ServerHostChanged) do begin try if TCPSocket.IOHandler.InputBufferIsEmpty then begin TCPSocket.IOHandler.CheckForDataOnSource(250); TCPSocket.IOHandler.CheckForDisconnect; if TCPSocket.IOHandler.InputBufferIsEmpty then Continue; end; // read a message and process as needed ... except ... Break; end; end; finally TCPSocket.Disconnect; ... end; except ... end; end; ... procedure IpSelectF.IPBtnClick(Sender: TObject); var Btn: TButton; begin Btn := Sender as TButton; if not AvailableIP[Btn.Tag] then Exit; MainF.ServerHost := '192.168.1.' + IntToStr(Btn.Tag); MainF.ServerHostChanged := True; if not MainF.ReceiveThread.Active then MainF.ReceiveThread.Start; end;
  12. On Windows, you can use the Network List Manager API, specifically the INetworkListManager.isConnectedToInternet property. However, as there are so many different ways that a PC can be connected to the Internet nowadays, you are better off simply trying to access an online resource (such as your status file) and see if it succeeds or fails, don't even bother polling the Internet status beforehand. That just introduces a TOCTOU race condition.
  13. Remy Lebeau

    TNetHTTPRequest Logon to Download

    You don't need to use an external sniffer with TIdHTTP, as it can log its own HTTP traffic directly. Simply assign a TIdConnectionIntercept-derived component to the TIdHTTP.Intercept property, such as one of the TIdLog... components. I don't know if TNetHTTPClient has any logging capabilities, though (probably not).
  14. Remy Lebeau

    TNetHTTPRequest Logon to Download

    Why PUT and not POST? Browsers don't use PUT for HTML-based webforms. Have you tried sniffing the HTTP traffic to see if there are significant differences between TIdHTTP's requests and TNetHTTPClient's requests? Does the logon use cookies? Do you have cookies enabled in the TNetHTTPClient?
  15. Remy Lebeau

    Memory leak on TParallel.For

    I wonder if this is a false positive? The log shows a thread pool is involved. Makes me wonder if the pool threads are simply still running when the leak report is generated. You have to make sure everything is shutdown first before the memory manager is cleaned up.
  16. Remy Lebeau

    Datafaction = DaAbort still shows the error message?

    Your rollback and cancel calls are backwards. Since the transaction was started first, it should be rolled back last. Cancel the edit before you rollback. Consider using a 2nd try..except block to handle this correctly. You are also not breaking the loop if the transaction is successful. Try this instead: retry := 0; repeat starttransaction; try table1.edit; try change table1; table1.post; except table1.cancel; re-raise; end; commit; Break; except Rollback; inc(retry); if retry >= 3 then showmessage & exit else sleep(500); end; until False;
  17. In the dproj file, locate the <Platforms> element and make sure it has a <Platform> child element for each target platform, and that each platform is set to a value of True. <Platforms> <Platform value="Win32">True</Platform> <Platform value="Win64">True</Platform> ... </Platforms> Sorry, I'm not at a computer right now, so I can't lookup the exact names of the other <Platform> elements. UPDATE: here are some other platform names: iOSDevice32 iOSDevice64 iOSSimulator iOSSimARM64 OSX32 OSX64 OSXARM64 Android Android64 Linux64
  18. Remy Lebeau

    VCL Componenet badge button

    What is a "VCL Component badge button" supposed to be? Please be more specific.
  19. Remy Lebeau

    On Posterror real error ;

    The SysUtils.Exception class does not have an ErrorCode property. But some descendants do, like EUpdateError, EDBClient, etc. What is the actual ClassName() of the caught Exception?
  20. I'm aware of that. You seem to have missed the point I was trying to make regarding rgdawson's earlier comment about the order of initialization/finalization. Default() creates a NEW INSTANCE, which is then assigned to the EXISTING instance, ie this code: procedure Main; var r: TMyRec; begin r := Default(TMyRec); end; SHOULD BE roughly equivalent to this: procedure Main; var r: TMyRec; // allocation only begin TMyRec.Initialize(r); try var temp: TMyRec; // allocation only TMyRec.Initialize(temp); try TMyRec.Assign(r, temp); finally TMyRec.Finalize(temp); end; finally TMyRec.Finalize(r); end; end; But what it is actually doing this more like this: procedure Main; var r: TMyRec; // allocation only begin TMyRec.Initialize(r); try var temp: TMyRec; // allocation only TMyRec.Initialize(temp); try TMyRec.Initialize(temp); // <-- SHOULD NOT BE HERE! TMyRec.Assign(r, temp); finally TMyRec.Finalize(temp); end; finally TMyRec.Finalize(r); end; end; Yes, because Default() is not being called. Exactly. r is initialized, then a temporary record is created and initialized TWICE (that's the bug), then the temp is assigned to r, then the temp and r are finalized. Here is the complete code I tested with, which makes it clearer which record is being initialized and finalized. You can clearly see the temporary record created by Default() is initialized twice: program CustomManagedRecordDefaultTest; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, System.Classes; type Stringlist = record private FData: TStringList; function GetText: string; procedure SetText(const AValue: string); public class operator Initialize(out Dest: Stringlist); class operator Finalize(var Dest: Stringlist); class operator Assign(var Dest: Stringlist; const[ref] Source: Stringlist); property Text: string read GetText write SetText; end; class operator Stringlist.Initialize(out Dest: Stringlist); begin Dest.FData := TStringList.Create; WriteLn(IntToHex(IntPtr(@Dest)) + ' Initialized'); end; class operator Stringlist.Finalize(var Dest: Stringlist); begin Dest.FData.Free; WriteLn(IntToHex(IntPtr(@Dest)) + ' Finalized'); end; class operator Stringlist.Assign(var Dest: Stringlist; const[ref] Source: Stringlist); begin Dest.FData.Assign(Source.FData); WriteLn(IntToHex(IntPtr(@Source)) + ' Assigned to ' + IntToHex(IntPtr(@Dest))); end; function Stringlist.GetText: string; begin Result := FData.Text; end; procedure Stringlist.SetText(const AValue: string); begin FData.Text := AValue; end; function AllCaps(const Strings: Stringlist): Stringlist; var sl: Stringlist; begin WriteLn(' Strings=', IntToHex(IntPtr(@Strings)), ' sl=', IntToHex(IntPtr(@sl)), ' Result=', IntToHex(IntPtr(@Result))); WriteLn(' Calling: sl := Default(Stringlist)'); sl := Default(Stringlist); WriteLn(' Default Returned, sl Assigned'); sl.Text := UpperCase(sl.Text); WriteLn(' Returning sl'); Result := sl; end; procedure DoTest; var MyStrings, TheirStrings: Stringlist; begin WriteLn(' MyStrings=', IntToHex(IntPtr(@MyStrings)), ' TheirStrings=', IntToHex(IntPtr(@TheirStrings))); WriteLn(' Calling: MyStrings := AllCaps(TheirStrings)'); MyStrings := AllCaps(TheirStrings); WriteLn(' AllCaps Returned, MyStrings Assigned'); end; begin WriteLn('Calling: DoTest'); DoTest; WriteLn('DoTest Returned'); ReadLn; end. Calling: DoTest 008FFD0C Initialized 008FFD08 Initialized MyStrings=008FFD0C TheirStrings=008FFD08 Calling: MyStrings := AllCaps(TheirStrings) 008FFCC8 Initialized Strings=008FFD08 sl=008FFCC8 Result=008FFD0C Calling: sl := Default(Stringlist) 008FFCC4 Initialized // <-- TEMP CREATED AND INIT HERE 008FFCC4 Initialized // <-- TEMP INIT AGAIN! 008FFCC4 Assigned to 008FFCC8 008FFCC4 Finalized // <-- TEMP DESTROYED HERE Default Returned, sl Assigned Returning sl 008FFCC8 Assigned to 008FFD0C 008FFCC8 Finalized AllCaps Returned, MyStrings Assigned 008FFD08 Finalized 008FFD0C Finalized DoTest Returned
  21. Remy Lebeau

    Undeclared identifier: '_CONTEXT'

    Yes, exactly, for example: uses ... {$IFDEF MSWINDOWS}, Winapi.Windows{$ENDIF} {$IFDEF ANDROID}, Androidapi.SomeOtherUnit{$ENDIF} ... ;
  22. Remy Lebeau

    Undeclared identifier: '_CONTEXT'

    The Winapi.Windows unit is for Windows builds only. If the component is trying to use that unit in an Android build, then its code is not IFDEF'ed properly to separate any platform-specific code, and thus needs to be fixed accordingly.
  23. Remy Lebeau

    Undeclared identifier: '_CONTEXT'

    Why are you re-compiling Winapi.Windows.pas? You should not be doing that. In any case, Winapi.Windows.pas declares the _CONTEXT record only if the CPUX64 or CPUX86 conditional is defined. Perhaps they are not defined in your situation?
  24. Maybe I'm just having a brain fart, but why would a new record instance need to be FINALIZED before it is INITIALIZED? The System.Default documentation even states: Finalizing implies destruction, not creation. And if a record contains compiler-managed types (interfaces, strings, dynarrays, etc), finalizing it before initializing it would invoke refcounting semantics on things that aren't valid yet. What am I missing here?
  25. As I stated earlier, PChar(S[Length(S)]) is extracting the last single Char from the string and type-casting its value into a PChar pointer, which is wrong. You need to use the @ operator to get the address of that Char. But in any case, using S[Length(S)] doesn't take into account that a string contains encoded codeunits, so will NOT be the last full character if the character is encoded using multiple codeunits. CharPrev() takes the encoding into account. The code is getting a pointer to the null terminator and then moving the pointer backwards 1 full character regardless of how how many codeunits it actually takes.
×