IdIPAddrMon.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. {
  2. $Project$
  3. $Workfile$
  4. $Revision$
  5. $DateUTC$
  6. $Id$
  7. This file is part of the Indy (Internet Direct) project, and is offered
  8. under the dual-licensing agreement described on the Indy website.
  9. (http://www.indyproject.org/)
  10. Copyright:
  11. (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
  12. }
  13. {
  14. $Log$
  15. }
  16. {
  17. Rev 1.7 10/26/2004 10:20:04 PM JPMugaas
  18. Updated refs.
  19. Rev 1.6 2004.02.03 5:45:14 PM czhower
  20. Name changes
  21. Rev 1.5 1/31/2004 1:18:40 PM JPMugaas
  22. Illiminated Todo; item so it should work in DotNET.
  23. Rev 1.4 1/21/2004 3:11:04 PM JPMugaas
  24. InitComponent
  25. Rev 1.3 10/19/2003 4:51:34 PM DSiders
  26. Added localization comments.
  27. Rev 1.2 2003.10.12 3:53:12 PM czhower
  28. compile todos
  29. Rev 1.1 3/5/2003 11:41:14 PM BGooijen
  30. Added IdCoreGlobal to the uses, this file was needed for the call to
  31. Sleep(...)
  32. Rev 1.0 12/28/2002 3:04:52 PM DSiders
  33. Initial revision.
  34. }
  35. unit IdIPAddrMon;
  36. {
  37. TIdIPAddrMon
  38. Monitors adapters known to the IP protocol stack for changes in any
  39. of the IP addresses. Similar to TIdIPWatch, but monitors all IP
  40. addresses/adapters.
  41. Does not keep a permanent IP address history list. But does trigger
  42. a TIdIPAddrMonEvent event to signal the adapter number, old IP, and
  43. new IP for the change in status.
  44. OnStatusChanged is used to capture changed IP addresses, and/or
  45. to sync with GUI display controls. If you do not assign a procedure
  46. for the event handler, this component essentially does nothing except
  47. eat small amounts of CPU time.
  48. The thread instance is created and freed when the value in Active is
  49. changed.
  50. TIdIPAddrMonEvent
  51. An procedure use to handle notifications from the component. Includes
  52. parameters that represent the adapter number, previous IP or '<unknown>',
  53. and the current IP or '<unknown>'.
  54. TIdIPAddrMonThread
  55. Timer thread for the IP address monitor component. Based on
  56. TIdIPWatchThread.
  57. Sleeps in increments of .5 seconds until the Interval has elapsed, and
  58. fires the timer event. Sleep is called in increments to allow checking
  59. for Terminated when a long Interval has been specified.
  60. Original Author:
  61. Don Siders, Integral Systems, Fri 27 Dec 2002
  62. Donated to the Internet Direct (Indy) Project for use under the
  63. terms of the Indy Dual License.
  64. }
  65. interface
  66. {$i IdCompilerDefines.inc}
  67. uses
  68. Classes,
  69. IdGlobal,
  70. IdComponent,
  71. IdThread;
  72. const
  73. IdIPAddrMonInterval = 500;
  74. type
  75. TIdIPAddrMonEvent = procedure(ASender: TObject; AAdapter: Integer; AOldIP, ANewIP: string) of object;
  76. TIdIPAddrMonThread = class(TIdThread)
  77. protected
  78. FInterval: UInt32;
  79. FOnTimerEvent: TNotifyEvent;
  80. procedure Run; override;
  81. procedure DoTimerEvent;
  82. end;
  83. TIdIPAddrMon = class(TIdComponent)
  84. private
  85. FActive: Boolean;
  86. FBusy: Boolean;
  87. FInterval: UInt32;
  88. FAdapterCount: Integer;
  89. FThread: TIdIPAddrMonThread;
  90. // TODO: replace these with TIdStackLocalAddressList
  91. FIPAddresses: TStrings;
  92. FPreviousIPAddresses: TStrings;
  93. FOnStatusChanged: TIdIPAddrMonEvent;
  94. procedure SetActive(Value: Boolean);
  95. procedure SetInterval(Value: UInt32);
  96. procedure GetAdapterAddresses;
  97. procedure DoStatusChanged;
  98. protected
  99. procedure InitComponent; override;
  100. procedure Loaded; override;
  101. public
  102. destructor Destroy; override;
  103. procedure CheckAdapters(Sender: TObject);
  104. procedure ForceCheck;
  105. property AdapterCount: Integer read FAdapterCount;
  106. property Busy: Boolean read FBusy;
  107. property IPAddresses: TStrings read FIPAddresses;
  108. property Thread: TIdIPAddrMonThread read FThread;
  109. published
  110. property Active: Boolean read FActive write SetActive;
  111. property Interval: UInt32 read FInterval write SetInterval default IdIPAddrMonInterval;
  112. property OnStatusChanged: TIdIPAddrMonEvent read FOnStatusChanged write FOnStatusChanged;
  113. end;
  114. implementation
  115. uses
  116. {$IFDEF DOTNET}
  117. {$IFDEF USE_INLINE}
  118. System.Threading,
  119. {$ENDIF}
  120. {$ENDIF}
  121. {$IFDEF USE_VCL_POSIX}
  122. Posix.SysSelect,
  123. Posix.SysTime,
  124. {$ENDIF}
  125. IdStack,
  126. SysUtils;
  127. procedure TIdIPAddrMon.InitComponent;
  128. begin
  129. inherited InitComponent;
  130. FInterval := IdIPAddrMonInterval;
  131. FActive := False;
  132. FBusy := False;
  133. FAdapterCount := 0;
  134. // TODO: replace these with TIdStackLocalAddressList
  135. FIPAddresses := TStringList.Create;
  136. FPreviousIPAddresses := TStringList.Create;
  137. // FThread created when component becomes Active
  138. end;
  139. destructor TIdIPAddrMon.Destroy;
  140. begin
  141. Active := False;
  142. FBusy := False;
  143. FIPAddresses.Free;
  144. FPreviousIPAddresses.Free;
  145. // FThread freed on Terminate
  146. inherited Destroy;
  147. end;
  148. procedure TIdIPAddrMon.Loaded;
  149. begin
  150. inherited Loaded;
  151. // Active = True must not be performed before all other props are loaded
  152. if Active then begin
  153. FActive := False;
  154. Active := True;
  155. end;
  156. end;
  157. procedure TIdIPAddrMon.CheckAdapters(Sender: TObject);
  158. begin
  159. // previous check could still be running...
  160. if FBusy then begin
  161. Exit;
  162. end;
  163. FBusy := True;
  164. try
  165. try
  166. GetAdapterAddresses;
  167. if IsDesignTime then begin
  168. Exit;
  169. end;
  170. // TODO: replace with TIdStackLocalAddressList
  171. {
  172. LChanged := FPreviousIPAddresses.Count <> FIPAddresses.Count;
  173. if not LChanged then
  174. begin
  175. for I := 0 to FIPAddresses.Count-1 do begin
  176. LChanged := FPreviousIPAddresses[I].IPAddress.Count <> FIPAddresses[I].IPAddress;
  177. if LChanged then begin
  178. Break;
  179. end;
  180. end;
  181. end;
  182. if LChanged then begin
  183. // something changed at runtime
  184. DoStatusChanged;
  185. end;
  186. }
  187. if (FPreviousIPAddresses.Count <> FIPAddresses.Count) or
  188. (FPreviousIPAddresses.Text <> FIPAddresses.Text) then
  189. begin
  190. // something changed at runtime
  191. DoStatusChanged;
  192. end;
  193. except
  194. // eat any exception
  195. end;
  196. finally
  197. FBusy := False;
  198. end;
  199. end;
  200. procedure TIdIPAddrMon.DoStatusChanged;
  201. var
  202. iOldCount: Integer;
  203. iNewCount: Integer;
  204. iAdapter: Integer;
  205. sOldIP: string;
  206. sNewIP: string;
  207. begin
  208. if not Assigned(FOnStatusChanged) then
  209. begin
  210. Exit;
  211. end;
  212. // figure out the change... new, removed, or altered IP for adapter(s)
  213. iOldCount := FPreviousIPAddresses.Count;
  214. iNewCount := FIPAddresses.Count;
  215. // find the new adapter IP address
  216. if iOldCount < iNewCount then
  217. begin
  218. sOldIP := '<unknown>'; {do not localize}
  219. for iAdapter := 0 to iNewCount - 1 do
  220. begin
  221. // TODO: replace with TIdStackLocalAddressList
  222. {
  223. sNewIP := FIPAddresses[iAdapter].IPAddress;
  224. if FPreviousIPAddresses.IndexOfIP(sNewIP, FIPAddresses[iAdapter].IPVersion) = -1 then
  225. begin
  226. FOnStatusChanged(Self, iAdapter, sOldIP, sNewIP);
  227. end;
  228. }
  229. sNewIP := FIPAddresses[iAdapter];
  230. if FPreviousIPAddresses.IndexOf(sNewIP) = -1 then
  231. begin
  232. FOnStatusChanged(Self, iAdapter, sOldIP, sNewIP);
  233. end;
  234. end;
  235. end
  236. // find the missing adapter IP address
  237. else if iOldCount > iNewCount then
  238. begin
  239. sNewIP := '<unknown>'; {do not localize}
  240. for iAdapter := 0 to iOldCount - 1 do
  241. begin
  242. // TODO: replace with TIdStackLocalAddressList
  243. {
  244. sOldIP := FPreviousIPAddresses[iAdapter].IPAddress;
  245. if FIPAddresses.IndexOfIP(sOldIP, FPreviousIPAddresses[iAdapter].IPVersion) = -1 then
  246. begin
  247. FOnStatusChanged(Self, iAdapter, sOldIP, sNewIP);
  248. end;
  249. }
  250. sOldIP := FPreviousIPAddresses[iAdapter];
  251. if FIPAddresses.IndexOf(sOldIP) = -1 then
  252. begin
  253. FOnStatusChanged(Self, iAdapter, sOldIP, sNewIP);
  254. end;
  255. end;
  256. end
  257. // find the altered adapter IP address
  258. else
  259. begin
  260. for iAdapter := 0 to AdapterCount - 1 do
  261. begin
  262. // TODO: replace with TIdStackLocalAddressList
  263. {
  264. sOldIP := FPreviousIPAddresses[iAdapter].IPAddress;
  265. sNewIP := FIPAddresses[iAdapter].IPAddress;
  266. if (FPreviousIPAddresses[iAdapter].IPVersion <> FIPAddresses[iAdapter].IPVersion) or
  267. (sOldIP <> sNewIP) then
  268. begin
  269. FOnStatusChanged(Self, iAdapter, sOldIP, sNewIP);
  270. end;
  271. }
  272. sOldIP := FPreviousIPAddresses[iAdapter];
  273. sNewIP := FIPAddresses[iAdapter];
  274. if sOldIP <> sNewIP then
  275. begin
  276. FOnStatusChanged(Self, iAdapter, sOldIP, sNewIP);
  277. end;
  278. end;
  279. end;
  280. end;
  281. procedure TIdIPAddrMon.ForceCheck;
  282. begin
  283. CheckAdapters(nil);
  284. end;
  285. procedure TIdIPAddrMon.SetActive(Value: Boolean);
  286. begin
  287. if Value <> FActive then
  288. begin
  289. if Value then
  290. begin
  291. // get initial addresses at start-up and allow display in IDE
  292. GetAdapterAddresses;
  293. end;
  294. if (not IsDesignTime) and (not IsLoading) then
  295. begin
  296. if Value then
  297. begin
  298. FThread := TIdIPAddrMonThread.Create(True);
  299. FThread.FOnTimerEvent := CheckAdapters;
  300. FThread.FInterval := Self.Interval;
  301. FThread.Start;
  302. end
  303. else if FThread <> nil then begin
  304. FThread.TerminateAndWaitFor;
  305. FreeAndNil(FThread);
  306. end;
  307. end;
  308. FActive := Value;
  309. end;
  310. end;
  311. procedure TIdIPAddrMon.SetInterval(Value: UInt32);
  312. begin
  313. FInterval := Value;
  314. if Assigned(FThread) then begin
  315. FThread.FInterval := FInterval;
  316. end;
  317. end;
  318. procedure TIdIPAddrMonThread.Run;
  319. var
  320. lInterval: Integer;
  321. begin
  322. lInterval := FInterval;
  323. while lInterval > 0 do
  324. begin
  325. // force a check for terminated every .5 sec
  326. if lInterval > 500 then
  327. begin
  328. IndySleep(500);
  329. lInterval := lInterval - 500;
  330. end else
  331. begin
  332. IndySleep(lInterval);
  333. LInterval := 0;
  334. end;
  335. if Terminated then
  336. begin
  337. Exit;
  338. end;
  339. end;
  340. // interval has elapsed... fire the thread timer event
  341. Synchronize(DoTimerEvent);
  342. end;
  343. procedure TIdIPAddrMonThread.DoTimerEvent;
  344. begin
  345. if Assigned(FOnTimerEvent) then begin
  346. FOnTimerEvent(Self);
  347. end;
  348. end;
  349. procedure TIdIPAddrMon.GetAdapterAddresses;
  350. var
  351. LAddresses: TIdStackLocalAddressList;
  352. I: Integer;
  353. begin
  354. {
  355. Doesn't keep a permanent history list like TIdIPWatch...
  356. but does track previous IP addresses to detect changes.
  357. }
  358. FPreviousIPAddresses.Assign(FIPAddresses);
  359. FIPAddresses.Clear;
  360. LAddresses := TIdStackLocalAddressList.Create;
  361. try
  362. GStack.GetLocalAddressList(LAddresses);
  363. for I := 0 to LAddresses.Count-1 do begin
  364. FIPAddresses.Add(LAddresses[I].IPAddress);
  365. end;
  366. finally
  367. LAddresses.Free;
  368. end;
  369. FAdapterCount := FIPAddresses.Count;
  370. end;
  371. end.