-
Content Count
2349 -
Joined
-
Last visited
-
Days Won
95
Everything posted by Remy Lebeau
-
Delphi 10.3.3 - Indy - could not load root certificate
Remy Lebeau replied to Vandrovnik's topic in Indy
Embarcadero will be releasing a hotfix for 10.3.3 soon to address this in the shipped version of Indy. -
Delphi 10.3.3 - Indy - could not load root certificate
Remy Lebeau replied to Vandrovnik's topic in Indy
Install instructions are on Indy's website. -
Black screen while processing longer task on windows server 2012 R2
Remy Lebeau replied to Soji's topic in General Help
That is not entirely true. It does have side-effects if you are not careful. It allows pending messages from SendMessage...() from other threads to be processed (even though they don't go through the message queue themselves, they still require the receiving thread to perform message retrieval operations so they can be delivered). More importantly, it allows on-demand synthetic messages, like WM_TIMER, to be added to the queue (see Why is my message queue full of WM_TIMER messages?). -
Delphi 10.3.3 - Indy - could not load root certificate
Remy Lebeau replied to Vandrovnik's topic in Indy
Which is a duplicate of https://quality.embarcadero.com/browse/RSP-26742, which has a patch on https://quality.embarcadero.com/browse/RSP-27144 But either way, make sure you are using the latest code from Indy's GitHub repo. The version that shipped with the IDE was modified by accident by Embarcadero, it was never supposed to be shipped that way. The code in the repo was not broken to begin with. Marco explained the situation to me and apologized for the mixup. -
Unless the Form has an OnClose event handler that sets the Action parameter to caFree, allowing the Form to free itself when it is closed. This is a common idiom when using Show() instead of ShowModal(). It does not look at the Owner at all. Nor could it anyway, because the Owner may not be a TWinControl at all. Candidates for an auto-assigned PopupParent include the Application's ActiveForm, the Application's MainForm, the Screen's ActiveForm, and the Application window.
-
I'm not a Unix/Linux developer, I have very little knowledge of what it takes to use those platform APIs properly. I didn't write those sections of Indy's code, I just maintain them. Obviously the new code I recently added is not working. So feel free to do whatever you need to make them work correctly, and then submit the changes to me and I will happily incorporate them.
-
When you encounter a problem with something, you need to be more specific about WHAT the problem actually is, and WHERE the problem is in the code. It is generally considered impolite to just dump code into a forum and expect people to debug it for you without any idea what is wrong with it. There is always only 1 top-level element in a document. But there could be XML processing instruction before that element (<?xml ...?>, etc). To get the 1st element in a document, you should use the DocumentElement property instead: LNodeElement := LDocument.DocumentElement; As it should be, since <messages> is the only child node of the <inbox-query-response> element. But your code is not going any deeper into the document, so you are not seeing the <message> child elements, and their child elements, and so on. You have to iterate each level of ChildNodes separately. That will never be true for the <messages> element that LNode is pointing at. Neither will that. With that said, try something more like this: procedure TForm2.RetrieveDocument; var LDocument: IXMLDocument; LNodeElement, LNode: IXMLNode; LAttrValue: string; I: Integer; begin LDocument := TXMLDocument.Create(nil); LDocument.LoadFromFile(SrcPath); { File should exist. } // Number of childNodes, always shows 1 ShowMessage(IntToStr(LDocument.ChildNodes.Count)); { Find the root element node. } LNodeElement := LDocument.DocumentElement; if LNodeElement <> nil then begin { Get a specific attribute. } if LNodeElement.HasAttribute('version') then begin LAttrValue := LNodeElement.Attributes['version']; ShowMessage('Attribute value: ' + LAttrValue); end; { Find a specific node. } LNodeElement := LNodeElement.ChildNodes.FindNode('messages'); if LNodeElement <> nil then begin { Traverse messages. } for I := 0 to LNodeElement.ChildNodes.Count - 1 do begin LNode := LNodeElement.ChildNodes.Get(I); { Display node name. } ShowMessage('Node name: ' + LNode.NodeName); if LNode.NodeName = 'message' then ProcessMessageElement(LNode); end; end; end; end; procedure TForm2.ProcessMessageElement(const AMessage: IXMLNode); var I: Integer; LNode: IXMLNode; begin { Traverse values. } for I := 0 to AMessage.ChildNodes.Count - 1 do begin LNode := AMessage.ChildNodes.Get(I); { Check whether the node type is Text. } if (LNode.NodeType = ntText) or LNode.IsTextElement then begin ShowMessage(LNode.NodeName + ' is a node of type Text. The text is: ' + LNode.Text); end; if LNode.NodeName = 'message-meta-data' then ProcessMetaDataElement(LNode); end; end; procedure TForm2.ProcessMetaDataElement(const AMetaData: IXMLNode); var I: Integer; LNode: IXMLNode; begin { Traverse values. } for I := 0 to AMetaData.ChildNodes.Count - 1 do begin LNode := AMetaData.ChildNodes.Get(I); { Check whether the node type is Text. } if (LNode.NodeType = ntText) or LNode.IsTextElement then begin ShowMessage(LNode.NodeName + ' is a node of type Text. The text is: ' + LNode.Text); end; if LNode.NodeName = 'peppol-header' then ProcessPeppolHeaderElement(LNode); end; end; procedure TForm2.ProcessPeppolHeaderElement(const AHeader: IXMLNode); var I: Integer; LNode: IXMLNode; begin { Traverse values. } for I := 0 to AHeader.ChildNodes.Count - 1 do begin LNode := AHeader.ChildNodes.Get(I); { Check whether the node type is Text. } if (LNode.NodeType = ntText) or LNode.IsTextElement then begin ShowMessage(LNode.NodeName + ' is a node of type Text. The text is: ' + LNode.Text); end; end; end;
-
Damn it. I had that error fixed in update 0fdd9ad, but it looks like it got reverted back in update 5251bc9. I have fixed it (again) in update a965917.
-
I don't think a new Fix Pack has been released yet. On the other hand, the download links do say "10.3 (RTM/UP1/2/3)", though the update dates are several months old.
-
Finalization section not called unless main form shown
Remy Lebeau replied to Mark Williams's topic in VCL
The TApplication.MainForm property is set by TApplication.CreateForm() only after the first TForm object is fully constructed. So no, the MainForm property will not have been assigned yet in the OnCreate event of any TForm object that is created before, or during, the first call to TApplication.CreateForm(). -
Finalization section not called unless main form shown
Remy Lebeau replied to Mark Williams's topic in VCL
The MainForm is established by the first call to TApplication.CreateForm(). Changing the project options simply changes the default code that calls CreateForm() in the main .dpr file. If there is no MainForm assigned when Application.Run() is called, Run() simply exits immediately. -
"text/html/pdf/plain" is not a valid media type. Where did you ever get that from? Valid media types are like "text/html", "text/plain", "application/pdf", etc
-
That is just the body content? What about the headers above the content? Can you please show the actual RAW emails in their entirety?
-
Then I suggest you look at the RAW email data that is in your inbox for any differences between what the Gmail app is sending vs what Delphi/AndroidStudio is sending.
-
That text *IS* encoded in UTF-8 properly ("" is the byte sequence EF BB BF, which is the UTF-8 BOM). This is just what UTF-8 encoded text looks like when it is mis-interpreted as a Latin encoding, such as ISO-8859-1 or Windows-1252. Which mean you are likely just missing a required "utf-8" charset attribute assigned to the text so the receiver knows it is UTF-8.
-
Enabling/disabling even a single privilege can still potentially report ERROR_NOT_ALL_ASSIGNED. It doesn't hurt to handle that condition, just in case.
-
TRegistry.SaveKey() uses the Win32 API RegSaveKey() function, which fails if the output file already exists, per documented behavior: Perhaps you meant DirectoryExists() instead of FileExists()? You could always call ForceDirectories() before SaveKey(), just in case.
-
Per the AdjustTokenPrivileges() documentation: Your code is not differentiating between ERROR_SUCCESS and ERROR_NOT_ALL_ASSIGNED if AdjustTokenPrivileges() returns non-zero.
-
Sending Email via GMail Using OAuth 2.0 via Indy
Remy Lebeau replied to Ugochukwu Mmaduekwe's topic in Indy
2FA is a good thing. And no, you don't actually need to authenticate every login. An app-specific password is meant to be used in only 1 location and shouldn't be passed around. You can set Google to remember where the password is being used from so you don't have to re-authenticate every time it is used from that location. I use app-specific passwords when testing Indy with GMail (POP3, SMTP, and IMAP) and don't have to re-authenticate each time. -
Sending Email via GMail Using OAuth 2.0 via Indy
Remy Lebeau replied to Ugochukwu Mmaduekwe's topic in Indy
However, you don't actually need OAuth to access GMail. You can instead go into your Google account settings and generate an Application-specific password, which works just fine with Indy. -
TThread holds an internal reference to itself (in the TThread.CurrentThread property), which under ARC ensures the thread stays alive and continues to run as expected even if all other references to the TThread object get cleared. That internal reference gets cleared when the thread stops running. However, that internal reference does not get assigned until the thread is actually running, which is why you need the extra delay on ARC systems, otherwise if ARC calls the TThread destructor, it will terminate the newly-created thread before it begins to run. Rather than using an arbitrary sleep, you should instead use a waitable event, like TEvent. Have the code that creates the TThread object wait on that event before continuing. Signal the event at the beginning of your thread's Execute(). For example: type TMyThread = class(TThread) private procedure aaaa; FStarted: TEvent; protected procedure Execute; override; public constructor Create; reintroduce; destructor Destroy; override; procedure WaitForStart; end; constructor TMyThread.Create; begin inherited Create(False); FStarted := TEvent.Create; end; destructor TMyThread.Destroy; begin FStarted.Free; inherited; end; procedure TMyThread.Execute; begin FStarted.SetEvent; ... end; procedure TMyThread.WaitForStart; begin FStarted.WaitFor(Infinite); end; procedure TForm1.Button1Click(Sender: TObject); var T: TMyThread; begin T := TMyThread.Create; T.WaitForStart; end;
-
The HTTP 206 Partial Content
Remy Lebeau replied to DarkDucke's topic in ICS - Internet Component Suite
Even though "keep-alive" wasn't standardized until HTTP 1.1, most HTTP 1.0 servers do recognize it. But either way, the "Connection" request header has nothing to do with this issue. Definitely something that should be fixed in the user's code, but again, not the root cause of the issue. The HTTP server will just ignore a "Content-Type" header in a GET request. -
The HTTP 206 Partial Content
Remy Lebeau replied to DarkDucke's topic in ICS - Internet Component Suite
The ONLY time a web server should ever send a 206 response code is if the request contained a "Range" header asking for a specific range of bytes in the response data. That doesn't make sense to do in this situation. So double check that you are not setting up your TSslHttpCli to send such a request header. TIdHTTP does not send a "Range" request header unless you explicitly ask it to, via the TIdHTTP.Request.Range(s) properties. -
Load a String from a file - returns strange char set
Remy Lebeau replied to bernhard_LA's topic in Algorithms, Data Structures and Class Design
Yes, it does: procedure TStrings.LoadFromFile(const FileName: string); var Stream: TStream; begin Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite); try LoadFromStream(Stream); finally Stream.Free; end; end; procedure TStrings.LoadFromFile(const FileName: string; Encoding: TEncoding); var Stream: TStream; begin Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite); try LoadFromStream(Stream, Encoding); finally Stream.Free; end; end; procedure TStrings.LoadFromStream(Stream: TStream); begin LoadFromStream(Stream, nil); end; procedure TStrings.LoadFromStream(Stream: TStream; Encoding: TEncoding); var Size: Integer; Buffer: TBytes; begin BeginUpdate; try Size := Stream.Size - Stream.Position; SetLength(Buffer, Size); Stream.Read(Buffer, 0, Size); Size := TEncoding.GetBufferEncoding(Buffer, Encoding, FDefaultEncoding); SetEncoding(Encoding); // Keep Encoding in case the stream is saved SetTextStr(Encoding.GetString(Buffer, Size, Length(Buffer) - Size)); finally EndUpdate; end; end; You mean TStrings, not TStringList. TMemo.Lines (aka the TMemoStrings class) derives from TStrings directly, not from TStringList. But yes, TStrings.LoadFromStream() behaves the way you describe (see above), TMemoStrings does not override that behavior. Yes, the way TStrings.LoadFromStream() was implemented is really inefficient for large amounts of text.