-
Content Count
3001 -
Joined
-
Last visited
-
Days Won
135
Everything posted by Remy Lebeau
-
The device should not matter, no. This is strictly an RTL error message that TThread.Start() raises if it can't resume a suspended thread, either because the thread is not allowed to resume (e, it is already finished), or the OS failed to resume the thread properly. Hard to say for sure what is going on without seeing the latest source code for TThread.
-
I don't have the source for the latest version to look at, but in XE3 (the last version I do have source for), that specific error message only occurs under these conditions: - (Windows only) calling Start() on a running thread, or a thread that has had Suspend() called on it more than once before Start(). - calling Start() on a thread that has already finished, but not been destroyed yet - calling Start() on a thread created by reading TThread.CurrentThread in a thread context that is not owned by a TThread. None of those conditions apply to your TThread.CreateAnonynousThread() example. Calling Start() on such a thread is absolutely the correct thing to do. So this has to be a bug that Embarcadero has introduced in a recent version and will have to fix.
-
They DO use a private copy, actually. It is hidden behind an abstraction layer that their technologies use internally. No need to rename units (I hate how IntraWeb does that for its private copy of Indy). Users are supposed to be free to update the public bundled copy of Indy without breaking the private copy. That is why the abstraction layer was created. Well, that is the theory anyway, but there are a few cracks where not all pieces are using the abstraction layer correctly/at all. DataSnap, LiveTiles, and now LivePreview it seems, depend on the shipped copy of Indy rather than the private copy. I have just filed a ticket with Embarcadero: RSP-33397: Can't load LivePreview270.bpl after updating Indy Same here. It is a goal to eventually get Indy added to GetIt, I just haven't had any time to work on it. Indy's complicated install steps for C++Builder need to be refined a bit first.
-
First off, I would suggest getting rid of TempStream completely. You did not show what it actually is, but I'm assuming it is a TMemoryStream. You are using it only to determine the email's total byte size for progress tracking. Indy has a TIdCalculateSizeStream class you can use for that purpose instead. No need to hold on to a TStream object for the lifetime of the transmission. For example: private EmailSize: Int64; ... procedure TForm1.btnSendClick(Sender: TObject); var Attachmentfile: TIdAttachmentFile; sSendReportTo, sSendReportCC: string; CalcStream: TIdCalculateSizeStream; begin // IdSMTP1 := TIdSMTP.Create(nil); // try // IdSSLIOHandlerSocketOpenSSL1 := TIdSSLIOHandlerSocketOpenSSL.Create(IdSMTP1); IdMessage1 := TIdMessage.Create(IdSMTP1); // // IO HANDLER SETTINGS // with IdSSLIOHandlerSocketOpenSSL1 do begin MaxLineAction := maException; SSLOptions.Method := sslvTLSv1; SSLOptions.Mode := sslmUnassigned; SSLOptions.VerifyMode := []; SSLOptions.VerifyDepth := 0; SSLOptions.Mode := sslmClient; end; // // SETTING SMTP COMPONENT DATA // IdSMTP1.Host := 'smtp-server.somewhere.com'; IdSMTP1.Port := 587; IdSMTP1.Username := 'MyName@somewhere.com'; // please change to your gmail address // IdSMTP1.Password := 'Abc123456'; IdSMTP1.IOHandler := IdSSLIOHandlerSocketOpenSSL1; IdSMTP1.AuthType := satDefault; IdSMTP1.UseTLS := utUseExplicitTLS; // IdSMTP1.OnWork := IdSMTP1Work; IdSMTP1.OnWorkBegin := IdSMTP1WorkBegin; IdSMTP1.OnWorkEnd := IdSMTP1WorkEnd; // // SETTING email MESSAGE DATA // IdMessage1.Recipients.EMailAddresses := 'ToAddress@somewhere.com'; IdMessage1.CCList.EMailAddresses := 'CCAddress@somewhere.com'; // add Attachment to mail // Attachmentfile := TIdAttachmentFile.Create(IdMessage1.MessageParts, 'E:\IndySMTPEmailTest\ReadMe.txt'); // IdMessage1.From.Address := 'myname@somewhere.com'; IdMessage1.Subject := 'Test Email Subject'; IdMessage1.Body.Text := 'Test Email Body'; IdMessage1.Priority := mpHigh; // with TIdText.Create(IdMessage1.MessageParts, nil) do begin Body.Text := '<p style="color: #5e9ca0;">This is a test <strong>message</strong> for <span style="color: #ff0000;"><strong>emailing</strong></span>.</p><h1 style="color: #5e9ca0;"> </h1>'; ContentType := 'text/html'; end; // IdMessage1.ContentType := 'multipart/mixed'; // CalcStream := TIdCalculateSizeStream.Create; try IdMessage1.SaveToStream(CalcStream, False); EmailSize := CalcStream.Size; finally CalcStream.Free; end; // try IdSMTP1.Connect; except on E: Exception do begin MessageDlg('Connection to Mail Server unsuccessful! The following information was returned..: ' + E.Message, mtWarning, [mbOK], 0); Exit; end; end; try try IdSMTP1.Send(IdMessage1); finally IdSMTP1.Disconnect(); end; except on e: Exception do begin MessageDlg('Mail send unsuccessful! The following information was returned..: ' + E.Message, mtWarning, [mbOK], 0); Exit; end; end; // finally IdSMTP1.Free; end // ShowMessage('Email sent'); end; procedure TForm1.IdSMTP1Work(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64); var nPercent: Integer; begin if AWorkMode <> wmWrite then Exit; // if EmailSize > 0 then begin nPercent := Trunc((Double(AWorkCount) / EmailSize) * 100); // ProgressBar1.Position := nPercent; Label1.Caption := IntToStr(nPercent) + ' %'; end else begin ProgressBar1.Position := 0; Label1.Caption := IntToStr(AWorkCount) + ' b'; end; Update; end; procedure TForm1.IdSMTP1WorkBegin(ASender: TObject; AWorkMode: TWorkMode; AWorkCountMax: Int64); begin if AWorkMode <> wmWrite then Exit; Label1.Caption := '0 %'; ProgressBar1.Position := 0; ProgressBar1.Max := 100; ShowMessage('Stream Size = ' + EmailSize.ToString); Update; end; procedure TForm1.IdSMTP1WorkEnd(ASender: TObject; AWorkMode: TWorkMode); begin if AWorkMode <> wmWrite then Exit; ProgressBar1.Position := 100; Label1.Caption := '100 %'; Update; end; Your failure is happening inside of TIdMessage.SaveToStream() itself, so 99% of the code you have shown is irrelevant since it is not actually being used. That is because the code is failing before TIdSMTP.Send() is reached, so there is no progress to report. Since the crash is happening inside of TIdMessage.SaveToStream(), the events are not going to be fired at all, since there is no SMTP transmission in progress. As for why the crash is occurring in the first place, given the call stack shown, there are only 2 possibilities: - TIdIOHandler.ClosedGracefully is True when it should not be. This gets set when a read operation returns 0 bytes, a write operation fails with a non-timeout error, or TIdIOHandler.CloseGracefully() is called. - TIdIOHandlerStream.StreamingAvailable() is returning False when it should not be. This happens when there are no TStream objects assigned to the IOHandler. TIdMessage.SaveToStream() uses a write-only TIdIOHandlerStream, writing to the specified output TStream. So TIdIOHandlerStream should not be trying to read from a non-existent input TStream at all, thus TIdIOHandlerStream.StreamingAvailable() should always return True, unless the IOHandler has been closed prematurely. And TIdIOHandler.ClosedGracefully should never be False unless the target TStream fails to write, which would be an indication of a larger issue. And Indy never calls TIdIOHandler.CloseGracefully(). So, you are just going to have to do some debugging, and figure out which of the above conditions is actually causing TIdIOHandlerStream.CheckForDisconnect() to think it needs to raise the exception. It should not be happening under normal conditions.
-
[FMX][Android]How get control name under the click?
Remy Lebeau replied to Fabian1648's topic in FMX
Why are you simply not using the Sender of the click event? procedure TMyForm.MyControlClick(Sender: TObject); begin ShowMessage(TControl(Sender).Name); end; -
INDY UDP broadcast to a specific Network
Remy Lebeau replied to shineworld's topic in Network, Cloud and Web
If the OS can broadcast to multiple subnets using a single socket bound to local IP 0.0.0.0, then so be it. I just know that hasn't always worked that way. But maybe things are better in recent years. If there are multiple adapters installed, and you know what IP they are assigned, you can bind a separate socket to each one directly, eg: FUDPServer.Active := False; FUDPServer.Bindings.Clear; ... for I := 0 to FNetworksList.Count - 1 do begin with FUDPServer.Bindings.Add do begin IP := FNetworksList[I].LocalIP; Port := 5001; end; end; ... FUDPServer.BroadcastEnabled := True; FUDPServer.Active := True; This way, if you then Send...()/Broadcast() on any given Binding, it will send out only to that specific adapter/subnet directly, rather than the OS having to hunt around the routing tables to figure out which adapter/subnet to send out to, as is the case when calling Send...()/Broadcast() on a single Binding that is bound to 0.0.0.0. Operating Systems are pretty good at maintaining good routing tables, but they are not infallible. Sometimes routes are not configured properly, or get corrupted over time. So sometimes it may be easier/better to just be more direct and cut out the middle man. -
There is some info in this discussion:
-
INDY UDP broadcast to a specific Network
Remy Lebeau replied to shineworld's topic in Network, Cloud and Web
You can't bind a socket to a broadcast IP, you can only send data to a broadcast IP as the destination. You must bind the socket to the adapter's local network IP instead, eg; // inits and starts searching phase FUDPServer.Active := False; FUDPServer.Bindings.Clear; with FUDPServer.Bindings.Add do begin IP := '192.168.0.27'; Port := 5001; end; ... FUDPServer.BroadcastEnabled := True; FUDPServer.Active := True; And you can't bind a socket to an IP that does not belong to the local machine. In your screenshots, there is no local adapter present that has a local IP on the '192.168.120.x' subnet (unless you are filtering the adapters and we just can't see them all). -
INDY UDP broadcast to a specific Network
Remy Lebeau replied to shineworld's topic in Network, Cloud and Web
Yes, you could specify the broadcast IP of the desired network, and let Windows figure out which adapter to use. Alternatively, rather than creating only 1 Binding that is bound to '0.0.0.0', you can create multiple Bindings, one for each network adapter (looks like you are already enumerating the adapters anyway), and then you can call Broadcast() on each adapter directly. That will allow you to search multiple networks at a time. -
Help to achieve multiple inheritance by class redesign
Remy Lebeau replied to iiid354's topic in Algorithms, Data Structures and Class Design
Interfaces can't have 'class' methods, either. What is the actual GOAL that you are trying to accomplish? Can you provide a use-case example of what you WANT that is not working for you? -
Install recent Delphi versions on Windows XP
Remy Lebeau replied to dummzeuch's topic in Delphi IDE and APIs
Even if you could get it INSTALLED on XP, it won't RUN on XP. The IDE uses APIs and libraries that simply don't exist on XP. -
Adressing IP with System.Net.HttpClient
Remy Lebeau replied to weabow's topic in Network, Cloud and Web
In general, I could see this working if you connect the underlying TCP socket to the desired IP address, and then have the TLS handshake use the SNI extension to specify the desired domain name as the target for the certificate, and also have HTTP send a "Host" header specifying the same domain name. But I don't know if/how this can be done with THTTPClient, though. It is not doable with Indy's TIdHTTP (without hacking its source code) since everything uses the same hostname/IP specified in the URL. -
This is covered in the documentation: http://docwiki.embarcadero.com/RADStudio/Sydney/en/10.4_Sydney_-_Release_2
-
Indy should definitely NOT be in the VCL unit scope. It is not setup that way in Indy's official repo, so that is something extra that Embarcadero must have setup on their end. Why, I could not answer that. Indy is available to both VCL and FMX without using separate unit scopes.
-
Cannot access in folder documents on Android 11 in Delphi XE 10.4.2
Remy Lebeau replied to mrceski's topic in Cross-platform
Why do people keep insisting on using the XE name for things that are not XE?!? Embarcadero stopped using the XE prefix after XE8. The current version is "10.4.2 Sydney", period. Did you read the Android 11 developer documentation? https://developer.android.com/about/versions/11 Google made behavioral changes: https://developer.android.com/about/versions/11/behavior-changes-all In particular: https://developer.android.com/about/versions/11/privacy/storage -
Which version of Delphi are you using? $(Auto) is only supported in 10.4.1 and later.
-
Events are just properties, like any other. You can assign your event handlers like this: IdSMTP1 := TIdSMTP.Create(nil); ... IdSMTP1.OnWork := IdSMTP1Work; IdSMTP1.OnWorkBegin := IdSMTP1WorkBegin; IdSMTP1.OnWorkEnd := IdSMTP1WorkEnd; ...
-
Global variable : why the compiler don't complain about this ?
Remy Lebeau replied to mderie's topic in General Help
Already exists: http://docwiki.embarcadero.com/RADStudio/en/Using_Namespaces_with_Delphi http://docwiki.embarcadero.com/RADStudio/en/Unit_Scope_Names -
"Variable Required" compiler error only in Delphi 10.4.2
Remy Lebeau replied to Dave Novo's topic in Delphi IDE and APIs
Yes, that is indeed odd. One would have hoped those helpers would have used WriteBuffer() instead of Write(), but they don't. Why knows why. Bad decision, IMHO. -
Because drawing events are meant JUST for drawing, using the current UI state to decide what to draw. They are not meant for managing/updating the UI state. As long as you are changing the color/font of the Canvas that you are drawing onto, then that is perfectly fine. That is not UI state, that is just drawing state. On the other hand, changing the color/font of a UI control from inside a drawing event would be wrong, and can lead to endless repaint loops that eat up CPU cycles and slow down the UI message loop. Because that is NOT needed OR appropriate during a drawing event. That is for UI logic to handle instead, for instance in reaction to some action. Changing the visibility of a UI control will trigger a UI repaint to draw the updated UI display with/without the control in it. The drawing should account for the control's current state, not update its state.
-
"Variable Required" compiler error only in Delphi 10.4.2
Remy Lebeau replied to Dave Novo's topic in Delphi IDE and APIs
It should not be working. Either it will pass the address of the Pointer itself rather than the address of the TRect, or else it will misinterpret the Pointer as a TBytes. Either way would be wrong. It needs to be either Write(r, sizeof(r)) or WriteData(@r, sizeof(r)). -
"Variable Required" compiler error only in Delphi 10.4.2
Remy Lebeau replied to Dave Novo's topic in Delphi IDE and APIs
Ideally, it should not compile. There are only 3 overloads of Write(), and that code does not logically match any of them: function Write(const Buffer; Count: Longint): Longint; overload; virtual; function Write(const Buffer: TBytes; Offset, Count: Longint): Longint; overload; virtual; function Write(const Buffer: TBytes; Count: Longint): Longint; overload; There is an overload of WriteData() that does match, though: function WriteData(const Buffer: Pointer; Count: Longint): Longint; overload; That being said, there is a known issue when passing a Pointer to Write(): https://delphihaven.wordpress.com/2012/09/30/potential-xe3-gotcha-dodgy-old-code-vs-new-tstream-overloads/ And that is the case here - as seen above, Write() is overloaded to accept either untyped or TBytes parameters. So, it is possible that sometime between Seattle and Sydney, Embarcadero finally fixed this bug in the compiler. Or, maybe it is just a matter of {$TYPEDADDRESS} being OFF in the Seattle code but ON in the Sydney code. As it should be, -
Absolutely DO NOT EVER update UI state in a drawing event! It shouldn't be doing that at all. Get rid of it.
-
TBitmap to TBytes for ESC/POS Thermal Printer
Remy Lebeau replied to at3s's topic in RTL and Delphi Object Pascal
FMX's TBitmap has a Map() method for accessing the raw pixel data. Map() returns a TBitmapData, which has a GetScanline() method. -
Here is a 3rd option: ... IdSMTP1 := TIdSMTP.Create(nil); try IdSSLIOHandlerSocketOpenSSL1 := TIdSSLIOHandlerSocketOpenSSL.Create(IdSMTP1); IdMessage1 := TIdMessage.Create(IdSMTP1); ... finally IdSMTP1.Free; end;