1. Home
  2. Computing & Technology
  3. Delphi Programming
Sending messages to non-windowed applications
Page 3: Obtaining a unique message identifier. The DeallocateHWND bug.
 More of this Feature
• Page 1: How Delphi dispatches messages in windowed applications
• Page 2: How to send messages to non-windowed applications
 Join the Discussion
"Post your views, comments, questions and doubts to this article."
Discuss!
 Related Resources
• Message handing with Delphi
• Windows API and Delphi
• Threading in Delphi
• Console Delphi application

Until now, we've covered how Delphi dispatches messages in windowed applications, and how to send messages to non-windowed applications, we move on to obtaining a unique message identifier.

Obtaining a unique message identifier
As stated on the previous page, if applications were to use arbitrary message id's their operation would soon interfere and messages meant for one application would be interpreted in unknown and unwanted ways by others. To prevent this Windows provides a way for each application or related applications to obtain a unique message identifier.

var
  WM_SHUTDOWN_THREADS: Cardinal;

procedure TfrmSgnThreads.FormCreate(Sender: TObject);
begin
  WM_SHUTDOWN_THREADS := RegisterWindowMessage('TVS_Threads');
end;

We pass a unique string identifier to the RegisterWindowMessage and it returns a message identifier that is guaranteed to be unique across a Windows session.

The application
All that remains is to put all the above code together. You can download the full source code.

To test the application we create a few threads by pressing the New Thread button, then notice how all of them correctly receive the WM_SHUTDOWN_THREADS message when we press the Send Signal button. To view the internal flow of code we printed a message when each thread is created and another message when the thread receives the message and is destroyed.

Everything works as expected. But as the threads and the hidden windows are destroyed we will soon notice a lot of exceptions popping up.

DeallocateHWND bug
We tried to narrow down the source of the problem. From the exception message we concluded there's probably a reference to unallocated memory at some point. The only places where we deallocate memory are when the threads themselves are destroyed and when the hidden window is destroyed. First we moved the DeallocateHWND call to the Execute procedure and comment the FreeOnTerminate line, so the threads don't destroy automatically:

procedure TTestThread.Execute;
begin
  { FreeOnTerminate := True; }
  while NOT FSignalShutdown do
  begin
    Sleep(10);
  end;
  Synchronize(PrintMsg);
  { destroy the hidden window and free up memory }
  DeallocateHWnd(FWinHandle);
end;

The errors still show up. So it must be something related to the DeallocateHWND call so we look at the Delphi DeallocateHWnd implementation which is in the Classes unit:

procedure DeallocateHWnd(Wnd: HWND);
var
  Instance: Pointer;
begin
  Instance := Pointer(GetWindowLong(Wnd, GWL_WNDPROC));
  DestroyWindow(Wnd);
  if Instance <> @DefWindowProc then FreeObjectInstance(Instance);
end;

At first glance everything looks fine. The window is destroyed and the memory occupied by our window procedure is freed. But... after we free up the memory occupied by our window procedure a few messages are still routed to the hidden window (yes, there are still messages routed to that window, including a bunch of WM_DESTROY and its relatives). So Windows will try to reference an unallocated memory space when trying to execute our window procedure and thus an exception will pop up. The solution we have found is to slightly alter the DeallocateHWnd code to change back the window procedure to the default one provided by Windows before we free up the memory for the code.

procedure TTestThread.DeallocateHWnd(Wnd: HWND);
var
  Instance: Pointer;
begin
  Instance := Pointer(GetWindowLong(Wnd, GWL_WNDPROC));
  if Instance <> @DefWindowProc then
  begin
    { make sure we restore the default 
      windows procedure before freeing memory } 
    SetWindowLong(Wnd, GWL_WNDPROC, Longint(@DefWindowProc));
    FreeObjectInstance(Instance);
  end;
  DestroyWindow(Wnd);
end;

You can download the full source code of the application with the fix here. We notice that all errors are gone and conclude the error was indeed in the DeallocateHWnd code.

If you have any questions or comments I would like to receive them in the Delphi Programming Forum and I'll try to answer all to the best of my knowledge and abilities.

First page > How Delphi dispatches messages in windowed applications > Page 1, 2, 3

Explore Delphi Programming
About.com Special Features

Stay connected and entertained with reviews on tips on the latest HDTVs, cellphones and more. More >

Easy ways to connect two computers for networking purposes. More >

  1. Home
  2. Computing & Technology
  3. Delphi Programming

©2009 About.com, a part of The New York Times Company.

All rights reserved.