Link to home
Start Free TrialLog in
Avatar of pswenson
pswenson

asked on

Application not closing when Windows shut down

I have an application that runs in the tray, it has a hidden main window with a pop-up menu and a tray icon.  Everything runs fine expect when I go to shut down windows it will not shut down.  I don't know which message windows sends at shut down, but I tried handling (via Applicaiton.OnMessage) WM_CLOSE, WM_QUIT, WM_ENDSESSION and calling Application.Terminate for each.  I put in a showmessage so I would know if the message was received. I never got the showmessage to work, so I think the application is simply not processing Windows messsages propertly.  The inconsistent thing is I have a 'terminate' option on my popup menu that appears when you right click on the tray icon.  If I try to close the app this way, it works fine.  Anyone know why my app won't process messages?
Avatar of ZifNab
ZifNab

Hi pswenson,

First, read this article and be sure you work that way to make your tray application. If the problem persists send us some code so we can search for a possible cause.

http://www.delumpa.com/tips/apps/apps3.htm

Regards, Zif.
Avatar of pswenson

ASKER

Upon further testing I think the tray has nothing to do with the problem.  The app creates some TThread descendents that have to be terminated when the project is shut down.  If I don't create the Threads, it shuts down fine.  I also wrote a test app that sends my main app a WM_CLOSE or a WM_QUIT message, both of which shut it down properly (although I notice WM_QUIT shuts it down faster).  I ran WinSpecter to see what messages get sent to the app at shut-down and didn't see any.  So I guess that leads me to the question "how does windows shut down the apps when you click start/restart or start/shutdown?"
Windows sends WM_QUIT messages to all app when shutting down.

Cheers,

Raymond.
Windows send the "QueryEndSession" message followed by "EndSession". if your app doesn't close for the "EndSession" Windows will then display that dreaded "application not responding - Wait, Kill or cancel" mesage.

When you receive the WM_EndSession, check the wparam to make sure that windows is closing.
I don't see ANY messages begin sent to my app with WinSpector at shut-down.  I can see those messages during a normal run though.  I can send an END_SESSION or WM_QUIT message normally from a little test app and shut down my app.  During shut-down it just doesn't work!  I always get the "application not responding" message.  I don't get it!
pwenson, maybe some code would be helpfull?
Try something like this...

OnMessage()
begin
  case Msg of
    WM_QUIT, WM_CLOSE, WM_QUERYENDSESSION, WM_ENDSESSION :            Application.Terminate;
  end;
end;

Cheers,
Viktor†
I tried Viktornet's solution, it didn't work...

The purpose of the project is to use Win NT file notification to automatically convert DFMs to text files when they are written to disk (and vice versa).  The app should stay down in the tray and have a popup that lets the user bring up a config screen, force conversion on a particular directory or file, and terminate.  Everything works great except when you shut windows down.

The code uses several components but I will post most of it.  The main form creates a bunch of datamodules that use components encapsulating TThread descendents}
unit frm_Main;
{===================================================================================================
  Description:  Main form for DFM Converter
  Usage: Hidden form, never interacted with except thru the TrayIcon's pop-up menu
  Copyright© 1998 Corporate Express, Inc.

  Keywords:
  $Author: Phil Swenson$
  $Date: 11/24/98 10:50:36 AM$
  $Workfile: C:\Prj\Dev\DFM Converter\D4\src\frm_Main.pas$
  $Project: DFM Converter$
  $Revision: 4$
  $Folder: src$
===================================================================================================}
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Menus, TrayIcon,
  dlg_Properties, dlg_ForceConvert, StdCtrls, ExtCtrls, ThreadedTimer, uDFMConverterGlobals
  {$IFDEF DEBUG}, RzCSIntf{$ENDIF};

type

  TfrmMain = class(TForm)
    TrayNotifyIcon: TTrayNotifyIcon;
    PopupMenu: TPopupMenu;
    menuProperties: TMenuItem;
    menuTerminate: TMenuItem;
    menuForceConvert: TMenuItem;
    procedure menuPropertiesClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure menuTerminateClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure menuForceConvertClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
    Log: TStringList;
    DirectoryWatchList: TList;
    dlgProperties: TdlgProperties;
    dlgForceConvert: TdlgForceConvert;
    function GetTextExtension: string;
    procedure LoadConfigData;
    procedure StartWatch(const sDirectoryToWatch, sTextExtension: string; const bWatchSubTree);
    procedure FreeAllWatches;
    procedure EnableAllWatches;
    procedure DisableAllWatches;
    procedure ShowPropertiesDialog;
    procedure ForceConvert;
    procedure MsgHandler(var Msg: TMsg; var Handled: Boolean);
  public
    { Public declarations }
  end;

var
  frmMain: TfrmMain;

implementation
uses dm_DirectoryWatcher, uDFMConverterPersist;
{$R *.DFM}
procedure TfrmMain.FormCreate(Sender: TObject);
begin
  TrayNotifyIcon.IconVisible:= True;
  DirectoryWatchList:= TList.Create;
  Log:= TStringList.Create;
  LoadConfigData;
  dlgProperties:= nil;
  dlgForceConvert:= nil;
  Application.OnMessage:= MsgHandler;
end;

procedure TfrmMain.MsgHandler(var Msg: TMsg; var Handled: Boolean);
begin
  case Msg.Message of
    WM_QUIT, WM_CLOSE, WM_QUERYENDSESSION, WM_ENDSESSION:
      Application.Terminate;
  end;    
end;

procedure TfrmMain.menuPropertiesClick(Sender: TObject);
begin
  ShowPropertiesDialog;
end;

procedure TfrmMain.ShowPropertiesDialog;
begin
  if Assigned(dlgProperties) then
  begin
    dlgProperties.BringToFront;
    Exit;
  end;
  dlgProperties:= TdlgProperties.Create(nil);
  try
    if dlgProperties.ShowModal = mrOK then
      LoadConfigData;
  finally
    try
      dlgProperties.Free;
    finally
      dlgProperties:= nil;
    end;
  end;
end;

procedure TfrmMain.LoadConfigData;
var
  iCount: Integer;
  DirectoryList: TDirectoryArray;
  ConverterPers: TDFMConverterPersistentData;
  DirectoryData: TDirectoryData;
  sTextExtension: string;
begin
  ConverterPers:= TDFMConverterPersistentData.Create;
  try
    FreeAllWatches;
    if not ConverterPers.ConverterEnabled then Exit;
    sTextExtension:= ConverterPers.TextFileExtension;
    //ConverterPers.LogFileEnabled;
    //ConverterPers.LogFilePath;
    DirectoryList:= ConverterPers.DirectoryList;
  finally
    ConverterPers.Free;
  end;

  for iCount:= Low(DirectoryList) to High(DirectoryList) do
  begin
    DirectoryData:= DirectoryList[iCount];
    if DirectoryData.DirectoryToWatch <> '' then
      StartWatch(DirectoryData.DirectoryToWatch, sTextExtension, DirectoryData.WatchSubTree);
  end;
end;

procedure TfrmMain.StartWatch(const sDirectoryToWatch, sTextExtension: string; const bWatchSubTree);
var
  dmDirectoryWatcher: TdmDirectoryWatcher;
begin
  dmDirectoryWatcher:= TdmDirectoryWatcher.Create(nil);
  DirectoryWatchList.Add(dmDirectoryWatcher);
  with dmDirectoryWatcher do
  begin
    DirectoryToWatch:= sDirectoryToWatch;
    TextExtension:= sTextExtension;
    Enabled:= True;
  end;
end;

procedure TfrmMain.FreeAllWatches;
var
  dmDirectoryWatcher: TdmDirectoryWatcher;
  iCount: Integer;
begin
  for iCount:= DirectoryWatchList.Count - 1 downto 0 do
  begin
    dmDirectoryWatcher:= DirectoryWatchList[iCount];
    dmDirectoryWatcher.Free;
  end;
  DirectoryWatchList.Clear;
end;

procedure TfrmMain.DisableAllWatches;
var
  dmDirectoryWatcher: TdmDirectoryWatcher;
  iCount: Integer;
begin
  for iCount:= DirectoryWatchList.Count - 1 downto 0 do
  begin
    dmDirectoryWatcher:= DirectoryWatchList[iCount];
    dmDirectoryWatcher.Enabled:= False;
  end;
end;

procedure TfrmMain.EnableAllWatches;
var
  dmDirectoryWatcher: TdmDirectoryWatcher;
  iCount: Integer;
begin
  for iCount:= DirectoryWatchList.Count - 1 downto 0 do
  begin
    dmDirectoryWatcher:= DirectoryWatchList[iCount];
    dmDirectoryWatcher.Enabled:= True;
  end;
end;

procedure TfrmMain.menuTerminateClick(Sender: TObject);
begin
  Close;
end;

function TfrmMain.GetTextExtension: string;
var
  ConverterPers: TDFMConverterPersistentData;
begin
  ConverterPers:= TDFMConverterPersistentData.Create;
  try
    Result:= ConverterPers.TextFileExtension;
  finally
    ConverterPers.Free;
  end;
end;

procedure TfrmMain.menuForceConvertClick(Sender: TObject);
begin
  ForceConvert;
end;

procedure TfrmMain.ForceConvert;
var
  sTextExt: string;
begin
  if Assigned(dlgForceConvert) then
  begin
    dlgForceConvert.BringToFront;
    Exit;
  end;
  DisableAllWatches;
  try
    sTextExt:= GetTextExtension;
    dlgForceConvert:= TdlgForceConvert.Create(nil);
    dlgForceConvert.TextExtension:= sTextExt;
    dlgForceConvert.ShowModal;
    try
      dlgForceConvert.Free;
    finally
      dlgForceConvert:= nil;
    end;
  finally
    EnableAllWatches;
  end;
end;

procedure TfrmMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  FreeAllWatches;
end;

procedure TfrmMain.FormDestroy(Sender: TObject);
begin
  DirectoryWatchList.Free;
  Log.Free;
end;


end.
The datamodule code is below...the rest of the code a tray icon component, a threaded timer, and a file notification component (using a thread per component).  There is one threaded timer and one file notification component per data module.  The tray icon component is on the main form.  There are also some utility units that should have no bearing on this problem (screens to edit properties, unit to save/load configuration).

unit dm_DirectoryWatcher;
{===================================================================================================
  Description:  Watches directory and converts files
  Usage: One instance created per directory to be watched
  Copyright© 1998 Corporate Express, Inc.

  Keywords:
  $Author: Phil Swenson$
  $Date: 11/30/98 8:37:14 AM$
  $Workfile: C:\Prj\Dev\DFM Converter\D4\src\dm_DirectoryWatcher.pas$
  $Project: DFM Converter$
  $Revision: 4$
  $Folder: src$
===================================================================================================}
interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  DirWatch, ThreadedTimer, uUtils, uDFMConverterGlobals, SyncObjs{$IFDEF DEBUG}, RzCSIntf{$ENDIF};

type
  TdmDirectoryWatcher = class;
  TFileItem = class
    FileName: string;
    Extension: string;
    Valid: Boolean;
  end;

  TdmDirectoryWatcher = class(TDataModule)
    tmQueueCheck: TThreadedTimer;
    DirWatch: TDirWatch;
    procedure dmDirectoryMonitorDestroy(Sender: TObject);
    procedure dmDirectoryMonitorCreate(Sender: TObject);
    procedure DirWatchNotify(Sender: TObject; Action: Integer; const FileName: string);
    procedure tmQueueCheckTimer(Sender: TObject);
  private
    { Private declarations }
    NoConvertList: TStringList;
    iQueueCount, iOldQueueCount: integer;
    FileQueue: TList;
    FsTextExtension: string;
    bNoConvertListEmpty, bFileConverted: Boolean;
    CriticalSection: TCriticalSection;
    function ProcessQueue: Boolean;
    procedure ConvertFile(FileItem: TFileItem);
    function GetFullPath(const sFileName: string): string;
    function GetDirectoryToWatch: string;
    procedure SetDirectoryToWatch(const Value: string);
    function GetDirWatchEnabled: Boolean;
    procedure SetDirWatchEnabled(const Value: Boolean);
    procedure SetTextExtension(const Value: string);
    procedure CleanUpFileQueue;
  public
    { Public declarations }
    property DirectoryToWatch: string read GetDirectoryToWatch write SetDirectoryToWatch;
    property Enabled: boolean read GetDirWatchEnabled write SetDirWatchEnabled;
    property TextExtension: string read FsTextExtension write SetTextExtension;
  end;

implementation

{$R *.DFM}

procedure TdmDirectoryWatcher.dmDirectoryMonitorCreate(Sender: TObject);
{$IFDEF DEBUG}const sRoutine = 'TdmDirectoryWatcher.dmDirectoryMonitorCreate';{$ENDIF}
begin
  {$IFDEF DEBUG}CodeSite.EnterMethod(sRoutine);{$ENDIF DEBUG}
  CriticalSection:= TCriticalSection.Create;
  FileQueue:= TList.Create;
  NoConvertList:= TStringList.Create;
  iQueueCount:= 0;
  iOldQueueCount:= 0;
  bNoConvertListEmpty:= True;
  {$IFDEF DEBUG}CodeSite.ExitMethod(sRoutine);{$ENDIF DEBUG}
end;

procedure TdmDirectoryWatcher.dmDirectoryMonitorDestroy(Sender: TObject);
{$IFDEF DEBUG}const sRoutine = 'TdmDirectoryWatcher.dmDirectoryMonitorDestroy';{$ENDIF}
var
  iCount: Integer;
  FileItem: TFileItem;
begin
  {$IFDEF DEBUG} CodeSite.EnterMethod(sRoutine); {$ENDIF}
  DirWatch.Enabled:= False;
  tmQueueCheck.Enabled:= False;
  for iCount:= 0 to FileQueue.Count - 1 do
  begin
    FileItem:= FileQueue.List[iCount];
    FileItem.Free;
  end;
  FileQueue.Free;
  NoConvertList.Free;
  CriticalSection.Free;
  {$IFDEF DEBUG} CodeSite.ExitMethod(sRoutine); {$ENDIF}
end;


function TdmDirectoryWatcher.GetDirectoryToWatch: string;
begin
  Result:= DirWatch.Directory;
end;

procedure TdmDirectoryWatcher.SetDirectoryToWatch(const Value: string);
begin
  DirWatch.Directory:= Value;
end;

procedure TdmDirectoryWatcher.SetTextExtension(const Value: string);
begin
  FsTextExtension:= LowerCase(Value);
end;

function TdmDirectoryWatcher.GetDirWatchEnabled: Boolean;
begin
  Result:= DirWatch.Enabled;
end;

procedure TdmDirectoryWatcher.SetDirWatchEnabled(const Value: Boolean);
begin
  DirWatch.Enabled:= Value;
  tmQueueCheck.Enabled:= Value;
end;

{ Notification that a file has changed from the DirWatch component }
procedure TdmDirectoryWatcher.DirWatchNotify(Sender: TObject; Action: Integer;
        const FileName: String);
{$IFDEF DEBUG}const sRoutine = 'TdmDirectoryWatcher.DirWatchNotify';{$ENDIF}
var
  sFileName, sExtension: string;
  FileItem: TFileItem;
  iLength: Integer;
begin
  {$IFDEF DEBUG} CodeSite.EnterMethod(sRoutine);{$ENDIF}
  try
    { can't process files that don't exist or don't exist by that name }
    case Action of
      FILE_ACTION_REMOVED:
      begin
        CriticalSection.Enter;
        try
          bNoConvertListEmpty:= False;
          NoConvertList.Add(FileName);
        finally
          CriticalSection.Leave;
        end;
        Exit;
      end;
      FILE_ACTION_RENAMED_OLD_NAME: Exit;
    end;
    sFileName:= FileName;
    sExtension:= LowerCase(ExtractFileExt(sFileName));
    { Only care about text files and form files }
    if (sExtension <> sFORM_FILE_EXTENSION) and (sExtension <> FsTextExtension) then Exit;
    FileItem:= TFileItem.Create;
    { FileItem.FileName includes the path relative to  DirWatch.Directory + FileName - FileExt }
    iLength:= Length(sFileName) - Length(sExtension);
    FileItem.FileName:= Copy(sFileName, 1, iLength);
    FileItem.Extension:= sExtension;
    FileItem.Valid:= True;
    CriticalSection.Enter;
    try
      FileQueue.Add(FileItem);
      {$IFDEF DEBUG} CodeSite.SendMsg(sFileName + ' added to queue'); {$ENDIF}
      iQueueCount:= FileQueue.Count;
    finally
      CriticalSection.Leave;
    end;
  finally
    {$IFDEF DEBUG} CodeSite.ExitMethod(sRoutine); {$ENDIF}
  end;
end;

{ removes invalid and redundant items form queue }
procedure TdmDirectoryWatcher.CleanUpFileQueue;
{$IFDEF DEBUG}const sRoutine = 'TdmDirectoryWatcher.CleanUpFileQueue';{$ENDIF}
var
  sFileName, sFileNameToCheck, sNoConvertFile: string;
  iFileQueueCount, iCount, iCount2: Integer;
  FileItem, FileItemToCheck: TFileItem;
begin
  {$IFDEF DEBUG} CodeSite.EnterMethod(sRoutine); {$ENDIF}
  iFileQueueCount:= FileQueue.Count;
  for iCount:= 0 to FileQueue.Count - 1 do
  begin
    FileItem:= FileQueue.Items[iCount];
    sFileName:= FileItem.FileName;
    for iCount2:= iCount + 1 to iFileQueueCount - 1 do
    begin
      FileItemToCheck:= FileQueue.Items[iCount2];
      if FileItemToCheck.Valid then
      begin
        sFileNameToCheck:= FileItemToCheck.FileName;
        if CompareText(sFileNameToCheck, sFileName) = 0 then
          FileItemToCheck.Valid:= False;
      end;
    end;
    if FileItem.Valid then
      for iCount2:= 0 to NoConvertList.Count - 1 do
      begin
        sNoConvertFile:= NoConvertList[iCount2];
        if CompareText(sNoConvertFile, sFileName) = 0 then
          FileItem.Valid:= False;
      end;
  end;
  {$IFDEF DEBUG} CodeSite.ExitMethod(sRoutine); {$ENDIF}
end;

function TdmDirectoryWatcher.GetFullPath(const sFileName: string): string;
begin
   Result:= DirWatch.Directory + '\' + sFileName;
end;

{ Converts a text to DFM or vice versa }
procedure TdmDirectoryWatcher.ConvertFile(FileItem: TFileItem);
{$IFDEF DEBUG}const sRoutine = 'TdmDirectoryWatcher.ConvertFile';{$ENDIF}
var
  sFileName, sNewFileName, sFullPathName: string;
begin
  {$IFDEF DEBUG} CodeSite.EnterMethod(sRoutine); {$ENDIF}
  bFileConverted:= True;
  sFullPathName:= GetFullPath(FileItem.FileName);
  sFileName:=  sFullPathName + FileItem.Extension;
  NoConvertList.Add(FileItem.FileName);
  bNoConvertListEmpty:= False;
  if FileItem.Extension = sFORM_FILE_EXTENSION then
  begin
    {$IFDEF DEBUG} CodeSite.SendMsg(sFileName + ' preconvert from DFM to text'); {$ENDIF}
    sNewFileName:=  sFullPathName + FsTextExtension;
    try
      TFileUtils.DFM2TXT(sFileName, sNewFileName);
      {$IFDEF DEBUG} CodeSite.SendMsg(sFileName + ' converted from DFM to Text'); {$ENDIF}
    except
      on E: Exception do
      begin
        {$IFDEF DEBUG} CodeSite.SendError('Error on converting DFM to Text: ' + E.Message); {$ENDIF}
        raise;
      end;
    end;
  end
  else { Text Extension }
  begin
    sNewFileName:= sFullPathName + sFORM_FILE_EXTENSION;
    {$IFDEF DEBUG} CodeSite.SendMsg(sFileName + ' preconvert from Text to DFM'); {$ENDIF}
    try
      TFileUtils.TXT2DFM(sFileName, sNewFileName);
      {$IFDEF DEBUG} CodeSite.SendMsg(sFileName + ' converted from Text to DFM'); {$ENDIF}
    except
      on E: Exception do
      begin
        {$IFDEF DEBUG} CodeSite.SendError('Error on converting Text to DFM: ' + E.Message); {$ENDIF}
        raise;
      end;
    end;
  end;
  {$IFDEF DEBUG} CodeSite.ExitMethod(sRoutine); {$ENDIF}
end;

{ iterates thru queue, elimiates redundant/invalid items, then does proper conversions }
function TdmDirectoryWatcher.ProcessQueue: Boolean;
{$IFDEF DEBUG}const sRoutine = 'TdmDirectoryWatcher.ProcessQueue';{$ENDIF}
var
  sFileName: string;
  iCount: Integer;
  FileItem: TFileItem;
begin
  {$IFDEF DEBUG} CodeSite.EnterMethod(sRoutine); {$ENDIF}
  Result:= False;
  CleanUpFileQueue;
  for iCount:= 0 to FileQueue.Count - 1 do
  begin
    FileItem:= FileQueue[iCount];
    sFileName:= FileItem.FileName;
    if FileItem.Valid then
    begin
      Result:= True;
      ConvertFile(FileItem);
    end;
    FileItem.Free;
    FileQueue[iCount]:= nil;
  end;
  FileQueue.Clear;
  iQueueCount:= 0;
  iOldQueueCount:= 0;
  {$IFDEF DEBUG} CodeSite.ExitMethod(sRoutine); {$ENDIF}
end;

procedure TdmDirectoryWatcher.tmQueueCheckTimer(Sender: TObject);
{$IFDEF DEBUG}const sRoutine = 'TdmDirectoryWatcher.tmQueueCheckTimer';{$ENDIF}
begin
  {$IFDEF DEBUG} CodeSite.EnterMethod(sRoutine); {$ENDIF}
  CriticalSection.Enter;
  try
    if iQueueCount <> 0 then
    begin
      if (iOldQueueCount = iQueueCount) then
        ProcessQueue
      else
        iOldQueueCount:= iQueueCount;
    end
    else
    begin
      if not bNoConvertListEmpty then
      begin
        NoConvertList.Clear;
        bNoConvertListEmpty:= True;
        {$IFDEF DEBUG} CodeSite.SendWarning('Clear convert list.'); {$ENDIF}
      end;
    end;
  finally
    CriticalSection.Leave;
  end;
  {$IFDEF DEBUG} CodeSite.ExitMethod(sRoutine); {$ENDIF}
end;



initialization
  {$IFDEF DEBUG}CodeSite.Clear;{$ENDIF}
end.
well i have no idea why this doesn't work...maybe something with the WinNT. o O

Why don't you try using Handled := True; where you terminate the app...
There was a question posted a while back about an app that prevented windows from shutting down.
The problem turned out to be that the window state of the main form was set to wsMinimised.
By replacing the code with
Application.Minimize;
everything was fine.
Is WindowState set to wsMinimised in the object inspector? If so, try setting it to wsNormal and put Application.Minimize in the FormCreate event.

Cheers, Phil.

Another possibility is that "Application.Terminate" only sets the variable Application.terminated to true.  Unless your code goes to a loop where the delphi (or you) issue a

if application.terminated then close;

the app will look like "not responding" to windows.  You can check this very easily by using a Halt(0) instead of Application.Terminate in your message handler.

Hmm...I didn't check your code out...too big for a small netscape screen :)

check the following out, it might help you:
https://www.experts-exchange.com/Q.10085856
ahalya:  Application.Terminate posts a WM_QUIT to the app.  Check
the help.  I tried a Halt, it doesn't work either.

jconde:  that question was the same, but the solution didn't apply.

I have also tried doing an application.hookmainwindow and intercepting a wm_endsession there to no avail...

I really think that the threads are screwing things up, I just don't know why...
Adjusted points to 300
well if the threads are screwing up things, then do something like this...

OnMessageHandle()
begin
  case Msg.Message of
    WM_QUIT, WM_CLOSE, WM_ENDSESSION, WM_QUERYENDSESSION :
    begin
      StopAllTheThreads; //This is some kind of function that stops the threads... you can also               //destroy the threads and free them here, and then do the Application.Terminate;
      Application.Terminate;
    end;
  end;
end;

Hope this helps,,,

Cheers,
Viktor
viktornet:  In effect that is what is happening in my code.  I call freeallwatches on the close event of the form.  FreeAllWatches will stop and free all the threads.  The close call never happens though because the message is never received.

The problem is the message doesn't get through to begin with.  I tried something that worked:  I subclassed the Application's window (that is intercepted it's WndProc) and made the main window so it was visible (got rid of Application.ShowMainWindow:= False in the project source)...this caused it to work.  Only doing one of the two didn't solve the problem, it had to be both.  This solution is not acceptable though...I don't want my main window showing.  If I minimize my main window and then try shutting down windows, it fails to close the program...For whatever reason, the application appears not to be processing messages when the main window is not visible.. I don't get it!
How about putting Application.ProcessMessages;
somewhere in your code after you do the Application.ShowMainForm := False; ??? Do you think that would work?? I'll ask you to try to put the FreeAllWatches; where you check for the messages with the case statement, other than in the Close() handler...

case msg.message of
//Messages :
begin
  FreeAllWatches;
  Application.Terminate;
end;
end;

and something similar to this,.
I figured it out!  One of the components was allocating it's own windows (AllocateHwnd) and in the wndproc it wasn't calling "DefWindowProc(FWndHandle, M.Msg, M.wParam, M.lParam);".  I'm not sure why this only had an effect at shut-down, but that's the only place it ever caused a problem.
If you use TTrayIcon component you must correct this procedure
=====
procedure TTrayIcon.WndProc(var msg : TMessage);
var MouseCo: Tpoint;
begin
   with msg do
      if (msg = WM_RESETTOOLTIP) then
         SetToolTip( fToolTip )
      else if (msg = WM_TOOLTRAYICON) then begin
         case lParam of
            WM_LBUTTONDBLCLK   : if assigned (FOnDblClick) then FOnDblClick(self);
            WM_LBUTTONUP       : if assigned(FOnClick)then FOnClick(self);
            WM_RBUTTONUP       : if assigned (FOnRightClick)then
                                 begin
                                   GetCursorPos(MouseCo);
                                   FOnRightClick(self,mbRight,[],MouseCo.x,MouseCo.y);
                                 end;
         end;
      end
//I add next code. may be it usefull for you
else if (msg = WM_QUERYENDSESSION) then
                 Result := Longint(True)
               else if (msg = WM_ENDSESSION) then
                 Application.Terminate;

end;
====
in constructor
====
constructor TTrayIcon.create(aOwner : Tcomponent);
begin
  inherited create(aOwner);
  FWindowHandle := AllocateHWnd( WndProc );
  FIcon := TIcon.Create;
end;

This code tested under NT 4.0

ASKER CERTIFIED SOLUTION
Avatar of linda101698
linda101698

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial