Jump to content

Remy Lebeau

Members
  • Content Count

    2684
  • Joined

  • Last visited

  • Days Won

    113

Everything posted by Remy Lebeau

  1. Remy Lebeau

    Indy problem with TIdTCPClient and IPv6

    "No address associated with hostname" is a system error, not an Indy error. Specifically, that error message comes from the gai_strerror() function when Indy calls the getaddrinfo() function and it returns an EAI_NODATA error code, meaning the hostname was found but did not map to an IP address. System networking configurations are outside the scope of Indy. I'm assuming you have the INTERNET permission enabled for your Android app, yes?
  2. Remy Lebeau

    THTTPCommandType - PATCH

    I imagine it is because PATCH is not in the official HTTP protocol RFCs, it is a non-official extension. But, since TIdHTTP supports PATCH, it makes sense that TIdHTTPServer should allow it, too. So I will consider adding it (https://github.com/IndySockets/Indy/issues/395). That being said, you can alternatively use the TIdHTTPRequestInfo.Command property instead, which is the original verb string before it is translated to the TIdHTTPRequestInfo.CommandType property. And because of that, I wonder if it would be better to just deprecate the TIdHTTPRequestInfo.CommandType property altogether? Because there is no official differentiation between them, so it is open to interpretation by implementations.
  3. Remy Lebeau

    Application Error

    The TIdMessage.DeleteTempFiles property was present in Indy 9. It initialized the TIdAttachment.DeleteTempFile property when parsing attachments. The TIdMessage.DeleteTempFiles property was removed in Indy 10, and the TIdAttachment.DeleteTempFile property was moved to TIdAttachmentFile and renamed to FileIsTempFile.
  4. Remy Lebeau

    Community edition form designer

    Yup. The Floating Designer was completely removed in 10.4.1: https://blogs.embarcadero.com/new-for-the-ide-in-rad-studio-10-4-1/
  5. IIRC, your application manifest needs to define an Intent Filter for each file extension/mime type you want to handle: https://developer.android.com/guide/components/intents-filters https://stackoverflow.com/questions/1733195/android-intent-filter-for-a-particular-file-extension Inside your code, you can use TAndroidHelper.Activity.getIntent() to get the JIntent that started your process.
  6. Remy Lebeau

    How does FMX check the printer status

    Please elaborate. What code did you try, and what error(s) did you get?
  7. That error comes from the VCL TImage.Canvas property getter, when the TImage.Picture.Graphic is holding a TGraphic object other than a TBitmap. The TImage.Canvas property is simply just a shortcut to the held TBitmap.Canvas. So you can't use it to draw/modify a non-bitmap graphic.
  8. Remy Lebeau

    how to respond on user drag of a column border in a listview

    Off the top of my head, something like this: private OldListViewWndProc: TWndMethod; procedure MyListViewWndProc(var Message: TMessage); ... uses ..., Winapi.CommCtrl, Winapi.Messages; procedure TMyForm.FormCreate(Sender: TObject); begin OldListViewWndProc := ListView1.WindowProc; ListView1.WindowProc := MyListViewWndProc; end; procedure TMyForm.MyListViewWndProc(var Message: TMessage); var pnmhdr: PHDNotifyW; begin if Message.Msg = WM_NOTIFY then begin case TWMNotify(Message).NMHdr^.code of HDN_BEGINTRACKW, HDN_ENDTRACKW, HDN_TRACKW: pnmhdr := PHDNotifyW(TWMNotify(Message).NMHdr); // use pnmhdr^ fields as needed: // - pnmhdr^.Item is the index of the column being tracked // - pnmhdr^.Button is the mouse button generating the message: 0=left, 1=right, 2=middle // - pnmhdr^.PItem is a pointer to a THDItemW describing the changes to the tracked column // // to block changes, set Message.Result := 1 and Exit here... end; end; OldListViewWndProc(Message); end;
  9. Remy Lebeau

    TJsonTextReader - extract object

    Using TJSONTextReader, you can parse the document like this: uses ..., System.Generics.Collections, System.JSON.Types, System.JSON.Readers; type TColumnNode = record Column: string; Caption: string; Width: string; end; TSearchNode = record Column: string; Rules: string; end; TColorNode = record Column: string; Rules: string; end; TReadState = ( None, InTopObject, InColumnsProp, InColumnsArray, InColumnObject, InSearchProp, InSearchArray, InSearchObject, InColorsProp, InColorsArray, InColorObject ); var JSON: string; Columns: TList<TColumnNode>; Search: TList<TSearchNode>; Colors: TList<TColorNode>; ColumnNode: TColumnNode; SearchNode: TSearchNode; ColorNode: TColorNode; TextReader: TTextReader; JSONReader: TJSONTextReader; State: TReadState; PropertyName: string; begin Columns := nil; Search := nil; Colors := nil; JSON := '...'; try Columns := TList<TColumnNode>.Create; Search := TList<TSearchNode>.Create; Colors := TList<TColorNode>.Create; TextReader := TStringReader.Create(JSON); try JSONReader := TJSONTextReader.Create(TextReader); try State := TReadState.None; while JSONReader.Read do begin case JSONReader.TokenType of TJsonToken.StartObject: begin case State of TReadState.None: State := TReadState.InTopObject; TReadState.InColumnsArray: begin ColumnNode := Default(TColumnNode); State := TReadState.InColumnObject; end; TReadState.InSearchArray: begin SearchNode := Default(TSearchNode); State := TReadState.InSearchObject; end; TReadState.InColorsArray: begin ColorNode := Default(TColorNode); State := TReadState.InColorObject; end; end; end; TJsonToken.EndObject: begin case State of TReadState.InTopObject: State := TReadState.None; TReadState.InColumnObject: begin Columns.Add(ColumnNode); State := TReadState.InColumnsArray; end; TReadState.InSearchObject: begin Search.Add(SearchNode); State := TReadState.InSearchArray; end; TReadState.InColorObject: begin Colors.Add(ColorNode); State := TReadState.InColorsArray; end; end; end; TJsonToken.StartArray: begin case State of TReadState.InColumnsProp: State := TReadState.InColumnsArray; TReadState.InSearchProp: State := TReadState.InSearchArray; TReadState.InSearchProp: State := TReadState.InColorsArray; end; end; TJsonToken.EndArray: begin case State of TReadState.InColumnsArray, TReadState.InSearchArray, TReadState.InColorsArray: State := TReadState.InTopObject; end; end; TJsonToken.PropertyName: begin PropertyName := JSONReader.Value.AsString; case State of TReadState.InTopObject: begin if PropertyName = 'columns' then State := TReadState.InColumnsProp end else if PropertyName = 'search' then State := TReadState.InSearchProp end else if PropertyName = 'colors' then State := TReadState.InColorsProp else JSONReader.Skip; end; TReadState.InColumnObject: begin if PropertyName = 'column' then CurrColumn.Column := JSONReader.ReadAsString else if PropertyName = 'caption' then CurrColumn.Caption := JSONReader.ReadAsString else if PropertyName = 'width' then CurrColumn.Width := JSONReader.ReadAsString else JSONReader.Skip; end; TReadState.InSearchObject: begin if PropertyName = 'column' then CurrSearch.Column := JSONReader.ReadAsString else if PropertyName = 'rules' then CurrSearch.Rules := JSONReader.ReadAsString else JSONReader.Skip; end; TReadState.InColorObject: begin if PropertyName = 'column' then CurrColor.Column := JSONReader.ReadAsString else if PropertyName = 'rules' then CurrColor.Rules := JSONReader.ReadAsString else JSONReader.Skip; end; else JSONReader.Skip; end; end; end; end; finally JSONReader.Free; end; finally TextReader.Free; end; // use Columns, Search, and Colors as needed... finally Columns.Free; Search.Free; Colors.Free; end; end; Obviously, take out whatever pieces you don't need. But needless to say, using a Reader can keep memory usage to a minimum, but at the cost of complicating the parsing logic. If your document is not very large, you might consider using a little extra memory in order to utilize the DOM-based framework instead, eg: uses ..., System.Generics.Collections, System.JSON; type TColumnNode = record Column: string; Caption: string; Width: string; end; TSearchNode = record Column: string; Rules: string; end; TColorNode = record Column: string; Rules: string; end; var JSON: string; Columns: TList<TColumnNode>; Search: TList<TSearchNode>; Colors: TList<TColorNode>; ColumnNode: TColumnNode; SearchNode: TSearchNode; ColorNode: TColorNode; JSONValue: TJSONValue; JSONObject: TJSONObject; JSONArray: TJSONArray; ArrElement: TJSONValue; begin Columns := nil; Search := nil; Colors := nil; JSON := '...'; try Columns := TList<TColumnNode>.Create; Search := TList<TSearchNode>.Create; Colors := TList<TColorNode>.Create; JSONValue := TJSONObject.ParseJSONValue(JSON); if JSONValue <> nil then try JSONObject := JSONValue as TJSONObject; JSONArray := JSONObject.GetValue('columns') as TJSONArray; for ArrElement in JSONArray do begin with ArrElement as JSONObject do begin ColumnNode.Column := GetValue('column').Value; ColumnNode.Caption := GetValue('caption').Value; ColumnNode.Width := GetValue('width').Value; Columns.Add(ColumnNode); end; end; JSONArray := JSONObject.GetValue('search') as TJSONArray; for ArrElement in JSONArray do begin with ArrElement as JSONObject do begin SearchNode.Column := GetValue('column').Value; SearchNode.Rules := GetValue('rules').Value; Search.Add(SearchNode); end; end; JSONArray := JSONObject.GetValue('colors') as TJSONArray; for ArrElement in JSONArray do begin with ArrElement as JSONObject do begin ColorNode.Column := GetValue('column').Value; ColorNode.Rules := GetValue('rules').Value; Colors.Add(ColorNode); end; end; finally JSONValue.Free; end; // use Columns, Search, and Colors as needed... finally Columns.Free; Search.Free; Colors.Free; end; end;
  10. Remy Lebeau

    How does FMX check the printer status

    Not really sure I understand the question. The only GetPrinter() I see in the FMX.Printer unit is the getter method for the TPrinter.Printers[] property. Are you looking for an FMX equivalent of the VCL TPrinter.GetPrinter() method, which outputs device info and a Handle to the currently active Printer? On Windows, the FMX.Printer.Win.TPrinterWin class has such a method. What status are you looking for exactly? Please clarify.
  11. Remy Lebeau

    Reading a binary file hangup without error

    I see several issues with that code: 1. your SetLength() is useless, because the subsequent ReadBytes() is completely replacing your allocated array with a different array allocates by ReadBytes(). 2. your SetString() is wrong, for two reasons: a. String is UnicodeString in D2009+, which you are clearly using due to TEncoding, which was also introduced in D2009. You are trying to populate the UnicodeString with AnsiChar data, but there is no overload of SetString() for that. You can only populate a UnicodeString with WideChar data. For AnsiChar data, you need to use AnsiString instead. b. You are advancing a PAnsiChar pointer through the byte array, but you are giving the array's whole length to SetString(), which means SetString() is going to read beyond the end of the array into surrounding memory after the 1st loop iteration. Since you are trying to convert each Byte into an (Ansi)String, you would need to use 1 for the SetString() length instead. 3. appending strings to the RichEdit1.Text property in a loop is VERY inefficient. It requires reading the RichEdit's entire content into a String in memory, then expanding that String in memory with appended data, then replacing the RichEdit's entire content with the new string. A much more efficient way to append strings to a RichEdit's content is to use its SelText property instead, eg: RichEdit1.SelStart := RichEdit1.GetTextLen; RichEdit1.SelLength := 0; RichEdit1.SelText := ...; But even that is not the best approach in this situation. Since you are trying to display a byte array as-is in the RichEdit as text, you may as well just assign the RichEdit.Text property 1 time with the entire array as 1 string, no loop needed at all, eg: var AFile: TFileStream; BR: TBinaryReader; MyByteArray: TBytes; mstr: AnsiString; begin AFile := TFileStream.Create(filename, fmOpenRead); BR := TBinaryReader.Create(AFile, TEncoding.Unicode, false); try // Get the whole file contents into the byte array MyByteArray := BR.ReadBytes(AFile.Size); SetString(mstr, PAnsiChar(@MyByteArray[0]), Length(MyByteArray)); RichEdit1.Text := mstr; // or: RichEdit1.Text := TEncoding.ANSI.GetString(MyByteArray); BR.Close; finally BR.Free; AFile.Free; end; end; In which case, you may as well just get rid of the TBinaryReader, as you can read bytes from the TFileStream directly: var AFile: TFileStream; MyByteArray: TBytes; mstr: AnsiString; begin AFile := TFileStream.Create(filename, fmOpenRead); try SetLength(MyByteArray, AFile.Size); // Get the whole file contents into the byte array AFile.ReadBuffer(MyByteArray[0], SetLength(MyByteArray)); SetString(mstr, PAnsiChar(@MyByteArray[0]), Length(MyByteArray)); RichEdit1.Text := mstr; // or: RichEdit1.Text := TEncoding.ANSI.GetString(MyByteArray); finally AFile.Free; end; end; In which case, you can just get rid of the TFileStream altogether, too: RichEdit1.Text := TFile.ReadAllText(filename, TEncoding.ANSI); Or RichEdit1.Lines.LoadFromFile(filename, TEncoding.ANSI);
  12. Remy Lebeau

    TNetHTTPRequest request_id

    For what, exactly? There is no request_id in the HTTP protocol. Perhaps you are working with a particular REST protocol instead that defines a request_id in its data, outside of HTTP?
  13. What kind of error? I've concatenated PNGs together before, but that was a long time ago, and IIRC I had to resort to a 3rd party PNG library to accomplish it (TPNGImage wasn't yet available in the VCL at the time). Copying the source PNGs to a temp bitmap, and then converting that bitmap back to a PNG, just didn't have the correct result for me (maybe it screwed up the alpha channels, I don't remember). So I had to drop down a layer and work with the raw PNG pixel data directly. The resulting PNGs were clean, but the effort was not.
  14. That is FMX code, but this question was posted in a VCL forum.
  15. Remy Lebeau

    Create an animated gif from a set of bitmaps?

    TGIFImage is a VCL class, AFAIK it is not available in FMX. Also, as I don't use FMX myself, my claim about its lack of support for animated GIFs is mainly based on the below post, which presents some 3rd party solutions for displaying animated GIFs in an FMX UI: https://stackoverflow.com/questions/45285599/how-to-use-animated-gif-in-firemonkey For displaying a series of bitmaps in a UI, one can use TBitmapListAnimation, but I don't know if that can also be used to create a GIF. Embarcadero's documentation only mentions that FMX's TBitmap supports GIF on all platforms except iOS, but doesn't mention what kinds of GIFs are supported.
  16. Remy Lebeau

    Create an animated gif from a set of bitmaps?

    AFAIK FireMonkey doesn't support animated GIFs. Use an 3rd party image library that does.
  17. Remy Lebeau

    AnsiString oddities

    Honestly, I have yet to try any of the Clang compilers at all, and while I would love to be able to use newer C++ language features in my code, this issue just reaffirms why I have no desire to EVER use the Clang compilers. They are just not supported properly in the IDE or the libraries. Too many problems that are "solved" by using the classic compiler instead.
  18. Remy Lebeau

    Opinions about Pascal vs C/C++ IDE

    There is no RAD Studio Community edition. There is a Delphi Community edition, and a C++Builder Community edition. They are separate products, and cannot be installed together. RAD Studio is the commercial bundle that includes both Delphi and C++Builder as a single product.
  19. Remy Lebeau

    Where I can find all implemented services documentation

    Perhaps you are looking for this? https://docwiki.embarcadero.com/RADStudio/en/FireMonkey_Platform_Services
  20. Remy Lebeau

    Using Resource in Email

    Ah. I see it now. I wonder why that message didn't show up on my feed earlier? Oh well.
  21. Remy Lebeau

    Embed Signature Resource in email

    Addressed in this discussion thread.
  22. Remy Lebeau

    Sending Email via GMail Using OAuth 2.0 via Indy

    If you use the TIdMessageBuilder... classes, such as TIdMessageBuilderPlain or TIdMessageBuilderHtml (https://www.indyproject.org/2008/01/16/new-html-message-builder-class/), they will populate the TIdMessage for you. If you populate the TIdMessage manually, then yes, you are responsible for populating it correctly (ie https://www.indyproject.org/2005/08/17/html-messages/).
  23. Remy Lebeau

    AnsiString oddities

    All string classes in C++Builder - the RTL's (Ansi|Unicode|Wide)String classes, the Standard Library's std::(w|u8|u16|u32)string classes, etc - have concatenation methods built-in. The main reason for using a StringBuilder is memory management (being able to preallocate a buffer and then performing multiple concatenations into it), and that really only applies to RTL strings, as Standard Library strings have a reserve() method for that same purpose. Also, the standard C++ alternative to a StringBuilder is the std::(w)istringstream classes.
  24. Remy Lebeau

    AnsiString oddities

    Don't feel bad. Until about a year ago, I was still regularly using C+++Builder 6 at my day job. Why are you concatenating a WIDE string literal to an ANSI string? You should drop the 'L' prefix, it doesn't really belong there. AnsiString doesn't have an operator+= that takes a Unicode string as input, only another AnsiString. So you are actually invoking the AnsiString constructor that accepts a Unicode string, and that is forcing a Unicode->ANSI data conversion at runtime, which you don't need. The code looks fine, provided that the Form1 pointer is valid to begin with, otherwise the code would have undefined behavior. Yes, you should, provided everything is valid. Have you tried displaying the AnsiString anywhere other than in a debug inspector? Have you tried showing it in your app's UI? Or in a popup MessageBox? Or save it to a file? Anything? That is what the debugger is meant for. Step through the code at runtime line by line, and actually step into the internal logic of the assignment and concatenation operations to make sure they are doing what you are expecting. The only way I could imagine that happening is if either the Form1 pointer is invalid during the assignment, or if the debugger is broken. Which do you think is more likely?
  25. Remy Lebeau

    Sample needed for Net.TSocket UDP cliente and server

    Just because it runs is in a thread doesn't guarantee it is fast/efficient. And actually, in your scenario, there is actually an interrupt, see below... I just now gave it a quick look, and one major issue I see is that you are not using TIdUDPServer the way it is meant to be used, You are creating it in your own thread that does its own reading of the UDP socket, rather than using TIdUDPServer's OnUDPRead event. It seems you are not aware that TIdUDPServer is a multi-threaded component, its OnUDPRead event is fired in the context of internal reading threads, one per Binding. The Synapse/Net.TSocket UDP sockets you are using don't work that same way. So, it actually makes sense now why you are losing packets when using Indy - your code has multiple threads reading from the same UDP socket at the same time, so there are going to be times that you will receive packets in your own thread and be able to process them, and there are going to be times when TIdUDPServer will receive packets that you are ignoring. If you want to do your own reading of an Indy UDP socket, don't use TIdUDPServer, use TIdUDPClient instead. Despite their names, they are not truly client/server components, like with how TCP components work. Since UDP is connection-less, you can actually mix TIdUDPClient and TIdUDPServer in various ways. You can use TIdUDPServer as a client and TIdUDPClient as a server. Or you can have two TIdUDPClients, or two TIdUDPServers, communicate with each other. The difference between them is only in how they manage their respective sockets internally. With TIdUDPClient, it just creates the socket, but you are responsible for all of the reading/writing. With TIdUDPServer, it does all of the reading for you, and then you are responsible for writing. That being said, I see a secondary issue in your code, not related to any particular socket library, that could also cause packet loss. In your reading thread, when you do receive a packet, you lock a TCriticalSection before processing the packet. But, you are using TCriticalSection.TryLock() for that lock, which is not a blocking function. If, for some reason, the CS is already locked by another thread, TryLock() will exit immediate with a False return value, and you will then skip processing the packet, you are not caching/retrying the packet, you just throw it away. There is actually 2 buffers involved - the kernel buffer inside the socket itself, and the application buffer that TIdUDPServer reads into before giving you the data in the OnUDPRead event. To set the size of the socket's buffer, you can use Indy's TIdSocketHandle.SetSockOpt() method to assign a value to the socket's SO_RCVBUF option. To set the size of the OnUDPRead buffer, TIdUDPServer has a BufferSize property, which defaults to 8K.
×