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?
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.
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.
When you receive the WM_EndSession, check the wparam to make sure that windows is closing.
ASKER
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†
OnMessage()
begin
case Msg of
WM_QUIT, WM_CLOSE, WM_QUERYENDSESSION, WM_ENDSESSION : Application.Terminate;
end;
end;
Cheers,
Viktor†
ASKER
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(Send er: 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.menuPropertiesCli ck(Sender: TObject);
begin
ShowPropertiesDialog;
end;
procedure TfrmMain.ShowPropertiesDia log;
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: TDFMConverterPersistentDat a;
DirectoryData: TDirectoryData;
sTextExtension: string;
begin
ConverterPers:= TDFMConverterPersistentDat a.Create;
try
FreeAllWatches;
if not ConverterPers.ConverterEna bled then Exit;
sTextExtension:= ConverterPers.TextFileExte nsion;
//ConverterPers.LogFileEna bled;
//ConverterPers.LogFilePat h;
DirectoryList:= ConverterPers.DirectoryLis t;
finally
ConverterPers.Free;
end;
for iCount:= Low(DirectoryList) to High(DirectoryList) do
begin
DirectoryData:= DirectoryList[iCount];
if DirectoryData.DirectoryToW atch <> '' then
StartWatch(DirectoryData.D irectoryTo Watch, sTextExtension, DirectoryData.WatchSubTree );
end;
end;
procedure TfrmMain.StartWatch(const sDirectoryToWatch, sTextExtension: string; const bWatchSubTree);
var
dmDirectoryWatcher: TdmDirectoryWatcher;
begin
dmDirectoryWatcher:= TdmDirectoryWatcher.Create (nil);
DirectoryWatchList.Add(dmD irectoryWa tcher);
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.menuTerminateClic k(Sender: TObject);
begin
Close;
end;
function TfrmMain.GetTextExtension: string;
var
ConverterPers: TDFMConverterPersistentDat a;
begin
ConverterPers:= TDFMConverterPersistentDat a.Create;
try
Result:= ConverterPers.TextFileExte nsion;
finally
ConverterPers.Free;
end;
end;
procedure TfrmMain.menuForceConvertC lick(Sende r: TObject);
begin
ForceConvert;
end;
procedure TfrmMain.ForceConvert;
var
sTextExt: string;
begin
if Assigned(dlgForceConvert) then
begin
dlgForceConvert.BringToFro nt;
Exit;
end;
DisableAllWatches;
try
sTextExt:= GetTextExtension;
dlgForceConvert:= TdlgForceConvert.Create(ni l);
dlgForceConvert.TextExtens ion:= 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(Sende r: TObject);
begin
DirectoryWatchList.Free;
Log.Free;
end;
end.
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.
$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
procedure FormCreate(Sender: TObject);
procedure menuTerminateClick(Sender:
procedure FormDestroy(Sender: TObject);
procedure menuForceConvertClick(Send
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
begin
TrayNotifyIcon.IconVisible
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.menuPropertiesCli
begin
ShowPropertiesDialog;
end;
procedure TfrmMain.ShowPropertiesDia
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: TDFMConverterPersistentDat
DirectoryData: TDirectoryData;
sTextExtension: string;
begin
ConverterPers:= TDFMConverterPersistentDat
try
FreeAllWatches;
if not ConverterPers.ConverterEna
sTextExtension:= ConverterPers.TextFileExte
//ConverterPers.LogFileEna
//ConverterPers.LogFilePat
DirectoryList:= ConverterPers.DirectoryLis
finally
ConverterPers.Free;
end;
for iCount:= Low(DirectoryList) to High(DirectoryList) do
begin
DirectoryData:= DirectoryList[iCount];
if DirectoryData.DirectoryToW
StartWatch(DirectoryData.D
end;
end;
procedure TfrmMain.StartWatch(const sDirectoryToWatch, sTextExtension: string; const bWatchSubTree);
var
dmDirectoryWatcher: TdmDirectoryWatcher;
begin
dmDirectoryWatcher:= TdmDirectoryWatcher.Create
DirectoryWatchList.Add(dmD
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
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
end;
end;
procedure TfrmMain.menuTerminateClic
begin
Close;
end;
function TfrmMain.GetTextExtension:
var
ConverterPers: TDFMConverterPersistentDat
begin
ConverterPers:= TDFMConverterPersistentDat
try
Result:= ConverterPers.TextFileExte
finally
ConverterPers.Free;
end;
end;
procedure TfrmMain.menuForceConvertC
begin
ForceConvert;
end;
procedure TfrmMain.ForceConvert;
var
sTextExt: string;
begin
if Assigned(dlgForceConvert) then
begin
dlgForceConvert.BringToFro
Exit;
end;
DisableAllWatches;
try
sTextExt:= GetTextExtension;
dlgForceConvert:= TdlgForceConvert.Create(ni
dlgForceConvert.TextExtens
dlgForceConvert.ShowModal;
try
dlgForceConvert.Free;
finally
dlgForceConvert:= nil;
end;
finally
EnableAllWatches;
end;
end;
procedure TfrmMain.FormClose(Sender:
begin
FreeAllWatches;
end;
procedure TfrmMain.FormDestroy(Sende
begin
DirectoryWatchList.Free;
Log.Free;
end;
end.
ASKER
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_Direct oryWatcher .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(S ender: 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.dmDire ctoryMonit orCreate(S ender: TObject);
{$IFDEF DEBUG}const sRoutine = 'TdmDirectoryWatcher.dmDir ectoryMoni torCreate' ;{$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.dmDire ctoryMonit orDestroy( Sender: TObject);
{$IFDEF DEBUG}const sRoutine = 'TdmDirectoryWatcher.dmDir ectoryMoni torDestroy ';{$ENDIF}
var
iCount: Integer;
FileItem: TFileItem;
begin
{$IFDEF DEBUG} CodeSite.EnterMethod(sRout ine); {$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(sRouti ne); {$ENDIF}
end;
function TdmDirectoryWatcher.GetDir ectoryToWa tch: string;
begin
Result:= DirWatch.Directory;
end;
procedure TdmDirectoryWatcher.SetDir ectoryToWa tch(const Value: string);
begin
DirWatch.Directory:= Value;
end;
procedure TdmDirectoryWatcher.SetTex tExtension (const Value: string);
begin
FsTextExtension:= LowerCase(Value);
end;
function TdmDirectoryWatcher.GetDir WatchEnabl ed: Boolean;
begin
Result:= DirWatch.Enabled;
end;
procedure TdmDirectoryWatcher.SetDir WatchEnabl ed(const Value: Boolean);
begin
DirWatch.Enabled:= Value;
tmQueueCheck.Enabled:= Value;
end;
{ Notification that a file has changed from the DirWatch component }
procedure TdmDirectoryWatcher.DirWat chNotify(S ender: TObject; Action: Integer;
const FileName: String);
{$IFDEF DEBUG}const sRoutine = 'TdmDirectoryWatcher.DirWa tchNotify' ;{$ENDIF}
var
sFileName, sExtension: string;
FileItem: TFileItem;
iLength: Integer;
begin
{$IFDEF DEBUG} CodeSite.EnterMethod(sRout ine);{$END IF}
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_NA ME: Exit;
end;
sFileName:= FileName;
sExtension:= LowerCase(ExtractFileExt(s FileName)) ;
{ 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(sRouti ne); {$ENDIF}
end;
end;
{ removes invalid and redundant items form queue }
procedure TdmDirectoryWatcher.CleanU pFileQueue ;
{$IFDEF DEBUG}const sRoutine = 'TdmDirectoryWatcher.Clean UpFileQueu e';{$ENDIF }
var
sFileName, sFileNameToCheck, sNoConvertFile: string;
iFileQueueCount, iCount, iCount2: Integer;
FileItem, FileItemToCheck: TFileItem;
begin
{$IFDEF DEBUG} CodeSite.EnterMethod(sRout ine); {$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(sFileNameToChe ck, 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(sRouti ne); {$ENDIF}
end;
function TdmDirectoryWatcher.GetFul lPath(cons t sFileName: string): string;
begin
Result:= DirWatch.Directory + '\' + sFileName;
end;
{ Converts a text to DFM or vice versa }
procedure TdmDirectoryWatcher.Conver tFile(File Item: TFileItem);
{$IFDEF DEBUG}const sRoutine = 'TdmDirectoryWatcher.Conve rtFile';{$ ENDIF}
var
sFileName, sNewFileName, sFullPathName: string;
begin
{$IFDEF DEBUG} CodeSite.EnterMethod(sRout ine); {$ENDIF}
bFileConverted:= True;
sFullPathName:= GetFullPath(FileItem.FileN ame);
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(sFileNa me, 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(sFileNa me, 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(sRouti ne); {$ENDIF}
end;
{ iterates thru queue, elimiates redundant/invalid items, then does proper conversions }
function TdmDirectoryWatcher.Proces sQueue: Boolean;
{$IFDEF DEBUG}const sRoutine = 'TdmDirectoryWatcher.Proce ssQueue';{ $ENDIF}
var
sFileName: string;
iCount: Integer;
FileItem: TFileItem;
begin
{$IFDEF DEBUG} CodeSite.EnterMethod(sRout ine); {$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(sRouti ne); {$ENDIF}
end;
procedure TdmDirectoryWatcher.tmQueu eCheckTime r(Sender: TObject);
{$IFDEF DEBUG}const sRoutine = 'TdmDirectoryWatcher.tmQue ueCheckTim er';{$ENDI F}
begin
{$IFDEF DEBUG} CodeSite.EnterMethod(sRout ine); {$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('Clea r convert list.'); {$ENDIF}
end;
end;
finally
CriticalSection.Leave;
end;
{$IFDEF DEBUG} CodeSite.ExitMethod(sRouti ne); {$ENDIF}
end;
initialization
{$IFDEF DEBUG}CodeSite.Clear;{$END IF}
end.
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_Direct
$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(
procedure dmDirectoryMonitorCreate(S
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.dmDire
{$IFDEF DEBUG}const sRoutine = 'TdmDirectoryWatcher.dmDir
begin
{$IFDEF DEBUG}CodeSite.EnterMethod
CriticalSection:= TCriticalSection.Create;
FileQueue:= TList.Create;
NoConvertList:= TStringList.Create;
iQueueCount:= 0;
iOldQueueCount:= 0;
bNoConvertListEmpty:= True;
{$IFDEF DEBUG}CodeSite.ExitMethod(
end;
procedure TdmDirectoryWatcher.dmDire
{$IFDEF DEBUG}const sRoutine = 'TdmDirectoryWatcher.dmDir
var
iCount: Integer;
FileItem: TFileItem;
begin
{$IFDEF DEBUG} CodeSite.EnterMethod(sRout
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(sRouti
end;
function TdmDirectoryWatcher.GetDir
begin
Result:= DirWatch.Directory;
end;
procedure TdmDirectoryWatcher.SetDir
begin
DirWatch.Directory:= Value;
end;
procedure TdmDirectoryWatcher.SetTex
begin
FsTextExtension:= LowerCase(Value);
end;
function TdmDirectoryWatcher.GetDir
begin
Result:= DirWatch.Enabled;
end;
procedure TdmDirectoryWatcher.SetDir
begin
DirWatch.Enabled:= Value;
tmQueueCheck.Enabled:= Value;
end;
{ Notification that a file has changed from the DirWatch component }
procedure TdmDirectoryWatcher.DirWat
const FileName: String);
{$IFDEF DEBUG}const sRoutine = 'TdmDirectoryWatcher.DirWa
var
sFileName, sExtension: string;
FileItem: TFileItem;
iLength: Integer;
begin
{$IFDEF DEBUG} CodeSite.EnterMethod(sRout
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_NA
end;
sFileName:= FileName;
sExtension:= LowerCase(ExtractFileExt(s
{ 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
iQueueCount:= FileQueue.Count;
finally
CriticalSection.Leave;
end;
finally
{$IFDEF DEBUG} CodeSite.ExitMethod(sRouti
end;
end;
{ removes invalid and redundant items form queue }
procedure TdmDirectoryWatcher.CleanU
{$IFDEF DEBUG}const sRoutine = 'TdmDirectoryWatcher.Clean
var
sFileName, sFileNameToCheck, sNoConvertFile: string;
iFileQueueCount, iCount, iCount2: Integer;
FileItem, FileItemToCheck: TFileItem;
begin
{$IFDEF DEBUG} CodeSite.EnterMethod(sRout
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(sFileNameToChe
FileItemToCheck.Valid:= False;
end;
end;
if FileItem.Valid then
for iCount2:= 0 to NoConvertList.Count - 1 do
begin
sNoConvertFile:= NoConvertList[iCount2];
if CompareText(sNoConvertFile
FileItem.Valid:= False;
end;
end;
{$IFDEF DEBUG} CodeSite.ExitMethod(sRouti
end;
function TdmDirectoryWatcher.GetFul
begin
Result:= DirWatch.Directory + '\' + sFileName;
end;
{ Converts a text to DFM or vice versa }
procedure TdmDirectoryWatcher.Conver
{$IFDEF DEBUG}const sRoutine = 'TdmDirectoryWatcher.Conve
var
sFileName, sNewFileName, sFullPathName: string;
begin
{$IFDEF DEBUG} CodeSite.EnterMethod(sRout
bFileConverted:= True;
sFullPathName:= GetFullPath(FileItem.FileN
sFileName:= sFullPathName + FileItem.Extension;
NoConvertList.Add(FileItem
bNoConvertListEmpty:= False;
if FileItem.Extension = sFORM_FILE_EXTENSION then
begin
{$IFDEF DEBUG} CodeSite.SendMsg(sFileName
sNewFileName:= sFullPathName + FsTextExtension;
try
TFileUtils.DFM2TXT(sFileNa
{$IFDEF DEBUG} CodeSite.SendMsg(sFileName
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
try
TFileUtils.TXT2DFM(sFileNa
{$IFDEF DEBUG} CodeSite.SendMsg(sFileName
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(sRouti
end;
{ iterates thru queue, elimiates redundant/invalid items, then does proper conversions }
function TdmDirectoryWatcher.Proces
{$IFDEF DEBUG}const sRoutine = 'TdmDirectoryWatcher.Proce
var
sFileName: string;
iCount: Integer;
FileItem: TFileItem;
begin
{$IFDEF DEBUG} CodeSite.EnterMethod(sRout
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(sRouti
end;
procedure TdmDirectoryWatcher.tmQueu
{$IFDEF DEBUG}const sRoutine = 'TdmDirectoryWatcher.tmQue
begin
{$IFDEF DEBUG} CodeSite.EnterMethod(sRout
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('Clea
end;
end;
finally
CriticalSection.Leave;
end;
{$IFDEF DEBUG} CodeSite.ExitMethod(sRouti
end;
initialization
{$IFDEF DEBUG}CodeSite.Clear;{$END
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...
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.
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.
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
check the following out, it might help you:
https://www.experts-exchange.com/Q.10085856
ASKER
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...
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
I really think that the threads are screwing things up, I just don't know why...
ASKER
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
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
ASKER
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!
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
How about putting Application.ProcessMessage s;
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,.
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,.
ASKER
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 ,[],MouseC o.x,MouseC o.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
=====
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
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
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
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.