I see a few problems with your code.
In general you cannot access UI controls from a background thread, but you pass ListBoxPrograms.Items as the Buffer parameter to the thread's constructor. This can work if the Add method of ListBoxPrograms.Items is implemented by sending a message to the listbox control, since the OS makes sure a message is delivered in the thread that created the control's window handle. On the other hand the VCL creates a control handle on an as needed basis, and if the ListBoxPrograms.Handle has not been created yet when the thread tries to add the first item the handle would be created in the background thread context, which would not work since this thread has no message loop.
So: Always access UI controls only in a Synchronized or Queued method! You did not show code for the ListFilesDir method, so perhaps you are doing that already.
The second point to investigate is how to safely interrupt the thread's work if it has to be killed before it has completed its work. The TThread Destructor calls Terminate and then waits for the thread to die gracefully (by leaving the Ececute method). For this to work as expected the thread's work loop has to check the Terminated property on each round and exit directly if it is true. Again this is something you would do in ListFilesDir.
Then there is the question of error handling in the thread's work loop. The usual pattern is to wrap the whole body of the Execute method in a try except block and either pass a trapped exception to the main thread for reporting to the user via a Synchronized method (not a queued one!), or use the Windows.MessageBox function to report it directly. That API is thread-safe, while the VCL.Dialogs methods are not.