restemplate/indy/Protocols/IdIPAddrMon.pas

456 lines
11 KiB
Plaintext

{
$Project$
$Workfile$
$Revision$
$DateUTC$
$Id$
This file is part of the Indy (Internet Direct) project, and is offered
under the dual-licensing agreement described on the Indy website.
(http://www.indyproject.org/)
Copyright:
(c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
}
{
$Log$
}
{
Rev 1.7 10/26/2004 10:20:04 PM JPMugaas
Updated refs.
Rev 1.6 2004.02.03 5:45:14 PM czhower
Name changes
Rev 1.5 1/31/2004 1:18:40 PM JPMugaas
Illiminated Todo; item so it should work in DotNET.
Rev 1.4 1/21/2004 3:11:04 PM JPMugaas
InitComponent
Rev 1.3 10/19/2003 4:51:34 PM DSiders
Added localization comments.
Rev 1.2 2003.10.12 3:53:12 PM czhower
compile todos
Rev 1.1 3/5/2003 11:41:14 PM BGooijen
Added IdCoreGlobal to the uses, this file was needed for the call to
Sleep(...)
Rev 1.0 12/28/2002 3:04:52 PM DSiders
Initial revision.
}
unit IdIPAddrMon;
{
TIdIPAddrMon
Monitors adapters known to the IP protocol stack for changes in any
of the IP addresses. Similar to TIdIPWatch, but monitors all IP
addresses/adapters.
Does not keep a permanent IP address history list. But does trigger
a TIdIPAddrMonEvent event to signal the adapter number, old IP, and
new IP for the change in status.
OnStatusChanged is used to capture changed IP addresses, and/or
to sync with GUI display controls. If you do not assign a procedure
for the event handler, this component essentially does nothing except
eat small amounts of CPU time.
The thread instance is created and freed when the value in Active is
changed.
TIdIPAddrMonEvent
An procedure use to handle notifications from the component. Includes
parameters that represent the adapter number, previous IP or '<unknown>',
and the current IP or '<unknown>'.
TIdIPAddrMonThread
Timer thread for the IP address monitor component. Based on
TIdIPWatchThread.
Sleeps in increments of .5 seconds until the Interval has elapsed, and
fires the timer event. Sleep is called in increments to allow checking
for Terminated when a long Interval has been specified.
Original Author:
Don Siders, Integral Systems, Fri 27 Dec 2002
Donated to the Internet Direct (Indy) Project for use under the
terms of the Indy Dual License.
}
interface
{$i IdCompilerDefines.inc}
uses
Classes,
IdGlobal,
IdComponent,
IdThread;
const
IdIPAddrMonInterval = 500;
type
TIdIPAddrMonEvent = procedure(ASender: TObject; AAdapter: Integer; AOldIP, ANewIP: string) of object;
TIdIPAddrMonThread = class(TIdThread)
protected
FInterval: UInt32;
FOnTimerEvent: TNotifyEvent;
procedure Run; override;
procedure DoTimerEvent;
end;
TIdIPAddrMon = class(TIdComponent)
private
FActive: Boolean;
FBusy: Boolean;
FInterval: UInt32;
FAdapterCount: Integer;
FThread: TIdIPAddrMonThread;
// TODO: replace these with TIdStackLocalAddressList
FIPAddresses: TStrings;
FPreviousIPAddresses: TStrings;
FOnStatusChanged: TIdIPAddrMonEvent;
procedure SetActive(Value: Boolean);
procedure SetInterval(Value: UInt32);
procedure GetAdapterAddresses;
procedure DoStatusChanged;
protected
procedure InitComponent; override;
procedure Loaded; override;
public
destructor Destroy; override;
procedure CheckAdapters(Sender: TObject);
procedure ForceCheck;
property AdapterCount: Integer read FAdapterCount;
property Busy: Boolean read FBusy;
property IPAddresses: TStrings read FIPAddresses;
property Thread: TIdIPAddrMonThread read FThread;
published
property Active: Boolean read FActive write SetActive;
property Interval: UInt32 read FInterval write SetInterval default IdIPAddrMonInterval;
property OnStatusChanged: TIdIPAddrMonEvent read FOnStatusChanged write FOnStatusChanged;
end;
implementation
uses
{$IFDEF DOTNET}
{$IFDEF USE_INLINE}
System.Threading,
{$ENDIF}
{$ENDIF}
{$IFDEF USE_VCL_POSIX}
Posix.SysSelect,
Posix.SysTime,
{$ENDIF}
IdStack,
SysUtils;
procedure TIdIPAddrMon.InitComponent;
begin
inherited InitComponent;
FInterval := IdIPAddrMonInterval;
FActive := False;
FBusy := False;
FAdapterCount := 0;
// TODO: replace these with TIdStackLocalAddressList
FIPAddresses := TStringList.Create;
FPreviousIPAddresses := TStringList.Create;
// FThread created when component becomes Active
end;
destructor TIdIPAddrMon.Destroy;
begin
Active := False;
FBusy := False;
FIPAddresses.Free;
FPreviousIPAddresses.Free;
// FThread freed on Terminate
inherited Destroy;
end;
procedure TIdIPAddrMon.Loaded;
begin
inherited Loaded;
// Active = True must not be performed before all other props are loaded
if Active then begin
FActive := False;
Active := True;
end;
end;
procedure TIdIPAddrMon.CheckAdapters(Sender: TObject);
begin
// previous check could still be running...
if FBusy then begin
Exit;
end;
FBusy := True;
try
try
GetAdapterAddresses;
if IsDesignTime then begin
Exit;
end;
// TODO: replace with TIdStackLocalAddressList
{
LChanged := FPreviousIPAddresses.Count <> FIPAddresses.Count;
if not LChanged then
begin
for I := 0 to FIPAddresses.Count-1 do begin
LChanged := FPreviousIPAddresses[I].IPAddress.Count <> FIPAddresses[I].IPAddress;
if LChanged then begin
Break;
end;
end;
end;
if LChanged then begin
// something changed at runtime
DoStatusChanged;
end;
}
if (FPreviousIPAddresses.Count <> FIPAddresses.Count) or
(FPreviousIPAddresses.Text <> FIPAddresses.Text) then
begin
// something changed at runtime
DoStatusChanged;
end;
except
// eat any exception
end;
finally
FBusy := False;
end;
end;
procedure TIdIPAddrMon.DoStatusChanged;
var
iOldCount: Integer;
iNewCount: Integer;
iAdapter: Integer;
sOldIP: string;
sNewIP: string;
begin
if not Assigned(FOnStatusChanged) then
begin
Exit;
end;
// figure out the change... new, removed, or altered IP for adapter(s)
iOldCount := FPreviousIPAddresses.Count;
iNewCount := FIPAddresses.Count;
// find the new adapter IP address
if iOldCount < iNewCount then
begin
sOldIP := '<unknown>'; {do not localize}
for iAdapter := 0 to iNewCount - 1 do
begin
// TODO: replace with TIdStackLocalAddressList
{
sNewIP := FIPAddresses[iAdapter].IPAddress;
if FPreviousIPAddresses.IndexOfIP(sNewIP, FIPAddresses[iAdapter].IPVersion) = -1 then
begin
FOnStatusChanged(Self, iAdapter, sOldIP, sNewIP);
end;
}
sNewIP := FIPAddresses[iAdapter];
if FPreviousIPAddresses.IndexOf(sNewIP) = -1 then
begin
FOnStatusChanged(Self, iAdapter, sOldIP, sNewIP);
end;
end;
end
// find the missing adapter IP address
else if iOldCount > iNewCount then
begin
sNewIP := '<unknown>'; {do not localize}
for iAdapter := 0 to iOldCount - 1 do
begin
// TODO: replace with TIdStackLocalAddressList
{
sOldIP := FPreviousIPAddresses[iAdapter].IPAddress;
if FIPAddresses.IndexOfIP(sOldIP, FPreviousIPAddresses[iAdapter].IPVersion) = -1 then
begin
FOnStatusChanged(Self, iAdapter, sOldIP, sNewIP);
end;
}
sOldIP := FPreviousIPAddresses[iAdapter];
if FIPAddresses.IndexOf(sOldIP) = -1 then
begin
FOnStatusChanged(Self, iAdapter, sOldIP, sNewIP);
end;
end;
end
// find the altered adapter IP address
else
begin
for iAdapter := 0 to AdapterCount - 1 do
begin
// TODO: replace with TIdStackLocalAddressList
{
sOldIP := FPreviousIPAddresses[iAdapter].IPAddress;
sNewIP := FIPAddresses[iAdapter].IPAddress;
if (FPreviousIPAddresses[iAdapter].IPVersion <> FIPAddresses[iAdapter].IPVersion) or
(sOldIP <> sNewIP) then
begin
FOnStatusChanged(Self, iAdapter, sOldIP, sNewIP);
end;
}
sOldIP := FPreviousIPAddresses[iAdapter];
sNewIP := FIPAddresses[iAdapter];
if sOldIP <> sNewIP then
begin
FOnStatusChanged(Self, iAdapter, sOldIP, sNewIP);
end;
end;
end;
end;
procedure TIdIPAddrMon.ForceCheck;
begin
CheckAdapters(nil);
end;
procedure TIdIPAddrMon.SetActive(Value: Boolean);
begin
if Value <> FActive then
begin
if Value then
begin
// get initial addresses at start-up and allow display in IDE
GetAdapterAddresses;
end;
if (not IsDesignTime) and (not IsLoading) then
begin
if Value then
begin
FThread := TIdIPAddrMonThread.Create(True);
FThread.FOnTimerEvent := CheckAdapters;
FThread.FInterval := Self.Interval;
FThread.Start;
end else
begin
if FThread <> nil then begin
FThread.TerminateAndWaitFor;
FreeAndNil(FThread);
end;
end;
end;
FActive := Value;
end;
end;
procedure TIdIPAddrMon.SetInterval(Value: UInt32);
begin
FInterval := Value;
if Assigned(FThread) then begin
FThread.FInterval := FInterval;
end;
end;
procedure TIdIPAddrMonThread.Run;
var
lInterval: Integer;
begin
lInterval := FInterval;
while lInterval > 0 do
begin
// force a check for terminated every .5 sec
if lInterval > 500 then
begin
IndySleep(500);
lInterval := lInterval - 500;
end else
begin
IndySleep(lInterval);
LInterval := 0;
end;
if Terminated then
begin
Exit;
end;
end;
// interval has elapsed... fire the thread timer event
Synchronize(DoTimerEvent);
end;
procedure TIdIPAddrMonThread.DoTimerEvent;
begin
if Assigned(FOnTimerEvent) then begin
FOnTimerEvent(Self);
end;
end;
procedure TIdIPAddrMon.GetAdapterAddresses;
var
LAddresses: TIdStackLocalAddressList;
I: Integer;
begin
{
Doesn't keep a permanent history list like TIdIPWatch...
but does track previous IP addresses to detect changes.
}
FPreviousIPAddresses.Assign(FIPAddresses);
FIPAddresses.Clear;
LAddresses := TIdStackLocalAddressList.Create;
try
GStack.GetLocalAddressList(LAddresses);
for I := 0 to LAddresses.Count-1 do begin
FIPAddresses.Add(LAddresses[I].IPAddress);
end;
finally
LAddresses.Free;
end;
FAdapterCount := FIPAddresses.Count;
end;
end.