Jump to content

Remy Lebeau

Members
  • Content Count

    2337
  • Joined

  • Last visited

  • Days Won

    95

Everything posted by Remy Lebeau

  1. Remy Lebeau

    QueryPerformanceCounter precision

    See https://hero.handmade.network/forums/code-discussion/t/7485-queryperformancefrequency_returning_10mhz_bug Also of interest, see MSDN: https://docs.microsoft.com/en-us/windows/win32/sysinfo/acquiring-high-resolution-time-stamps
  2. It can be passed in as a normal parameter, and even used in ClassType comparisons, but it can't be used for type-casts, eg: function CreateAnObjectByClassName(const ClassName: string; BaseClass: TPersistentClass): TPersistent; var Cls: TPersistentClass; begin Cls := GetClass(ClassName); if not Cls.InheritsFrom(BaseClass) then raise Exception.Create('...'); Result := BaseClass(Cls).Create; // <-- doesn't work end; The compiler needs to know the actual class type that exposes the necessary constructor in order to call it directly. Otherwise, you have to resort to using RTTI to call the constructor, as demonstrated in the code Stefan linked to. There are alternative ways to accomplish that, just not as clean as what you are thinking.
  3. That is because neither TObject, TPersistent, nor TInterfacedObject have a virtual constructor that your descendants can override. The lowest RTL class that introduces a virtual constructor is TComponent. You need to add a virtual constructor to TMyClass and have desccendants override it as needed. And then declare your own metaclass type for your creation function to use. For example: type TMyClass = class(TInterfacedPersistent, IMyInterface) public constructor Create; virtual; end; TMyClassType = class of TMyClass; function CreateAnObjectByClassName(const AClassName: string): TPersistent; begin Result := TMyClassType(GetClass(AClassName)).Create; end; ... type TMyClass1 = class(TMyClass) public constructor Create; override; end; TMyClass2 = class(TMyClass) public constructor Create; override; end; RegisterClasses([TMyClass1, TMyClass2, ...]);
  4. Nope. There is no method of TRttiContext that takes an object instance as input. FindType() takes in a class type as a string, and GetType() takes in a class type as a TClass or PTypeInfo.
  5. Remy Lebeau

    TIdHTTP to TNetHTTPRequest

    In what way exactly? Rather than someone translating your TIdHTTP code, can you show your existing TNetHTTPRequest code that is not working for you?
  6. Remy Lebeau

    Where I can find the SSL DLLs for Indy?

    https://github.com/IndySockets/OpenSSL-Binaries https://www.indyproject.org/2020/06/16/openssl-binaries-moved-to-github/
  7. Remy Lebeau

    TMemo and unicode

    The code shown is not the correct logic to parse an RFC2047-encoded string. Try something more like the instead (though this could be simplified using IdGlobal.Fetch(), for instance): uses IdGlobal, IdGobalProtocols, IdCoderMIME, idCoderQuotedPrintable; procedure TForm1.convertbuttonclick(Sender: TObject); var s, s2, charset, encoding, data : string; i, j: Integer; begin s := Memo1.Lines[0]; s2 := s; i := Pos('=?', s); if i > 0 then begin Inc(i, 2); j := Pos('?', s, i); if j > 0 then begin charset := Copy(s, i, j-i); i := j+1; j := Pos('?', s, i); if j > 0 then begin encoding := Copy(s, i, j-i); i := j + 1; j := Pos('?=', s, i); if j > 0 then begin data := Copy(s, i, j-i); if TextIsSame(encoding, 'B') then s2 := idDecoderMIME.DecodeString(data, CharsetToEncoding(charset)) else if TextIsSame(encoding, 'Q') then s2 := idDecoderQuotedPrintable1.DecodeString(data, CharsetToEncoding(charset)); end; end; end; end; Memo2.Lines.Clear; Memo2.Lines.Add(s2); end; That being said, Indy already implements a decoder for RFC2047-encoded strings, in the DecodeHeader() function of the IdCoderHeader unit, eg: uses IdCoderHeader; procedure TForm1.convertbuttonclick(Sender: TObject); var s, s2 : string; begin s := Memo1.Lines[0]; // '=?UTF-8?B?5aaC5L2V6K6TIGFydC1tYXRlIOaIkOeCug==?=' s2 := DecodeHeader(s); Memo2.Text := s2; end;
  8. Remy Lebeau

    Smtp avoid spam classification

    It is hard to diagnose your issue without seeing the raw email data, and/or your code that is creating the email, and even knowing which version of which Pascal compiler you are using (Delphi vs FreePascal), since string handling varies by compiler and even by version. I can't answer that with the limited details you have provided so far. For instance, if you are using a Unicode version of Delphi (2009+ or later) or FreePascal (3.0+ with UnicodeStrings enabled), then yes, it should be converting to UTF-8 property. But if you are using a pre-Unicode version of Delphi/FreePasscal, then no, it will not convert to UTF-8 automatically, so you are responsible for encoding your own strings to UTF-8 before giving them to Indy. What does your actual text look like? This score implies that you have a lot of script changes happening inside of individual words. Which might just be a side effect of UTF-8 not being encoded properly. Hard to say without seeing the raw data. What do you have the TIdMessage.Encoding property set to? It is set to meDefault by default, which will be auto-updated to either mePlainText or meMIME depending on whether the TIdMessage.MessageParts collection is empty or not, respectively. A 'MIME-Version' header is generated only when TIdMessage.Encoding is meMIME. In general, you don't need to use MIME if your email contains only plain text, but it sounds like the provider may be requiring MIME. So, you may need to force the TIdMessage.Encoding to meMIME. UTF-8 is an 8-bit encoding, so it requires a transfer encoding that supports 8-bit data. '7bit' will truncate/zero the high bit of each byte, thus corrupting non-ASCII characters. So, you need to set the TIdMessage.ContentTransferEncoding property (and the TIdMessagePart.ContentTransfer property in entries of the TIdMessage.MessageParts collection) to either '8bit', 'base64', or 'quoted-printable' when using UTF-8 text to avoid any potential data loss. Note that '8bit' emails do require additional support from an SMTP server (features which Indy does not currently implement), so use 'base64' or 'quoted-printable', which are compatible with 7-bit systems. If the text is mostly ASCII characters, I would use 'quoted-printable'. If the text is mostly non-ASCII characters, I would use 'base64' instead.
  9. Remy Lebeau

    problem with file attributes

    Then you likely did not create the file where you are expecting. Are you using relative paths, for instance? What is the actual value of your fn variable? The filesystem does not lie about file existence. If it says a file does not exist, then it really does not exist. Double-check your input. With the same ERROR_FILE_NOT_FOUND error? I suggest you use SysInternals Process Monitor to see EXACTLY which file path your app is REALLY trying to access, and make sure it matches your expectation.
  10. Remy Lebeau

    problem with file attributes

    Then you likely don't have access to the file, or at least don't have permissions to ask the filesystem for its attributes. You need to call GetLastError() to find out why GetFileAttributes() is failing.
  11. Remy Lebeau

    problem with file attributes

    GetFileAttributes() can return INVALID_FILE_ATTRIBUTES for a number of different reasons, not just because the file does not exist. You have to use GetLastError() to differentiate the actual cause.
  12. Remy Lebeau

    IdHttp not connect IP Camera error 401

    I suggested EITHER approach, not BOTH. Use either BASIC or DIGEST. Did you verify that TIdHTTP is actually trying to send BASIC/DIGEST authentication in its request? I'm guessing either you are not setting the TIdHTTP.Request.Username/Password correctly (are you ABSOLUTELY SURE you are using the correct credentials? Are there any non-ASCII characters present in them?), or the camera is not accepting Indy's flavor of DIGEST authentication (BASIC is very, well, basic and difficult to get wrong). Can you provide an example showing the raw HTTP messages from start to end?
  13. Until a few months ago, I also depended on BCB6 for my full-time job (my software has reached its End-Of-Life, so this is no longer an issue), but in the past 15-odd years, moving it into a VM was never really an option. The physical machine it ran in was over 500GB of logs, databases, backups, etc, so cloning it would have taken forever. But more importantly, it had specialized PCI hardware installed that didn't work inside a VM, and I never had the time to create a new VM with just BCB6 installed. I would have had to continue running my apps on the physical machine, but used the remote debugger inside a VM, and I wasn't too keen on that approach, either,
  14. Remy Lebeau

    IdHttp not connect IP Camera error 401

    Your camera supports BASIC and DIGEST authentications. By default, TIdHTTP enables only BASIC authentication, which you are explicitly disabling by setting the TIdHTTP.Request.BasicAuthorization property to False. You need to either: re-enable BASIC authentication by setting TIdHTTP.Request.BasicAuthorization to True. enable DIGEST authentication by adding the IdAuthenticationDigest, or IdAllAuthentications, unit to your Form's uses clause.
  15. Remy Lebeau

    Delete a Registry Key...

    KEY_ALL_ACCESS is meant for admins only. You should specify only the exact rights you actually need (which FYI, TRegistry.DeleteKey() also requires KEY_QUERY_VALUE, since it calls RegQueryInfoKey() to determine the number of subkeys and the maximum subkey name length), eg: reg := TRegistry.Create(_DELETE or KEY_ENUMERATE_SUB_KEYS or KEY_QUERY_VALUE); Otherwise, just use the Win32 RegDeleteTree() or SHDeleteKey() function instead of TRegistry.DeleteKey(), and let the OS handle the necessary recursion.
  16. Correct. There is no Win32 API to enumerate and query existing tray icons. In the old days, the Tray was a simple toolbar that you could enumerate with standard Toolbar messages, but that would not get you any details about the app/window associated with each icon. With the redesign of the Taskbar and Tray in Windows 7 onward, I don't think that is doable anymore, or at least not 100%, due to the user's ability to hide icons.
  17. Remy Lebeau

    Close a TIdTCPClient connection

    No, they are not. All you really need is Disconnect(), or at least Socket.Close() if closing the socket from another thread. That is not an Indy error message, or any OS error message I have ever seen. So I am assuming you mean it is coming from the Barcoder Reader or Ethernet base (what is that supposed to be?) instead. Does the reader/base only allow 1 connection at a time? If you just yank out the network cable, it won't matter what you do on the client side, the connection will be lost on the server side and it will need time to process that loss, which may take awhile, depending on the server's implementation. Disconnect()/Close() will perform a graceful disconnect (send a FIN packet to notify the peer that the connection is being closed intentionally). That should allow the reader/base to free up the used slot immediately. However, it won't matter what you do in the case of the connection being lost abnormally, like from a network outage. All you can do in that case is wait for the reader/base to handle the loss on its end in its own time to make the slot available again.
  18. Maybe possible, but not easily. Once upon a time, you could use a WH_CALLWNDPROC hook to intercept WM_COPYDATA messages being sent to the Taskbar's "Shell_TrayWnd" window, and then extract the NOTIFYICONDATA info from those messages. But I'm not sure if that still applies anymore, what with all of the changes Microsoft has made to the Taskbar's system tray icon handling in the past decade.
  19. Remy Lebeau

    Install recent Delphi versions on Windows XP

    In that case, I don't recall offhand the last version that officially supported XP as a target platform. In the current DocWiki, versions prior to 10.1 Berlin do not say the specific Windows versions they target. 10.1 says Windows 7 SP1+, Windows 8-10, and Windows Server 2008-2012. I do know that there are a few areas of the modern RTL/VCL that make use of newer APIs that don't exist on XP, so 10.x-compiled executables are not likely to run as-is on XP.
  20. Remy Lebeau

    Thread Issues: using resume and start

    If the sole purpose of the thread is just to delay a UI notification, then TThread.ForceQueue() would be better, no thread needed at all: procedure THeaderFooterForm.Button1Click(Sender: TObject); begin TThread.ForceQueue(nil, procedure begin HeaderLabel.Text := 'My Job'; end ); end;
  21. Remy Lebeau

    Thread Issues: using resume and start

    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.
  22. Remy Lebeau

    Thread Issues: using resume and start

    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.
  23. Remy Lebeau

    Can't load LivePreview270.bpl

    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.
  24. Remy Lebeau

    Using Events when creating a component??

    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.
  25. Remy Lebeau

    [FMX][Android]How get control name under the click?

    Why are you simply not using the Sender of the click event? procedure TMyForm.MyControlClick(Sender: TObject); begin ShowMessage(TControl(Sender).Name); end;
×