Yep, a separate thread is the best(only?) solution. Threads are pretty easy.
Use the File->New->Other and create a Thread Object.
Include the thread header in your main program, then create it VCL style.
In your private section of your form, add TMyThread *MyThread;
Then in the OnCreate section for the form:
MyThread = new TMyThread(false);
Here, false means to not create the thread in a suspended state. Threads can be suspended and resumed at any time by the main VCL thread (which is usually your main form). You just call MyThread->Suspend() and MyThread->Resume(). It stops the thread no matter what it is doing, so be careful with that. You don't want to do something like that when you are querying a serial port for instance, because certain timeouts and events aren't handled properly by Windows. I don't recommend using suspend and resume.
You can get around that by creating a new thread when you need it and terminating it when you don't. Termination is another tricky one. The thread has a property, FreeOnTerminate. You don't really want to use this if you are going to make more than one thread in an application instance. I'll explain more in a minute.
The main execution of the thread is in the Execute function, which you will fill with your own execution routines. Typically, and this is left up to you, you want to put a while (!Terminated) loop in the execute function. When your main thread (the main form) calls MyThread->Terminate(), ALL it does is set the property "Terminated" to true. So your while loop will come back to the top and see that it is terminated and finally exit the thread's execute function.
The typical way to terminate and destroy the thread is like follows:
if (MyThread)
{
MyThread->Terminate();
MyThread->WaitFor(); // waits for the thread to finish the execute function.
delete MyThread;
MyThread = NULL;
}
Don't forget to always set MyThread back to NULL after deleting it.
The property "FreeOnTerminate", if it is true, will destroy the thread after it finishes the Execute function. The above code "if(MyThread)" will still be true, but pointing to an invalid location in memory. That's why I say you probably don't want to use the FreeOnTerminate in most cases, and always set MyThread back to NULL.
If MyThread needs to run code in the main form's thread, code that the main form will be running itself at some point, be careful. You need to use a function called Synchronize if you do this. The reason is, the main form could be running some function "UpdateDisplay" and then your thread comes along and tries to run the same function, and BOOM, everything goes bad. The synchronize function will wait for the main form to finish processing that method (if it currently is) and then process the thread's method. Don't want to use it on lengthy time-consuming functions though, for obvious reasons.
Hope this helps get you started!
Chris