Jump to content

Remy Lebeau

Members
  • Content Count

    2998
  • Joined

  • Last visited

  • Days Won

    135

Everything posted by Remy Lebeau

  1. If that is the case, if each event is its own HTTP chunk, then you can use the TIdHTTP.OnChunkReceived event to handle the stream events. At the moment, you still have to provide a TStream in order for the OnChunkReceived event to fire (I may change that in the future), but you can use TIdEventStream with no event hanlders assigned so that the chunks written to the stream are ignored.
  2. That is a VERY old version of Indy. Indy has been in 10.6.2.x since 2015. You should seriously consider upgrading to the latest version from Indy's GitHub repo.
  3. Remy Lebeau

    TDirectory.GetFiles() is not working in Delphi 11

    Seems you already asked this on StackOverflow, and a few answers were posted there: https://stackoverflow.com/questions/74422756/
  4. Remy Lebeau

    Quote of the Day...

    Seems to be. I can't access anything on alpha.mike-r.com, so it must be down. I'm sure you can find another QOTD server online somewhere. There are a few mentioned on the bottom on https://wiki.freepascal.org/QOTD, for instance. Just look around.
  5. Why are you trying to send JSON? That is not what Microsoft's documentation tells you to send: https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow#redeem-a-code-for-an-access-token The body is NOT to be JSON at all. It needs to be a '&'-delimited list of 'name=value' pairs instead. The TIdHTTP.Post() method handles the 'application/x-www-form-urlencoded' format for you, but only if you give it a TStrings (like TStringList), not a TStream, eg: requestBody := TStringList.Create; requestBody.Add('grant_type=client_credentials'); requestBody.Add('client_id=xxx'); requestBody.Add('client_secret=_xxx_'); requestBody.Add('scope=https://graph.microsoft.com/.default'); url := 'https://login.microsoftonline.com/' + TenantID + '/oauth2/v2.0/token'; FLastResult := IdHTTP.Post(url, requestBody);
  6. I have implemented this kind of logic before, but only in plain socket code (though I suppose it can be applied to socket libraries like ICS or Indy, too). It requires the TLS server to peek not recv the first few bytes of a new connection, and only if those bytes belong to the header of a TLS handshake then enable TLS on that connection (allowing the previously-peeked bytes to now be read by whatever TLS library you use), otherwise don't enable TLS on the connection and just read the connection raw.
  7. The screenshot you provided appears to be an actual websocket event. 'text/event-stream' is just a stream of linebreak-delimited events. TIdHTTP is not currently designed to handle that kind of stream. Using TIdEventStream is going to give you arbitrary blocks of data as they are read off the socket, which you are assuming are complete events, but that is not guaranteed (or even likely) to happen. To be safe, you would have to buffer the stream data, break it up on linebreaks as they become available, and decode only complete events as UTF-8. The code you have presented has the potential to decode incomplete events. In this type of situation, it would be useful if TIdHTTP had a flag available to tell it not to read the response body at all and just exit, letting the caller then read the body from the IOHandler directly as needed. Similar to the existing hoNoReadMultipartMIME and hoNoReadChunked flags, but for other kinds of responses. Such a flag does not exist at this time.
  8. That is only part of the response, and not even the relevant part, eihter. That snippet shows that you are requesting an unencrypted HTTP url and being redirected to an encrypted HTTPS url, but it is not showing the contents of the subsequent HTTPS request or its response, which contains the actual event data.
  9. Remy Lebeau

    Attachments in Base64

    Those are the wrong settings to use when attachments are involved. You should read the following articles on Indy's blog (just ignore the portions about HTML): https://www.indyproject.org/2005/08/17/html-messages/ https://www.indyproject.org/2008/01/16/new-html-message-builder-class/ In your situation, you need to set the top-level ContentType to 'multipart/mixed' instead, and then add the plain text as a TIdText object in the TIdMessage.MessageParts collection, and the PDF as a TIdAttachmentFile object in the TIdMessage.MessageParts collection, for example: IdMessage1.ContentType := 'multipart/mixed'; with TIdText.Create(IdMessage1.MessageParts, nil) do begin ContentType := 'text/plain'; CharSet := 'utf-8'; Body.Text := 'Tässä on ääkkösiä ja liite'; end; with TIdAttachmentFile.Create(IdMessage1.MessageParts, '<path to>\1.pdf') do begin ContentType := 'application/pdf'; end; The helper TIdMessageBuilderPlain class can set that up correctly for you, for example: Builder := TIdMessageBuilderPlain.Create; try Builder.PlainText.Text := 'Tässä on ääkkösiä ja liite'; Builder.PlainTextCharSet := 'utf-8'; Builder.Attachments.Add('<path to>\1.pdf').ContentType := 'application/pdf'; Builder.FillMessage(IdMessage1); finally Builder.Free; end; By setting the top-level ContentType to 'text/plain', you are telling email readers to interpret the email's entire content as just one big plain text document, rather than letting them parse the content for attachments, etc. The default AttachmentEncoding is 'MIME', you should leave it at that. UUE was popular way back in the days of Usenet groups, but nobody uses those anymore. MIME is the dominant encoding used nowadays.
  10. Since the data is streaming, there is no guarantee that the data that is available during each firing of the TIdHTTP.OnWork event will represent a complete line of data. It could be partial data. At the very least, I would not suggest using the Memo's LoadFromStream() method, which replaces the entire contents of the Memo on each call. It would be better to convert each event's data to a raw string and then append it to (not replace) the Memo's current text, which can be done efficiently using its SelStart/SelLength/SelText properties. But, more importantly, the TMemoryStream that you are having TIdHTTP write to will just keep receiving more and more data that you are never discarding from memory, so the stream's memory usage will keep growing over time, until memory runs out eventually. If you want to process the data in chunks as it is arriving, I would suggest using a TIdEventStream instead, replacing the TIdHTTP.OnWork event handler with a TIdEventStream.OnWrite event handler instead. You will be given access to the actual bytes per event as a dynamic array, which means you can simply increment the array's refcount, send the array pointer to the main thread, and then decrement the refcount.
  11. It would help if you could show the raw HTTP response data that is actually being transmitted. curl is only outputting the JSON data, not the full HTTP data (you can enable its verbose output to see that). Streaming events like you have described could be transmitted in many different formats. Depending on the format used, using Indy's TIdHTTP component, for instance, you might be able to either: - use its OnChunkReceived event - enable its hoNoReadMultipartMIME or hoNoReadChunked option and then read the lines manually from Indy's socket directly - use a TIdEventStream as the target to receive the data, using its OnWrite event to access the lines. It is hard to say for sure without more information.
  12. Remy Lebeau

    Sender of TAction.OnHint Event?

    There is another possibility. If you don't mind assigning the OnHint events in code at runtime (or at least looping through the actions at runtime to update their OnHint handlers that are assigned at design-time), then you can use the TMethod record to alter the event handler's Self pointer to point at each TAction object instead of the TForm object. Then you can just type-cast the Self pointer inside the handler. For example: procedure TForm1.FormCreate(Sender: TObject); var Evt: THintEvent; begin ... Evt := Action1.OnHint; // or: Evt := @ActionHint; TMethod(Evt).Data := Action1; // instead of Form1 Action1.OnHint := Evt; ... // repeat for all TAction objects that share ActionHint()... end; procedure TForm1.ActionHint(var HintStr: string; var CanShow: Boolean); begin // use TAction(Self) as needed... end;
  13. Remy Lebeau

    Any advice when to use FileExists?

    I don't have an up-to-date version to verify this with, but I seriously doubt what you claim is true. Up to at least XE8, FileExists() was never coded to raise any exceptions. I can't imagine Embarcadero would ever add exception handling to FileExists() in the years since that release.
  14. Remy Lebeau

    Sender of TAction.OnHint Event?

    Unfortunately, you can't, at least not from the event itself, anyway. You will have to instead use a different event handler for each action, making them call a common method if they need to share code, eg: procedure TForm1.Action1Hint(var HintStr: string; var CanShow: Boolean); begin DoActionHint(Action1, HintStr, CanShow); end; procedure TForm1.Action2Hint(var HintStr: string; var CanShow: Boolean); begin DoActionHint(Action2, HintStr, CanShow); end; procedure TForm1.DoActionHint(Sender: TObject; var HintStr: string; var CanShow: Boolean); begin ... end; .
  15. Remy Lebeau

    Use of inline variables..

    Apparently, this is a known bug since inline variables were first introduced in 10.3 Rio, and the following tickets are still open: RSP-22359: Nameless types are not allowed within inline variable declarations RSP-31970: How to inline static array variables in Rio Based on the comments on the tickets, the workaround I suggested earlier to pre-define the array type should work for an inline variable: type Char256Array = array [0 .. 255] of Char; var buffer: Char256Array;
  16. Remy Lebeau

    Use of inline variables..

    List counts are 32bit Integers, not 16bit Smallints. I doubt you will ever have more than 32K Forms/DataModules active at a time, but it is less efficient to make the compiler compare a Smallint loop counter to an Integer count on every iteration, the counter would have to be scaled up to 32bits each time. Is that an actual compiler error? Or just an ErrorInsight error, but the code actually compiles fine?
  17. Remy Lebeau

    Use of inline variables..

    Yes. Its scope is limited to the loop body, so it will enter and leave scope on each iteration. Not really, no. If ARC were involved (which is no longer the case), that would include incrementing and decrementing the object's refcount. But in any case, since the variable is just a pointer, the compiler is likely to optimize the variable to reuse the same memory for each new instance each time the loop iterates. What does the error message actually say? It looks OK ome, so I'm guessing that the compiler simply hasn't implemented support for inline arrays. Do you have the same problem if you declare the array type beforehand? type Char256Array = array [0 .. 255] of Char; var buffer: Char256Array; Last I heard, the CodeEditor/ErrorInsight still hadn't been updated to support the new inline syntax yet. But the compiler will still accept it.
  18. Remy Lebeau

    Media player causing weird issue

    To use a custom file extension like that, you have to create and register a custom TMedia class to handle it. The StackOverflow link I gave you earlier explained that.
  19. Remy Lebeau

    A gem from the past (Goto)

    I would have just wrapped the logic in a loop instead: function whatever: SomeType; begin repeat // some code that generates the result until Result <> ResultIWantToDebug; end; Or, renamed the original function and introduced a new function with the original name to call the new function in a loop: function actual_whatever: SomeType; begin // some code that generates the result end; function whatever: SomeType; begin repeat // some code that generates the result until Result <> ResultIWantToDebug; end;
  20. Remy Lebeau

    How can i stream audio from mediaplayer with idhttp

    Why are you sending a new HTTP request from inside the TIdHTTP.OnAuthorization event? There is already an HTTP request in progress when that event is fired, it is asking you to provide credentials for that request. If you look at the example more carefully, it is not even using TIdHTTP to stream the actual media, TMediaPlayer is handling that internally. TIdHTTP is simply being used to retrieve a list of available media URLs from a server, and then a selected URL is given directly to TMediaPlayer to play, one at a time. So, what is stopping you from doing the same thing in your code? Did you even try giving TMediaPlayer a media URL to play? MediaPlayer1.FileName := 'https://radio.weatherusa.net/NWR/KEC61_2.mp3'; MediaPlayer1.Play; If that still doesn't work, then see my earlier StackOveflow answer to How play .mp3 files loaded from a URL in TMediaPlayer with Firemonkey?
  21. Remy Lebeau

    How can i stream audio from mediaplayer with idhttp

    Have you looked at Embarcadero's Streaming Media example yet? https://docwiki.embarcadero.com/CodeExamples/en/FMX.StreamingMedia_Sample
  22. Remy Lebeau

    Range check error (10016) on SetWindowLong

    Type-cast the 3rd parameter: SetWindowLong(thisform.Handle, GWL_STYLE, LONG(WS_POPUP or WS_CLIPSIBLINGS or WS_CLIPCHILDREN or WS_SYSMENU)); Though, SetWindowLong() has been deprecated for a long time, as it doesn't support 64-bit apps. You really should be using SetWindowLongPtr() instead: SetWindowLongPtr(thisform.Handle, GWL_STYLE, LONG_PTR(WS_POPUP or WS_CLIPSIBLINGS or WS_CLIPCHILDREN or WS_SYSMENU));
  23. Remy Lebeau

    Is Embarcadero's DocWiki down?

    Anyone else having trouble accessing https://docwiki.embarcadero.com ?
  24. Remy Lebeau

    Page Control

    Unfortunately, the underlying Win32 tab control which TPageControl is built on does not provide that information natively. The TPageControl.OnChanging event is fired BEFORE the active TabSheet changes, and the TPageContro.OnChange event is fired AFTER the active TabSheet is changed. So, you will have to keep track of the previous TabSheet in your own code, eg: private PreviousTabSheet: TTabSheet; procedure TForm1.PageControl1Changing(Sender: TObject; var AllowChange: Boolean); begin PreviousTabSheet := PageControl1.ActivePage; AllowChange := True; end; procedure TForm1.PageControl1Change(Sender: TObject); begin if PreviousTabSheet <> nil then begin // use PreviousTabSheet as needed... PreviousTabSheet := nil; end; end;
  25. Remy Lebeau

    Change 'DPI awareness' on runtime

    Embarcadero's DPI handling is still quite broken. They have taken several major releases to work on it, and it still has problems. You are probably best off simply disabling DPI awareness at the project level, and then scale your UI in your own code as needed.
×