views:

175

answers:

3

I am calling CreateThread from a delphi application and it seems to work, however I get a system error: code 1400, invalid window handle.

the code is as follow:

procedure TForm1.SyncFile;
var
  thr: THandle;
  thrID: DWORD;
begin
  thr := CreateThread(nil, 0, @sync, nil, 0, thrID);
  if (thr = 0) then
    ShowMessage('Error creating thread');
end;

procedure sync;
begin
   Connect;
   Application.ProcessMessages;
   SyncText;
   Disconnect;
   ExitThread(0);
end;

I tried with TerminateThread() in form1.syncfile but nop. Also tried with CloseHandle(thr) but no solution here either. I tried then the Delphi TThread but no solution.

What am I doing wrong? Code is appreciated.

+2  A: 

You shouldn't perform operations which work against a TForm (or call Application.ProcessMessages) in any thread except the main, GUI thread. The windows message pump expects all windowing operations to be done on the thread that creates the window, and no other thread.

Try to push your "work" into background threads, but leave the GUI calls on the main thread.

Reed Copsey
removing the processmessages doesn't stop this from happening. If I understood correctly, any GUI changes (form1.blah or screen.cursor) should be out of the new thread?
Mr Aleph
@Mr Aleph: Yes. I'm not sure what "SyncText" (or your other procedures) are doing, but make sure they don't touch any UI elements.
Reed Copsey
thanks, let me try
Mr Aleph
A: 

The signature of the thread function is wrong. It needs to match the one described in the documentation.

Furthermore, you should not call CreateThread directly. Call BeginThread instead. It will correctly mark your program as being multithreaded so that the memory manager knows to use proper protection for all further allocation operations.

That's all independent of anything you do in the thread. When we get to that, there are other problems. One is that you call Application.ProcessMessages. Message queues belong to specific threads. The Application object assumes it's processing the main VCL thread's message queue. When you call ProcessMessages, you're using the VCL-thread-processing code to process a different thread's message queue, and then all the assumptions that code makes about which queue it's processing are wrong. If you want to process messages in your worker threads, then you'll need to write the message-processing code yourself. Begin by reading about GetMessage, PeekMessage, and DispatchMessage. (Even if you're not using multiple threads, the presence of Application.ProcessMessages is an indication that a program has design problems. Threads just compound it.)

You don't need to call ExitThread. If your thread routine were a function like it's supposed to be, then you could just return a value like any ordinary function.

You haven't said where you get the "invalid window handle" exception. If you get it in the ProcessMessages call, then that's probably where the problem is. But if you get the error in one of the other functions you call, like Connect or SyncText, then you'll need to take a look there, too. Maybe you're accessing properties of a window that doesn't exist anymore, or maybe you're using an uninitialized variable.

Rob Kennedy
Let me see if I can absorb and understand all this. the error happens when I close the application
Mr Aleph
+1  A: 

The Windows API forbids a thread from directly working with a window handle that was created (and "belongs") to another thread.

An exception to this is the messaging system - it's perfectly valid (and indeed, expected) for a thread to Post or Send a message to a window handle owned by another thread. Indeed, this is one of the simplest, tho most basic, mechanisms for ensuring synchronised communication between threads (exploiting the UI element owner threads message queue)

Any code in a thread which ends up trying to work with a window handle (other than via messaging) that it did not create is likely to get this error (in practice, the offending WinAPI call will fail and you will only see this error if the call is followed by error checking code that raises the appropriate exception).

Since GUI objects in a Delphi application are created in the context of the main thread (unless you have jumped through some serious hoops to create UI elements in some other thread), then any code in a thread which attempts to interact with Delphi UI elements is highly likely to fall foul of this architectural feature of the WinAPI.

As others have said, the simplest approach is to ensure that only non-UI code is performed in a thread.

Deltics