624 lines
19 KiB
ObjectPascal
624 lines
19 KiB
ObjectPascal
{-----------------------------------------------------------------------------
|
|
The contents of this file are subject to the Mozilla Public License
|
|
Version 1.1 (the "License"); you may not use this file except in compliance
|
|
with the License. You may obtain a copy of the License at
|
|
http://www.mozilla.org/MPL/MPL-1.1.html
|
|
|
|
Software distributed under the License is distributed on an "AS IS" basis,
|
|
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
|
|
the specific language governing rights and limitations under the License.
|
|
|
|
The Original Code is: JvChangeNotify.PAS, released on 2002-05-26.
|
|
|
|
The Initial Developer of the Original Code is Peter Thrnqvist [peter3 at sourceforge dot net]
|
|
Portions created by Peter Thrnqvist are Copyright (C) 2002 Peter Thrnqvist.
|
|
All Rights Reserved.
|
|
|
|
Contributor(s):
|
|
|
|
You may retrieve the latest version of this file at the Project JEDI's JVCL home page,
|
|
located at http://jvcl.sourceforge.net
|
|
|
|
Description:
|
|
A wrapper for the Find[First/Next]ChangeNotification API calls.
|
|
|
|
Changes:
|
|
//dierk schmid 2004-4-28
|
|
-- TJvChangeNotify: Put property "active" from public to published section
|
|
(cause I always forget to set this property in runtime to true)
|
|
-- TJvChangeNotify.SetActive: Exit if csDesigning in ComponentState (Active is now published)
|
|
-- TJvChangeItem.SetDir: Exception not when csDesigning in ComponentState
|
|
(cause, it was impossible to reset in designtime the directory property)
|
|
-- Same TJvChangeNotify.CheckActive: Exception not when csDesigning+csloading in ComponentState
|
|
-- added procedure TJvChangeNotify.Loaded; override;
|
|
|
|
Known Issues:
|
|
-----------------------------------------------------------------------------}
|
|
// $Id: JvChangeNotify.pas 11043 2006-11-26 07:21:48Z marquardt $
|
|
|
|
unit JvChangeNotify;
|
|
|
|
interface
|
|
|
|
{$I jvcl.inc}
|
|
{$I windowsonly.inc}
|
|
|
|
uses
|
|
{$IFDEF UNITVERSIONING}
|
|
JclUnitVersioning,
|
|
{$ENDIF UNITVERSIONING}
|
|
{$IFDEF CLR}
|
|
System.Text,
|
|
{$ENDIF CLR}
|
|
Windows, Classes,
|
|
JvComponentBase, JvTypes;
|
|
|
|
type
|
|
TJvNotifyArray = array [0..MAXIMUM_WAIT_OBJECTS - 1] of THandle;
|
|
TJvChangeAction = (caChangeFileName, caChangeDirName, caChangeAttributes, caChangeSize,
|
|
caChangeLastWrite, caChangeSecurity);
|
|
TJvChangeActions = set of TJvChangeAction;
|
|
TJvNotifyEvent = procedure(Sender: TObject; Dir: string; Actions: TJvChangeActions) of object;
|
|
TJvThreadNotifyEvent = procedure(Sender: TObject; Index: Integer) of object;
|
|
TJvNotifyError = procedure(Sender: TObject; const Msg: string) of object;
|
|
|
|
TJvChangeItems = class;
|
|
TJvChangeNotify = class;
|
|
|
|
// Exception used by NotifyError
|
|
EJVCLChangeNotifyException = class(EJVCLException)
|
|
private
|
|
FErrorDirectory : string;
|
|
public
|
|
constructor Create(const ErrorMsg: string; const ErrorDirectory: string);
|
|
|
|
property ErrorDirectory : string read FErrorDirectory;
|
|
end;
|
|
|
|
TJvChangeItem = class(TCollectionItem)
|
|
private
|
|
FParent: TJvChangeItems;
|
|
FActions: TJvChangeActions;
|
|
FSubTrees: Boolean;
|
|
FDir: string;
|
|
FOnChange: TNotifyEvent;
|
|
procedure SetSubTrees(const Value: Boolean);
|
|
procedure SetDir(const Value: string);
|
|
protected
|
|
function GetDisplayName: string; override;
|
|
procedure Change; virtual;
|
|
public
|
|
constructor Create(Collection: TCollection); override;
|
|
procedure Assign(Source: TPersistent); override;
|
|
published
|
|
property Directory: string read FDir write SetDir;
|
|
property Actions: TJvChangeActions read FActions write FActions default [caChangeFileName, caChangeDirName];
|
|
property IncludeSubTrees: Boolean read FSubTrees write SetSubTrees default False;
|
|
property OnChange: TNotifyEvent read FOnChange write FOnChange;
|
|
end;
|
|
|
|
TJvChangeItems = class(TCollection)
|
|
protected
|
|
FOwner: TJvChangeNotify;
|
|
function GetItem(Index: Integer): TJvChangeItem;
|
|
procedure SetItem(Index: Integer; Value: TJvChangeItem);
|
|
function GetOwner: TPersistent; override;
|
|
public
|
|
constructor Create(AOwner: TJvChangeNotify);
|
|
function Add: TJvChangeItem;
|
|
procedure Assign(Source: TPersistent); override;
|
|
property Items[Index: Integer]: TJvChangeItem read GetItem write SetItem; default;
|
|
end;
|
|
|
|
{ WARNING: Do not call Thread.Terminate from user code. This will leave a
|
|
dangling TJvChangeNotify.FThread reference which will cause an access
|
|
violation at the next TJvChangeNotify.SetActive call. }
|
|
TJvChangeThread = class(TThread)
|
|
private
|
|
FNotifyArray: TJvNotifyArray;
|
|
FCount: Integer;
|
|
FIndex: Integer;
|
|
FInterval: Integer;
|
|
FNotify: TJvThreadNotifyEvent;
|
|
procedure SynchChange;
|
|
protected
|
|
procedure Execute; override;
|
|
public
|
|
constructor Create(NotifyArray: TJvNotifyArray; Count, Interval: Integer; AFreeOnTerminate: Boolean);
|
|
property OnChangeNotify: TJvThreadNotifyEvent read FNotify write FNotify;
|
|
end;
|
|
|
|
TJvChangeNotify = class(TJvComponent)
|
|
private
|
|
FThread: TJvChangeThread;
|
|
FActive: Boolean;
|
|
FInterval: Integer;
|
|
FCollection: TJvChangeItems;
|
|
FNotify: TJvNotifyEvent;
|
|
FNotifyArray: TJvNotifyArray;
|
|
FFreeOnTerminate: Boolean;
|
|
procedure SetCollection(const Value: TJvChangeItems);
|
|
procedure SetInterval(const Value: Integer);
|
|
procedure SetActive(const Value: Boolean);
|
|
procedure CheckActive(const Name: string);
|
|
procedure NotifyError(const Msg: string);
|
|
procedure DoThreadChangeNotify(Sender: TObject; Index: Integer);
|
|
procedure SetFreeOnTerminate(const Value: Boolean);
|
|
protected
|
|
procedure Change(Item: TJvChangeItem); virtual;
|
|
procedure Loaded; override;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
published
|
|
property Active: Boolean read FActive write SetActive default False;
|
|
property Notifications: TJvChangeItems read FCollection write SetCollection;
|
|
property CheckInterval: Integer read FInterval write SetInterval default 100;
|
|
// Set FreeOnTerminate to True if you want to be able to change the Active property
|
|
// in the OnChangeNotify event.
|
|
property FreeOnTerminate: Boolean read FFreeOnTerminate write SetFreeOnTerminate default True;
|
|
property OnChangeNotify: TJvNotifyEvent read FNotify write FNotify;
|
|
end;
|
|
|
|
function ActionsToString(Actions: TJvChangeActions): string;
|
|
|
|
{$IFDEF UNITVERSIONING}
|
|
const
|
|
UnitVersioning: TUnitVersionInfo = (
|
|
RCSfile: '$URL: https://jvcl.svn.sourceforge.net/svnroot/jvcl/tags/JVCL3_32/run/JvChangeNotify.pas $';
|
|
Revision: '$Revision: 11043 $';
|
|
Date: '$Date: 2006-11-26 08:21:48 +0100 (dim., 26 nov. 2006) $';
|
|
LogPath: 'JVCL\run'
|
|
);
|
|
{$ENDIF UNITVERSIONING}
|
|
|
|
implementation
|
|
|
|
uses
|
|
SysUtils,
|
|
JvVCL5Utils, JvJCLUtils, JvResources;
|
|
// JvJCLUtils for DirectoryExists
|
|
|
|
function ActionsToString(Actions: TJvChangeActions): string;
|
|
const
|
|
ActionStrings: array [TJvChangeAction] of string =
|
|
(RsFileNameChange, RsDirectoryNameChange, RsAttributesChange,
|
|
RsSizeChange, RsWriteChange, RsSecurityChange);
|
|
var
|
|
I: TJvChangeAction;
|
|
begin
|
|
Result := '';
|
|
for I := Low(TJvChangeAction) to High(TJvChangeAction) do
|
|
if I in Actions then
|
|
if Result = '' then
|
|
Result := ActionStrings[I]
|
|
else
|
|
Result := Result + ',' + ActionStrings[I];
|
|
end;
|
|
|
|
//=== { TJvChangeItem } ======================================================
|
|
|
|
constructor TJvChangeItem.Create(Collection: Classes.TCollection); // TCollection redefined in JvVCL5Utils
|
|
begin
|
|
inherited Create(Collection);
|
|
FParent := TJvChangeItems(Collection);
|
|
FSubTrees := False;
|
|
FActions := [caChangeFileName, caChangeDirName];
|
|
end;
|
|
|
|
procedure TJvChangeItem.Assign(Source: TPersistent);
|
|
begin
|
|
if Source is TJvChangeItem then
|
|
begin
|
|
Directory := TJvChangeItem(Source).Directory;
|
|
Actions := TJvChangeItem(Source).Actions;
|
|
IncludeSubTrees := TJvChangeItem(Source).IncludeSubTrees;
|
|
end
|
|
else
|
|
inherited Assign(Source);
|
|
end;
|
|
|
|
procedure TJvChangeItem.SetSubTrees(const Value: Boolean);
|
|
begin
|
|
if FSubTrees <> Value then
|
|
begin
|
|
if csDesigning in FParent.FOwner.ComponentState then
|
|
FSubTrees := Value
|
|
else
|
|
if Value then
|
|
FSubTrees := Value and (Win32Platform = VER_PLATFORM_WIN32_NT)
|
|
else
|
|
FSubTrees := False;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvChangeItem.SetDir(const Value: string);
|
|
begin
|
|
if FDir <> Value then
|
|
begin
|
|
if not (csDesigning in FParent.FOwner.ComponentState) and
|
|
((Length(Value) = 0) or not DirectoryExists(Value)) then
|
|
{$IFDEF CLR}
|
|
raise EJVCLException.CreateFmt(RsEFmtInvalidPath, [Value]);
|
|
{$ELSE}
|
|
raise EJVCLException.CreateResFmt(@RsEFmtInvalidPath, [Value]);
|
|
{$ENDIF CLR}
|
|
FDir := Value;
|
|
end;
|
|
end;
|
|
|
|
function TJvChangeItem.GetDisplayName: string;
|
|
begin
|
|
if FDir <> '' then
|
|
Result := FDir
|
|
else
|
|
Result := inherited GetDisplayName;
|
|
end;
|
|
|
|
procedure TJvChangeItem.Change;
|
|
begin
|
|
if Assigned(FOnChange) then
|
|
FOnChange(Self);
|
|
end;
|
|
|
|
//=== { TJvChangeItems } =====================================================
|
|
|
|
constructor TJvChangeItems.Create(AOwner: TJvChangeNotify);
|
|
begin
|
|
inherited Create(TJvChangeItem);
|
|
FOwner := AOwner;
|
|
end;
|
|
|
|
function TJvChangeItems.Add: TJvChangeItem;
|
|
begin
|
|
if Count < MAXIMUM_WAIT_OBJECTS then
|
|
Result := TJvChangeItem(inherited Add)
|
|
else
|
|
{$IFDEF CLR}
|
|
raise EJVCLException.CreateFmt(RsEFmtMaxCountExceeded, [MAXIMUM_WAIT_OBJECTS]);
|
|
{$ELSE}
|
|
raise EJVCLException.CreateResFmt(@RsEFmtMaxCountExceeded, [MAXIMUM_WAIT_OBJECTS]);
|
|
{$ENDIF CLR}
|
|
end;
|
|
|
|
function TJvChangeItems.GetItem(Index: Integer): TJvChangeItem;
|
|
begin
|
|
Result := TJvChangeItem(inherited GetItem(Index));
|
|
end;
|
|
|
|
procedure TJvChangeItems.SetItem(Index: Integer; Value: TJvChangeItem);
|
|
begin
|
|
inherited SetItem(Index, Value);
|
|
end;
|
|
|
|
function TJvChangeItems.GetOwner: TPersistent;
|
|
begin
|
|
Result := FOwner;
|
|
end;
|
|
|
|
procedure TJvChangeItems.Assign(Source: TPersistent);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
if Source is TJvChangeItems then
|
|
begin
|
|
Clear;
|
|
for I := 0 to TJvChangeItems(Source).Count - 1 do
|
|
Add.Assign(TJvChangeItems(Source)[I]);
|
|
end
|
|
else
|
|
inherited Assign(Source);
|
|
end;
|
|
|
|
//=== { TJvChangeNotify } ====================================================
|
|
|
|
constructor TJvChangeNotify.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
FCollection := TJvChangeItems.Create(Self);
|
|
FActive := False;
|
|
FInterval := 100;
|
|
FFreeOnTerminate := True;
|
|
end;
|
|
|
|
destructor TJvChangeNotify.Destroy;
|
|
begin
|
|
if Assigned(FThread) then
|
|
FThread.FreeOnTerminate := False;
|
|
FFreeOnTerminate := False; // do not call SetFreeOnTerminate here
|
|
Active := False;
|
|
FCollection.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TJvChangeNotify.CheckActive(const Name: string);
|
|
begin
|
|
if Active and
|
|
not ((csDesigning in ComponentState) or (csLoading in ComponentState)) then //active is now published
|
|
{$IFDEF CLR}
|
|
raise EJVCLException.CreateFmt(RsEFmtCannotChangeName, [Name]);
|
|
{$ELSE}
|
|
raise EJVCLException.CreateResFmt(@RsEFmtCannotChangeName, [Name]);
|
|
{$ENDIF CLR}
|
|
end;
|
|
|
|
procedure TJvChangeNotify.SetCollection(const Value: TJvChangeItems);
|
|
begin
|
|
FCollection.Assign(Value);
|
|
end;
|
|
|
|
procedure TJvChangeNotify.Change(Item: TJvChangeItem);
|
|
begin
|
|
if Assigned(Item) then
|
|
begin
|
|
Item.Change;
|
|
if Assigned(FNotify) then
|
|
FNotify(Self, Item.Directory, Item.Actions);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvChangeNotify.SetInterval(const Value: Integer);
|
|
begin
|
|
CheckActive('Interval');
|
|
if Value <= 0 then
|
|
Exit;
|
|
if FInterval <> Value then
|
|
FInterval := Value;
|
|
end;
|
|
|
|
procedure TJvChangeNotify.NotifyError(const Msg: string);
|
|
var
|
|
ErrorMsg: string;
|
|
{$IFDEF CLR}
|
|
sb: StringBuilder;
|
|
{$ENDIF CLR}
|
|
begin
|
|
{$IFDEF CLR}
|
|
sb := StringBuilder.Create(256);
|
|
sb.Length := FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nil,
|
|
GetLastError, 0, sb, sb.Length, nil);
|
|
ErrorMsg := sb.ToString();
|
|
{$ELSE}
|
|
SetLength(ErrorMsg, 256);
|
|
SetLength(ErrorMsg, FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nil,
|
|
GetLastError, 0, PChar(ErrorMsg), Length(ErrorMsg), nil));
|
|
{$ENDIF CLR}
|
|
raise EJVCLChangeNotifyException.Create(ErrorMsg, Msg);
|
|
end;
|
|
|
|
procedure TJvChangeNotify.DoThreadChangeNotify(Sender: TObject; Index: Integer);
|
|
begin
|
|
Change(Notifications[Index]);
|
|
end;
|
|
|
|
procedure TJvChangeNotify.SetActive(const Value: Boolean);
|
|
const
|
|
cActions: array [TJvChangeAction] of Cardinal =
|
|
(FILE_NOTIFY_CHANGE_FILE_NAME, FILE_NOTIFY_CHANGE_DIR_NAME,
|
|
FILE_NOTIFY_CHANGE_ATTRIBUTES, FILE_NOTIFY_CHANGE_SIZE,
|
|
FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_NOTIFY_CHANGE_SECURITY);
|
|
var
|
|
cA: TJvChangeAction;
|
|
Flags: Cardinal;
|
|
I: Integer;
|
|
J: Integer;
|
|
S: string;
|
|
begin
|
|
if FActive <> Value then
|
|
begin
|
|
FActive := Value;
|
|
if csDesigning in ComponentState then
|
|
Exit; //active is now published
|
|
|
|
if FActive then
|
|
begin
|
|
if FCollection.Count > MAXIMUM_WAIT_OBJECTS then
|
|
{$IFDEF CLR}
|
|
raise EJVCLException.CreateFmt(RsEFmtMaxCountExceeded,[MAXIMUM_WAIT_OBJECTS]);
|
|
{$ELSE}
|
|
raise EJVCLException.CreateResFmt(@RsEFmtMaxCountExceeded,[MAXIMUM_WAIT_OBJECTS]);
|
|
{$ENDIF CLR}
|
|
{$IFDEF CLR}
|
|
for I := 0 to High(FNotifyArray) do
|
|
FNotifyArray[I] := INVALID_HANDLE_VALUE;
|
|
{$ELSE}
|
|
FillChar(FNotifyArray, SizeOf(TJvNotifyArray), INVALID_HANDLE_VALUE);
|
|
{$ENDIF CLR}
|
|
for I := 0 to FCollection.Count - 1 do
|
|
begin
|
|
Flags := 0;
|
|
{ convert TJvChangeActions to bitfields }
|
|
for cA := Low(TJvChangeAction) to High(TJvChangeAction) do
|
|
if cA in FCollection[I].Actions then
|
|
Flags := Flags or (cActions[cA]);
|
|
S := FCollection[I].Directory;
|
|
if (S = '') or not DirectoryExists(S) then
|
|
{$IFDEF CLR}
|
|
raise EJVCLException.CreateFmt(RsEFmtInvalidPathAtIndex, [S, I]);
|
|
{$ELSE}
|
|
raise EJVCLException.CreateResFmt(@RsEFmtInvalidPathAtIndex, [S, I]);
|
|
{$ENDIF CLR}
|
|
FNotifyArray[I] := FindFirstChangeNotification(
|
|
{$IFDEF CLR} S {$ELSE} PChar(S) {$ENDIF},
|
|
BOOL(FCollection[I].IncludeSubTrees), Flags);
|
|
if FNotifyArray[I] = INVALID_HANDLE_VALUE then
|
|
begin
|
|
// Clean up before raising the exception
|
|
for J := 0 to I - 1 do
|
|
begin
|
|
FindCloseChangeNotification(FNotifyArray[J]);
|
|
FNotifyArray[J] := INVALID_HANDLE_VALUE;
|
|
end;
|
|
FActive := False;
|
|
// Now raise the exception
|
|
NotifyError(FCollection[I].Directory);
|
|
end;
|
|
end;
|
|
if FThread <> nil then
|
|
begin
|
|
FThread.OnChangeNotify := nil;
|
|
FThread.Terminate;
|
|
if FThread.Suspended then
|
|
FThread.Resume;
|
|
if FreeOnTerminate then
|
|
FThread := nil
|
|
else
|
|
begin
|
|
FThread.WaitFor;
|
|
FreeAndNil(FThread);
|
|
end;
|
|
end;
|
|
FThread := TJvChangeThread.Create(FNotifyArray, FCollection.Count, FInterval, FFreeOnTerminate);
|
|
FThread.OnChangeNotify := DoThreadChangeNotify;
|
|
FThread.Resume;
|
|
end
|
|
else
|
|
if FThread <> nil then
|
|
begin
|
|
FThread.OnChangeNotify := nil;
|
|
FThread.Terminate;
|
|
if FThread.Suspended then
|
|
FThread.Resume;
|
|
if FreeOnTerminate then
|
|
FThread := nil
|
|
else
|
|
begin
|
|
FThread.WaitFor;
|
|
FreeAndNil(FThread);
|
|
end;
|
|
end;
|
|
|
|
{
|
|
while FActive do
|
|
begin
|
|
I := WaitForMultipleObjects(FCollection.Count, @FNotifyArray, False, FInterval);
|
|
if (I >= 0) and (I < FCollection.Count) then
|
|
begin
|
|
try
|
|
Change(FCollection.Items[I]);
|
|
finally
|
|
Assert(FindNextChangeNotification(FNotifyArray[I]));
|
|
end;
|
|
end
|
|
else
|
|
Application.ProcessMessages;
|
|
end;
|
|
for I := 0 to FCollection.Count - 1 do // Iterate
|
|
FindCloseChangeNotification(FNotifyArray[I]);
|
|
}
|
|
end;
|
|
end;
|
|
|
|
procedure TJvChangeNotify.Loaded;
|
|
begin
|
|
inherited Loaded;
|
|
if FActive then
|
|
begin
|
|
FActive := False;
|
|
SetActive(True);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvChangeNotify.SetFreeOnTerminate(const Value: Boolean);
|
|
var
|
|
State: Boolean;
|
|
begin
|
|
if csLoading in ComponentState then
|
|
FFreeOnTerminate := Value
|
|
else
|
|
begin
|
|
State := Active;
|
|
try
|
|
Active := False;
|
|
FFreeOnTerminate := Value;
|
|
finally
|
|
Active := State;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
//=== { TJvChangeThread } ====================================================
|
|
|
|
constructor TJvChangeThread.Create(NotifyArray: TJvNotifyArray; Count, Interval: Integer; AFreeOnTerminate:Boolean);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
inherited Create(True);
|
|
FCount := Count;
|
|
FInterval := Interval;
|
|
{$IFDEF CLR}
|
|
for I := 0 to High(FNotifyArray) do
|
|
FNotifyArray[I] := INVALID_HANDLE_VALUE;
|
|
{$ELSE}
|
|
FillChar(FNotifyArray, SizeOf(TJvNotifyArray), INVALID_HANDLE_VALUE);
|
|
{$ENDIF CLR}
|
|
for I := 0 to FCount - 1 do
|
|
FNotifyArray[I] := NotifyArray[I];
|
|
FreeOnTerminate := AFreeOnTerminate;
|
|
end;
|
|
|
|
procedure TJvChangeThread.Execute;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
// (rom) secure thread against exceptions (Delphi 5 needs it)
|
|
try
|
|
while not Terminated do
|
|
begin
|
|
I := WaitForMultipleObjects(FCount,
|
|
{$IFDEF CLR}
|
|
FNotifyArray,
|
|
{$ELSE}
|
|
@FNotifyArray[0],
|
|
{$ENDIF CLR}
|
|
False, FInterval);
|
|
if (I >= 0) and (I < FCount) and not Terminated then
|
|
begin
|
|
try
|
|
FIndex := I;
|
|
Synchronize(SynchChange);
|
|
finally
|
|
// (rom) raising an exception in a thread is not a good idea
|
|
// (rom) Assert removed
|
|
//Assert(FindNextChangeNotification(FNotifyArray[I]));
|
|
FindNextChangeNotification(FNotifyArray[I]);
|
|
end;
|
|
end;
|
|
end;
|
|
if Terminated then
|
|
for I := 0 to FCount - 1 do
|
|
if FNotifyArray[I] <> INVALID_HANDLE_VALUE then
|
|
begin
|
|
FindCloseChangeNotification(FNotifyArray[I]);
|
|
FNotifyArray[I] := INVALID_HANDLE_VALUE;
|
|
end;
|
|
except
|
|
end;
|
|
end;
|
|
|
|
procedure TJvChangeThread.SynchChange;
|
|
begin
|
|
if Assigned(FNotify) then
|
|
FNotify(Self, FIndex);
|
|
end;
|
|
|
|
{ EJVCLChangeNotifyException }
|
|
|
|
constructor EJVCLChangeNotifyException.Create(const ErrorMsg: string; const ErrorDirectory: string);
|
|
begin
|
|
inherited CreateFmt(RsENotifyErrorFmt, [ErrorMsg, ErrorDirectory]);
|
|
FErrorDirectory := ErrorDirectory;
|
|
end;
|
|
|
|
{$IFDEF UNITVERSIONING}
|
|
|
|
initialization
|
|
RegisterUnitVersion(HInstance, UnitVersioning);
|
|
|
|
finalization
|
|
UnregisterUnitVersion(HInstance);
|
|
{$ENDIF UNITVERSIONING}
|
|
|
|
end.
|
|
|