456 lines
11 KiB
Plaintext
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.
|