MainForm.pas 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  1. { $HDR$}
  2. {**********************************************************************}
  3. { Unit archived using Team Coherence }
  4. { Team Coherence is Copyright 2002 by Quality Software Components }
  5. { }
  6. { For further information / comments, visit our WEB site at }
  7. { http://www.TeamCoherence.com }
  8. {**********************************************************************}
  9. {}
  10. { $Log: 22976: MainForm.pas
  11. {
  12. { Rev 1.0 09/10/2003 3:16:16 PM Jeremy Darling
  13. { Project uploaded for the first time
  14. }
  15. {***************************************************************
  16. * Project : Indy 10 Chat Demo (Server)
  17. * Unit Name: MainForm.pas
  18. * Purpose : This is a simple demo application showing an easy
  19. * way to implement a simple chat system via indy.
  20. * Author : Jeremy Darling ([email protected])
  21. * Date : 09/10/03
  22. * Other Info : Please see the included index.htm files
  23. * History :
  24. * 09/09/03 - Support for standard telnet applied
  25. * 09/08/03 - Project created
  26. ****************************************************************}
  27. unit MainForm;
  28. interface
  29. uses
  30. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  31. IdBaseComponent, IdComponent, IdTCPServer, IdContext, StdCtrls, IdScheduler,
  32. IdSchedulerOfThread, IdSchedulerOfThreadDefault, CheckLst, ComCtrls, ExtCtrls,
  33. IdDsnCoreResourceStrings, IdStack, IdCoreGlobal, IdSocketHandle, ShellAPI,
  34. IniFiles, IdAntiFreezeBase, IdAntiFreeze, ChatContextData;
  35. type
  36. TfrmMain = class(TForm)
  37. Server: TIdTCPServer;
  38. pnlButtonBar: TPanel;
  39. pcMain: TPageControl;
  40. tsSettings: TTabSheet;
  41. Label2: TLabel;
  42. Label3: TLabel;
  43. Label4: TLabel;
  44. lbIPs: TCheckListBox;
  45. cbPorts: TComboBox;
  46. edPort: TEdit;
  47. tsProcessLog: TTabSheet;
  48. lbProcesses: TListBox;
  49. btnStartStop: TButton;
  50. IdAntiFreeze1: TIdAntiFreeze;
  51. IdSchedulerOfThreadDefault1: TIdSchedulerOfThreadDefault;
  52. tsGreeting: TTabSheet;
  53. Panel3: TPanel;
  54. lblUserNamePrompt: TLabel;
  55. edUserPrompt: TEdit;
  56. memGreeting: TMemo;
  57. btnTestClient: TButton;
  58. procedure btnStartStopClick(Sender: TObject);
  59. procedure FormCreate(Sender: TObject);
  60. procedure FormDestroy(Sender: TObject);
  61. procedure lbProcessesDrawItem(Control: TWinControl; Index: Integer;
  62. Rect: TRect; State: TOwnerDrawState);
  63. procedure ServerStatus(ASender: TObject; const AStatus: TIdStatus;
  64. const AStatusText: String);
  65. procedure ServerException(AContext: TIdContext; AException: Exception);
  66. procedure FormActivate(Sender: TObject);
  67. procedure ServerExecute(AContext: TIdContext);
  68. procedure ServerConnect(AContext: TIdContext);
  69. procedure ServerDisconnect(AContext: TIdContext);
  70. procedure edPortKeyPress(Sender: TObject; var Key: Char);
  71. procedure btnTestClientClick(Sender: TObject);
  72. private
  73. { Private declarations }
  74. function CheckStartOk : Boolean;
  75. function StartServer : Boolean;
  76. function StopServer : Boolean;
  77. procedure PopulateIPAddresses;
  78. function PortDescription(const PortNumber: integer): string;
  79. procedure LoadDefaultValues;
  80. procedure SaveDefaultValues;
  81. procedure CheckOptions;
  82. function GetServerOnline: Boolean;
  83. function InternalServerBeforeStart : Boolean;
  84. procedure InternalServerAfterStart;
  85. function InternalServerBeforeStop : Boolean;
  86. procedure InternalServerAfterStop;
  87. procedure Log(Msg : String; Color : TColor = clBlack);
  88. procedure SetControls;
  89. public
  90. { Public declarations }
  91. procedure WriteMessage(Msg : string);
  92. procedure MsgAvail(Sender: TChatContextData);
  93. property ServerOnline : Boolean read GetServerOnline;
  94. end;
  95. var
  96. frmMain : TfrmMain;
  97. Ini : TIniFile;
  98. implementation
  99. {$R *.DFM}
  100. procedure TfrmMain.btnStartStopClick(Sender: TObject);
  101. begin
  102. // This procedure should never change.
  103. if ServerOnline then
  104. StopServer
  105. else
  106. StartServer;
  107. end;
  108. function TfrmMain.CheckStartOk: Boolean;
  109. var
  110. i, c : Integer;
  111. begin
  112. // This section should stay the same, add your new code below
  113. i := 0;
  114. for c := 0 to lbIPs.Items.Count -1 do
  115. begin
  116. if lbIPs.Checked[c] then
  117. inc(i);
  118. end;
  119. result := i > 0;
  120. if not result then
  121. begin
  122. Log('Can''t start server until you select at least one IP to bind to.', clRed);
  123. MessageDlg('Can''t start server until you select at least one IP to bind to.', mtError, [mbOK], 0);
  124. end;
  125. // Add your code after this comment
  126. end;
  127. procedure TfrmMain.PopulateIPAddresses;
  128. var
  129. i : integer;
  130. begin
  131. // Again this section should not change
  132. with lbIPs do
  133. begin
  134. Clear;
  135. Items := GStack.LocalAddresses;
  136. Items.Insert(0, '127.0.0.1');
  137. end;
  138. try
  139. cbPorts.Items.Add(RSBindingAny);
  140. cbPorts.Items.BeginUpdate;
  141. for i := 0 to IdPorts.Count - 1 do
  142. cbPorts.Items.Add(PortDescription(Integer(IdPorts[i])));
  143. finally
  144. cbPorts.Items.EndUpdate;
  145. end;
  146. end;
  147. function TfrmMain.PortDescription(const PortNumber: integer): string;
  148. begin
  149. // Guess what more code that shouldn't change
  150. with GStack.WSGetServByPort(PortNumber) do
  151. try
  152. if PortNumber = 0 then
  153. begin
  154. Result := Format('%d: %s', [PortNumber, RSBindingAny]);
  155. end
  156. else
  157. begin
  158. Result := ''; {Do not Localize}
  159. if Count > 0 then
  160. begin
  161. Result := Format('%d: %s', [PortNumber, CommaText]); {Do not Localize}
  162. end;
  163. end;
  164. finally
  165. Free;
  166. end;
  167. end;
  168. function TfrmMain.StartServer: Boolean;
  169. var
  170. Binding : TIdSocketHandle;
  171. i : integer;
  172. SL : TStringList;
  173. begin
  174. // This code starts the server up and posts back information about
  175. // the server starting up.
  176. // You should place your pre and post startup code in InternalServerBeforeStart
  177. // and InternalServerAfterStart accordingly.
  178. Result := false;
  179. if not CheckStartOk then
  180. exit;
  181. SL := TStringList.Create;
  182. if not StopServer then
  183. begin
  184. Log( 'Error stopping server', clRed );
  185. Result := false;
  186. exit;
  187. end;
  188. Server.Bindings.Clear; // bindings cannot be cleared until TServer is inactive
  189. try
  190. try
  191. Server.DefaultPort := StrToInt(edPort.Text);
  192. for i := 0 to lbIPs.Items.Count - 1 do
  193. if lbIPs.Checked[i] then
  194. begin
  195. Binding := Server.Bindings.Add;
  196. Binding.IP := lbIPs.Items.Strings[i];
  197. Binding.Port := StrToInt( edPort.Text );
  198. Log( 'Server bound to IP ' + Binding.IP + ' on port ' + edPort.Text );
  199. end;
  200. if InternalServerBeforeStart then
  201. begin
  202. Server.Active := true;
  203. result := Server.Active;
  204. InternalServerAfterStart;
  205. if ServerOnline then
  206. begin
  207. Log( 'Server started', clGreen );
  208. btnStartStop.Caption := 'Stop Server';
  209. SetControls;
  210. end;
  211. end;
  212. except
  213. on E : Exception do
  214. begin
  215. Log( 'Server not started', clRed );
  216. Log( E.Message, clRed );
  217. Result := false;
  218. end;
  219. end;
  220. finally
  221. FreeAndNil( SL );
  222. end;
  223. end;
  224. function TfrmMain.StopServer: Boolean;
  225. var
  226. b : Boolean;
  227. begin
  228. // This code stops the server and posts back information about
  229. // the server shutting down.
  230. // You should place your pre and post shutdown code in InternalServerBeforeStop
  231. // and InternalServerAfterStop accordingly.
  232. Result := false;
  233. b := Server.Active;
  234. if InternalServerBeforeStop then
  235. begin
  236. Server.Active := false;
  237. Server.Bindings.Clear;
  238. Result := not Server.Active;
  239. if result then
  240. begin
  241. if b then
  242. Log( 'Server stopped', clGreen );
  243. end
  244. else
  245. begin
  246. Log( 'Server not stopped', clRed );
  247. end;
  248. InternalServerAfterStop;
  249. btnStartStop.Caption := 'Start Server';
  250. SetControls;
  251. end
  252. else
  253. Log( 'Server not stopped', clRed );
  254. end;
  255. procedure TfrmMain.FormCreate(Sender: TObject);
  256. begin
  257. // Initialization routines. You should find the appropriate procedure
  258. // to initialize your stuff below. The form create should hardly ever
  259. // need to be changed.
  260. Ini := TIniFile.Create(ChangeFileExt(ParamStr(0), '.ini'));
  261. pcMain.ActivePageIndex := 0;
  262. pcMain.Align := alClient;
  263. PopulateIPAddresses;
  264. StopServer;
  265. LoadDefaultValues;
  266. CheckOptions;
  267. end;
  268. procedure TfrmMain.FormDestroy(Sender: TObject);
  269. begin
  270. // If you created anything in form create or in one of the other
  271. // initialization routines then get rid of it here.
  272. StopServer;
  273. SaveDefaultValues;
  274. Ini.Free;
  275. end;
  276. procedure TfrmMain.LoadDefaultValues;
  277. var
  278. i, c : Integer;
  279. s : String;
  280. begin
  281. // This is were you get the chance to load values from the global
  282. // Ini file. The section for ports and IP's has been added in here
  283. // for you by default.
  284. edPort.Text := Ini.ReadString('Settings', 'Port', edPort.Text);
  285. c := Ini.ReadInteger('Settings', 'IPs', 0);
  286. for i := 1 to c do
  287. begin
  288. s := Ini.ReadString('Settings', 'IP' + IntToStr(i), '');
  289. if lbIPs.Items.IndexOf(s) > -1 then
  290. lbIPs.Checked[lbIPs.Items.IndexOf(s)] := true;
  291. end;
  292. c := Ini.ReadInteger('Greeting', 'GreetingLines', 0);
  293. if c > 0 then
  294. begin
  295. memGreeting.Lines.Clear;
  296. for i := 0 to c -1 do
  297. memGreeting.Lines.Add(Ini.ReadString('Greeting', 'GreetingLine' + IntToStr(i), ''));
  298. edUserPrompt.Text := Ini.ReadString('Greeting', 'UsernamePrompt', 'Username:');
  299. end;
  300. end;
  301. procedure TfrmMain.SaveDefaultValues;
  302. var
  303. i, c : Integer;
  304. begin
  305. // This is were you get the chance to save values to the global
  306. // Ini file. The section for ports and IP's has been added in here
  307. // for you by default.
  308. Ini.WriteString('Settings', 'Port', edPort.Text);
  309. c := 0;
  310. for i := 0 to lbIPs.Items.Count -1 do
  311. if lbIPs.Checked[i] then
  312. begin
  313. inc(c);
  314. Ini.WriteString('Settings', 'IP' + IntToStr(c), lbIPs.Items[i]);
  315. end;
  316. Ini.WriteInteger('Settings', 'IPs', c);
  317. Ini.WriteInteger('Placement', 'Top', Top);
  318. Ini.WriteInteger('Placement', 'Left', Left);
  319. Ini.WriteInteger('Greeting', 'GreetingLines', memGreeting.Lines.Count);
  320. for i := 0 to memGreeting.Lines.Count -1 do
  321. Ini.WriteString('Greeting', 'GreetingLine' + IntToStr(i), memGreeting.Lines[i]);
  322. Ini.WriteString('Greeting', 'UsernamePrompt', edUserPrompt.Text);
  323. end;
  324. procedure TfrmMain.CheckOptions;
  325. var
  326. i : Integer;
  327. opt : string;
  328. bDoAutoStart : Boolean;
  329. function OptName : String;
  330. begin
  331. if pos('=', opt) > 0 then
  332. begin
  333. result := copy(opt, 1, pos('=', opt) - 1);
  334. if result[1] in ['-', '/', '\'] then
  335. result := copy(result, 2, length(result));
  336. end
  337. else
  338. result := opt;
  339. end;
  340. function OptValue : String;
  341. begin
  342. if pos('=', opt) > 0 then
  343. result := copy(opt, pos('=', opt) + 1, length(opt))
  344. else
  345. result := opt;
  346. end;
  347. begin
  348. // The check options procedure should be used to check commandline options
  349. // if you wish to support command line options then please add it here.
  350. // By default port and autostart are supported.
  351. bDoAutoStart := false;
  352. for i := 1 to ParamCount do
  353. begin
  354. opt := LowerCase(ParamStr(i));
  355. if OptName = 'port' then
  356. edPort.Text := OptValue;
  357. if OptName = 'autostart' then
  358. bDoAutoStart := true;
  359. end;
  360. if bDoAutoStart then
  361. StartServer;
  362. end;
  363. function TfrmMain.GetServerOnline: Boolean;
  364. begin
  365. // Just a faster way then checking server.active for some
  366. result := Server.Active;
  367. end;
  368. procedure TfrmMain.lbProcessesDrawItem(Control: TWinControl;
  369. Index: Integer; Rect: TRect; State: TOwnerDrawState);
  370. begin
  371. // This draws the items in the Process Log in colors to allow quick
  372. // visual inspection
  373. with Control as TListBox do
  374. begin
  375. Canvas.Brush.Color := Color;
  376. Canvas.FillRect(Rect);
  377. Canvas.Font.Color := TColor(Items.Objects[Index]);
  378. Canvas.TextOut(Rect.Left + 2, Rect.Top, Items[Index]);
  379. end;
  380. end;
  381. procedure TfrmMain.Log(Msg: String; Color: TColor);
  382. begin
  383. // Simply adds a new item to the process log and then makes it the
  384. // currently selected item.
  385. lbProcesses.Items.AddObject(Msg, Pointer(Color));
  386. lbProcesses.ItemIndex := lbProcesses.Items.Count -1;
  387. end;
  388. procedure TfrmMain.ServerStatus(ASender: TObject; const AStatus: TIdStatus;
  389. const AStatusText: String);
  390. begin
  391. // Logs any ServerStatus messages to the Process Log
  392. Log(AStatusText);
  393. end;
  394. procedure TfrmMain.ServerException(AContext: TIdContext;
  395. AException: Exception);
  396. begin
  397. // Logs any server exceptions to the Process Log
  398. Log(AException.Message, clRed);
  399. end;
  400. function TfrmMain.InternalServerBeforeStart: Boolean;
  401. begin
  402. // Preform your startup code here. If you do not wish the server to start
  403. // then simply return false from this function and report back the proper
  404. // error by calling Log(YourMessage, clRed);
  405. try
  406. Server.Greeting.Text.Assign(memGreeting.Lines);
  407. result := true;
  408. except
  409. on E:Exception do
  410. begin
  411. Log(E.Message, clRed);
  412. result := false;
  413. end;
  414. end;
  415. end;
  416. procedure TfrmMain.InternalServerAfterStart;
  417. begin
  418. // Your code should go here. At this point the server is active.
  419. // So if you need to stop it then you should call StopServer
  420. // or for a hard halt call Server.Active := false;
  421. end;
  422. procedure TfrmMain.InternalServerAfterStop;
  423. begin
  424. // Your code should go here. At this point the server has been stoped.
  425. // So if you need to start it then you should call StartServer
  426. // or for a force start call Server.Active := true;
  427. end;
  428. function TfrmMain.InternalServerBeforeStop: Boolean;
  429. begin
  430. // Preform your shutdown code here. If you do not wish the server to stop
  431. // then simply return false from this function and report back the proper
  432. // error by calling Log(YourMessage, clRed);
  433. Result := true;
  434. end;
  435. procedure TfrmMain.SetControls;
  436. begin
  437. // Sets up the UI controls to either be enabled or disabled based upon
  438. // the current server state. See below for examples.
  439. lbIPs.Enabled := not ServerOnline;
  440. edPort.Enabled := not ServerOnline;
  441. cbPorts.Enabled := not ServerOnline;
  442. memGreeting.Enabled := not ServerOnline;
  443. edUserPrompt.Enabled := not ServerOnline;
  444. btnTestClient.Enabled := ServerOnline;
  445. end;
  446. procedure TfrmMain.FormActivate(Sender: TObject);
  447. begin
  448. Top := Ini.ReadInteger('Placement', 'Top', Top);
  449. Left:= Ini.ReadInteger('Placement', 'Left', Left);
  450. end;
  451. procedure TfrmMain.ServerExecute(AContext: TIdContext);
  452. begin
  453. // Your stuff for OnExecute goes here.
  454. TChatContextData(AContext.Data).CheckMsg(AContext);
  455. end;
  456. procedure TfrmMain.ServerConnect(AContext: TIdContext);
  457. var
  458. s : String;
  459. begin
  460. AContext.Data := TChatContextData.Create;
  461. TChatContextData(AContext.Data).OnMsgAvail := MsgAvail;
  462. s := edUserPrompt.Text + #13#10;
  463. AContext.Connection.IOHandler.WriteBuffer(s[1], Length(s));
  464. Log('Client connection established from ip: ' + AContext.Connection.LocalName, clBlue);
  465. end;
  466. procedure TfrmMain.ServerDisconnect(AContext: TIdContext);
  467. begin
  468. Log('Client connection removed from ip: ' + AContext.Connection.LocalName, clBlue);
  469. AContext.Data.Free;
  470. AContext.Data := nil;
  471. end;
  472. procedure TfrmMain.edPortKeyPress(Sender: TObject; var Key: Char);
  473. begin
  474. if not (Key in ['0', '1'..'9', #8]) then
  475. Key := #0;
  476. end;
  477. procedure TfrmMain.btnTestClientClick(Sender: TObject);
  478. begin
  479. ShellExecute(handle, 'OPEN', 'Telnet', PChar('localhost ' + IntToStr(Server.DefaultPort)), '', SW_SHOWNORMAL );
  480. end;
  481. procedure TfrmMain.WriteMessage(Msg: string);
  482. var
  483. cList : TList;
  484. Count : Integer;
  485. begin
  486. cList := Server.Contexts.LockList;
  487. try
  488. for Count := 0 to cList.Count -1 do
  489. begin
  490. with TIdContext(cList[Count]) do
  491. begin
  492. Connection.IOHandler.WriteBuffer(Msg[1], Length(Msg));
  493. end;
  494. end;
  495. finally
  496. Server.Contexts.UnlockList;
  497. end;
  498. end;
  499. procedure TfrmMain.MsgAvail(Sender: TChatContextData);
  500. var
  501. s : String;
  502. begin
  503. s := Sender.Pop;
  504. s := s + #10#13;
  505. WriteMessage(s);
  506. end;
  507. end.