Jump to content

Remy Lebeau

Members
  • Content Count

    3000
  • Joined

  • Last visited

  • Days Won

    135

Everything posted by Remy Lebeau

  1. The event is signaled when TerminatedSet() is called. TerminatedSet() is called by the Terminated property setter. If the thread is still running when the TThread object is being destroyed, the inherited destructor will set the Terminated property and wait for the thread to finish running.
  2. Several of Embarcadero's systems have been down since Friday night, including QualityPortal, DocWiki, GetIt, Blogs, etc.
  3. That means you are creating the TTimer in the main thread to begin with, not in the background thread. That's exactly how TTimer already works. Its constructor creates a hidden window in the calling thread, which when activated will then receive timer messages from that same thread. So the thread that creates the TTimer must have a message loop to receive and dispatch those timer messages. So, you might think about moving the creation of the TTimer into the background thread's Execute() method, and that may work 99% of the time, but know that the creation of that hidden window is not done in a thread-safe manner, so you really should not be using TTimer in a background thread at all. If you really need a thread-based timer, you could just use the Win32 timeSetEvent() function, which is a multimedia timer that runs its callback in a background thread that the OS manages, If you really want to use a custom TThread class, then using TEvent is the simplest option that allows you to terminate the thread on demand, eg: uses ..., Classes, SyncObjs; type TTimerThread = class (TThread) private fEvent: TEvent; fInterval: LongWord; fOnElapsed: TNotifyEvent; protected procedure Execute; override; procedure TerminatedSet; override; public constructor Create(AInterval: LongWord; AOnElapsed: TNotifyEvent); reintroduce; destructor Destroy; end; constructor TTimerThread.Create(AInterval: LongWord; AOnTimer: TNotifyEvent); begin inherited Create(False); fInterval := AInterval; fOnElapsed := AOnElapsed; fEvent := TEvent.Create; end; destructor TTimerThread.Destroy; begin fEvent.Free; inherited Destroy; end; procedure TTimerThread.TerminatedSet; begin fEvent.SetEvent; end; procedure TTimerThread.Execute; begin while fEvent.WaitFor(fInterval) = wrTimeout do fOnElapsed(Self); end;
  4. Remy Lebeau

    Thread Destroy with no corresponding Thread Create?

    Why is the code creating the TStringStream object in the constructor and destroying it in the destructor, instead of just using it locally inside of Execute()? Do other threads need access to the TStringStream? If not, then it doesn't need to be a class member.
  5. Remy Lebeau

    docwiki

    Their system went down late Friday night, multiple servers were affected, including DocWiki, Blogs, QualityPortal, etc. Last I heard, they are still working on resolving the problem.
  6. How exactly are you attempting to open the file? Are you getting an error? If so, what is it? I'm no Android expert, but outside of a public folder, I think Android does not allow two apps to share files with each other unless both apps cooperate with each other, ie the providing app has to grant access to the receiving app. And since the providing app in this case is no longer available, you might be SOL. 🤷
  7. Remy Lebeau

    What is otares?

    Why? What are you planning on doing with otares files if you could get them generated? Delphi doesn't use otares files anymore, it's a legacy thing only.
  8. Remy Lebeau

    Help with 'Continue' pls.

    No. I just knew the feature supposedly exists. Ouch! And not even opened for review yet....
  9. Remy Lebeau

    Help with 'Continue' pls.

    Nope. Eof simply evaluates the current record, it does not move to another record. Hence the need to call Next() explicitly. Yes.
  10. Remy Lebeau

    Extend Standard VCL

    In a word, no. However, if you intend to use the extended component in just one form/project, and don't mind setting the new properties in code at runtime rather than with the Object Inspector at design-time, then you could use an interposer class to add the properties, and that way you don't have to make the extra effort of putting the new component in a new package and installing it into the IDE. For example: unit MyUnit; interface uses ..., Vcl.Forms, Vcl.StdCtrls, System.IniFiles, ...; type TCheckBox = class(Vcl.StdCtrls.TCheckBox) public IniSection: string; IniProperty: string; procedure LoadFromIni(Ini: TCustomIniFile); procedure SaveToIni(Ini: TCustomIniFile); end; TMyForm = class(TForm) SomeCheckBox: TCheckBox; procedure FormCreate(Sender: TObject); ... private procedure LoadConfig; procedure SaveConfig; end; ... implementation ... procedure TCheckBox.LoadFromIni(Ini: TCustomIniFile); begin Checked := Ini.ReadBool(IniSection, IniProperty, False); end; procedure TCheckBox.SaveToIni(Ini: TCustomIniFile); begin Ini.WriteBool(IniSection, IniProperty, Checked); end; procedure TMyForm.FormCreate(Sender: TObject); begin SomeCheckBox.IniSection := ...; SomeCheckBox.IniProperty := ...; end; procedure TMyForm.LoadConfig; var Ini: TIniFile; begin ... SomeCheckBox.LoadFromIni(Ini); ... end; procedure TMyForm.SaveConfig; var Ini: TIniFile; begin ... SomeCheckBox.SaveToIni(Ini); ... end;
  11. Remy Lebeau

    OpenSSL Commands

    I updated my earlier example. Make sure CmdLine is not pointing at a string literal when passing it to CreateProcess(), as the 2nd parameter of the Unicode version of CreateProcess() (CreateProcessW) is not allowed to point at read-only memory.
  12. Remy Lebeau

    OpenSSL Commands

    Use the Win32 CreateProcess() function. Of course, a better option might be to NOT shell out to openssl.exe at all, but to use the OpenSSL API directly in your own code instead. It really depends on what you are trying to accomplish exactly. Only if you execute them as parameters to cmd.exe. Then you are doing something wrong, but we can't see what you are actually doing. Please show your actual code you are having trouble with. For example: uses ..., Windows; procedure RunCommandLine(CmdLine: String); var si: TStartupInfo; pi: TProcessInformation; begin {$IFDEF UNICODE}UniqueString(CmdLine);{$ENDIF} ZeroMemory(@si, SizeOf(si)); si.cb := SizeOf(si); if not CreateProcess(nil, PChar(CmdLine), nil, nil, False, 0, nil, nil, si, pi) then RaiseLastOSError; ... CloseHandle(pi.hThread); CloseHandle(pi.hProcess); end; ... RunCommandLine('C:\path\openssl.exe version'); Just note that openssl.exe is a console app, so if you run this code in a non-console program, it will create a new console that will close when openssl.exe exits. If you actually want to see the console window to read the output, or even to capture the output into your own code, requires more work than this code shows.
  13. Remy Lebeau

    Overview of the generated files after build/compile

    Feel free to file a feature request with Embarcadero
  14. Remy Lebeau

    Overview of the generated files after build/compile

    There is no such list generated by the IDE/compiler. And the only documentation that I know of which talks about generated files is: File Types Index File Extensions of Files Generated by RAD Studio
  15. Remy Lebeau

    How can I make TTimer run in the main tread ?

    Then TTimer is not a good choice, as it is not a real-time timer, it is a message-based timer, and so is subject to the speed of the message queue. And also, the WM_TIMER message is low-priority and only generated when there are no other messages pending. You should use an actual multimedia timer instead. And don't use PlaySound() when high performance is needed. Pre-prepare the audio sample ahead of time and use a multimedia API to play the sample when needed. Such as waveOutWrite(), or more modern audio APIs like WASAPI, DirectSound/XAudio2, etc.
  16. Remy Lebeau

    D11.3 Surfaces a Bug in 8-Year-Old Code that reads DBF file...

    @Steve Maughan you might want to read: Converting 32-bit Delphi Applications to 64-bit Windows since the original code was doing things that were not meant to be done in 64bit builds to begin with.
  17. Remy Lebeau

    Delphi 12 IDE: unchecking a component is NOT SAVED

    You can update the "HKCU\SOFTWARE\Embarcadero\BDS\23.0\Known Packages" Registry key. Find the entry for the package, and either: modify the entry's Data value to prefix it with an underscore ("_"). remove the entry (optionally move it to the "HKCU\SOFTWARE\Embarcadero\BDS\23.0\Disabled Packages" key)
  18. Remy Lebeau

    GetWindowHandle + Ctrl V

    If your app actually needs admin rights, then you should add an application manifest to your project to specify its requestedExecutionLevel at compile-time, not change the properties of the EXE file after the compile is finished. Modern Delphi versions even have a project setting for that very purpose: http://docwiki.embarcadero.com/RADStudio/en/Manifest_File http://docwiki.embarcadero.com/RADStudio/en/Application_Manifest Basically, what you did for your app, but for the IDE instead, so it runs as an admin, and then any project it runs will also run as an admin.
  19. You should NEVER stream pointers from one process' address space into another process' address space, unless both processes are running concurrently and one process needs to directly access the other process's memory via the ReadProcessMemory() and/or WriteProcessMemory() APIs (using shared memory would be better, though). Otherwise, just don't do it! Stream offsets instead. And in the example given, a linked list can certainly be streamed using offsets instead of pointers. The actual pointers would only be meaningful in the memory of the process that originally creates the list, and in the process that loads the stream into a new list in it own memory. Pointers are fairly meaningless when passed around from one process to another.
  20. Remy Lebeau

    GetWindowHandle + Ctrl V

    Do not use keybd_event(), use SendInput() instead. Only if you have permission to access the other app. Into a simple app like Notepad, yes. But given that Kaspersky is a security app, I doubt they want people to automate it like this, so the approach you are attempting will likely never work with it.
  21. Remy Lebeau

    indy https error 10061

    Are the client and server on the same LAN? If not, then there could be other AVs/Firewalls present in between the client and server as the connection jumps from one network to another. The only thing that makes sense given what you have described is if there is a local AV/Firewall running on the client machine that is blocking the connections from TIdHTTP directly, but has an exception which allows Fiddler to make connections. You say your TIdHTTP code is in a DLL, what app is loading that DLL?
  22. Remy Lebeau

    indy https error 10061

    Rather than using the ProxyParams (which affects TIdHTTP's internal logic in how it handles HTTP/S requests), I would suggest using a packet sniffer like Wireshark to see the actual IP/Port that TIdHTTP is trying to connect to for a normal non-proxy request, and see whether that connection is actually going out over the network. The main causes of error 10061 are: the target IP is reachable by the client, but there is no TCP server running on the target Port there is a TCP server running on the target IP/Port, but it has too many pending connections in its backlog the connection is being blocking by an AV/firewall that is located anywhere on the network(s) between your client and target server. Unfortunately, there is no way to know on the client side what the root cause of the error actually is, so the only thing the client can do is catch the error and retry the request, preferably after a short delay, in case the error is temporary.
  23. Remy Lebeau

    How do I execute FFMPEG from inside a VCL App?

    I would not suggest putting it all on the OS to run that entire command in one go. You should break it up into steps and then use appropriate APIs to run each step directly in your code, eg: uses ..., System.SysUtils; procedure TForm1.act_MUSIC_CONVERSION_FLACStoMP3Execute(Sender: TObject); var dir: string; sr: TSearchRec; begin dir := IncludeTrailingPathDelimiter(FileListBox1.Directory); if FindFirst(dir + '*.flac', faAnyFile, sr) <> 0 then try repeat if (sr.Attr and faDirectory) = 0 then begin ShellExecute(0, nil, 'ffmpeg.exe', PChar('-i "' + sr.Name + '" -ab 320k -map_metadata 0 -id3v2_version 3 "' + sr.Name + '.mp3"'), PChar(dir), SW_HIDE); end; until FindNext(sr) <> 0; finally FindClose(sr); end; end; Alternatively: uses ..., System.SysUtils, System.IOUtils; procedure TForm1.act_MUSIC_CONVERSION_FLACStoMP3Execute(Sender: TObject); var dir, fileName: string; begin dir := IncludeTrailingPathDelimiter(FileListBox1.Directory); for fileName in TDirectory.GetFiles(dir, '*.flac') do ShellExecute(0, nil, 'ffmpeg.exe', PChar('-i "' + fileName + '" -ab 320k -map_metadata 0 -id3v2_version 3 "' + fileName + '.mp3"'), PChar(dir), SW_HIDE); end; Yes, its' more work, but it gives you much more control.
  24. There is nowhere you can "put the app" that will make the OS run your app during sleeps/wakeups. Your app needs to already be running in order to catch those events in real-time in your app's code. As for Startup specifically, you could use the "Startup" group in the Start Menu, or the "HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" key in the Registry, to run your app during startup/logon. There are also startup/shutdown and logon/logoff policy scripts that you can edit to run your app: Working with startup, shutdown, logon, and logoff scripts using the Local Group Policy Editor
  25. Also, because SetDialogParams() is taking a TTaskDialogParams by value, a copy is made, which will have its own TStringList object, so you must implement the Assign() operator as well to copy the source record's TStringList data to the copied record after it is Initialized: class operator TTaskDialogParams.Assign(var Dest: TTaskDialogParams; const [ref] Src: TTaskDialogParams); begin Dest.CustomButtons.Assign(Src.CustomButtons); end; class operator TTaskDialogParams.Finalize(var Dest: TTaskDialogParams); begin Dest.CustomButtons.Free; end; class operator TTaskDialogParams.Initialize(out Dest: TTaskDialogParams); begin Dest.CustomButtons := TStringList.Create; end; Otherwise, if you don't do this, the default assignment behavior will copy the object pointer instead, causing the copied record to lose its own TStringList object (leaking it) and end up pointing to the original TStringList object, so when the copied record is Finalized then the original TStringList object gets freed, corrupting the original record that was copied from. That being said, there is no good reason to pass the record by value in the code shown. SetDialogParams() should take the record by const reference instead, then no copy is made. Read the documentation, especially the section on "Passing Managed Records as Parameters" : https://docwiki.embarcadero.com/RADStudio/en/Custom_Managed_Records
×