Indy10 即时通讯Demo

最近闲来无事,重新学习了Indy10,顺手写了一段即时通讯代码。与上次写的笔记有不同之处,但差别不大。

未研究过TCP打洞技术,所以下面的代码采用的是  客户端--服务器--客户端  模式,也就是服务器端转发消息的模式。

 客户端模仿了QQ,可以在屏幕四周停靠自动隐藏

本文也演示了在线程中操作VCL的两张方法:

1:向主线程发送消息

2:在线程中使用临界区

program Server;

uses
  Forms,
  UntMain in 'UntMain.pas' {Form2},
  Unit2 in 'Unit2.pas',
  Unit4 in 'Unit4.pas';

{$R *.res}

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TForm2, Form2);
  Application.Run;
end.

服务器端:

unit UntMain;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, IdContext, IdScheduler, IdSchedulerOfThread, IdSchedulerOfThreadPool,
  IdBaseComponent, IdComponent, IdCustomTCPServer, IdTCPServer, ImgList,
  CoolTrayIcon, ExtCtrls, RzPanel, Unit2, IdGlobal, StdCtrls, RzLstBox,
  IdSchedulerOfThreadDefault, RzStatus, RzButton, RzEdit,SyncObjs;

type
  TForm2 = class(TForm)
    CoolTrayIcon1: TCoolTrayIcon;
    ImageList1: TImageList;
    IdTCPServer1: TIdTCPServer;
    RzStatusBar1: TRzStatusBar;
    RzListBox1: TRzListBox;
    IdSchedulerOfThreadDefault1: TIdSchedulerOfThreadDefault;
    Button1: TButton;
    RzStatusPane1: TRzStatusPane;
    RzStatusPane2: TRzStatusPane;
    RzMemo1: TRzMemo;
    RzButton1: TRzButton;
    RzMemo2: TRzMemo;
    Timer1: TTimer;
    procedure IdTCPServer1Execute(AContext: TIdContext);
    procedure CustomMessage(var message: TMessage); message CustMsg;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure Button1Click(Sender: TObject);
    procedure IdTCPServer1Disconnect(AContext: TIdContext);
    procedure RzButton1Click(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
  private
    { Private declarations }

  public
    { Public declarations }
  end;
  //TIdServerContext 类继承自 TIdContext类
  //IdCustomTCPServer 单元 第295行
  TMyClass = class(TIdServerContext)
    CltInfo: TCltInfo;
  end;

var
  Form2: TForm2;
  CriticalSection:TCriticalSection;
implementation

{$R *.dfm}
uses
  Unit4;
procedure TForm2.Button1Click(Sender: TObject);
begin
  IdTCPServer1.Active := True;
  if IdTCPServer1.Active then
  begin
    RzMemo1.Lines.Add('服务器开启成功...');
  end;
end;

procedure TForm2.CustomMessage(var message: TMessage);
var
  i,n: Integer;
  ss,ip,Nc,sNc: string;
  buf:TDataPack;
  list:Tlist;
  FContext:TIdContext;
begin
  FContext := TMyClass(message.LParam);
  case message.WParam of
    CltConnect:
    begin
      ss:='';
      Nc := TMyClass(FContext).CltInfo.CltName;
      ip:= TMyClass(FContext).CltInfo.CltIP;
      RzListBox1.Items.Add(Nc);
      RzMemo2.Lines.Add('【客户:】' + Nc + ' (' + ip +') 登陆'+'---'+DateTimeToStr(Now));

      for i := 0 to RzListBox1.Items.Count - 1 do // 发送连线客户端列表
        ss:=ss+form2.RzListBox1.ItemCaption(i)+'|';
      sNc :=Encrystrings(ss);
      FillChar(buf, SizeOf(TDataPack), '');
      buf.Command := CltList;
      StrCopy(@buf.Data, PChar(sNc));
      List := form2.IdTCPServer1.Contexts.LockList;
      n:= List.Count;
      try
        for I := 0 to n-1 do
        begin
          try

            TMyClass(List.Items[i]).Connection.IOHandler.Write(RawToBytes(buf, SizeOf(TDataPack)), SizeOf(TDataPack));
          except
            //
          end;
        end;
      finally
        form2.IdTCPServer1.Contexts.UnlockList;
      end;
    end;

    CltDisconnect:
      begin
        for i := 0 to RzListBox1.Items.Count - 1 do
        begin
          if RzListBox1.ItemCaption(i) = TMyClass(FContext).CltInfo.CltName  then
          begin

            RzListBox1.Items.Delete(i);
            RzMemo2.Lines.Add('【用户:】 '+ string(TMyClass(FContext).CltInfo.CltName) +'  离开---'+DateTimeToStr(Now));
            Break;
          end;
        end;

        FillChar(buf, SizeOf(TDataPack), '');
        ss := '';

        for i := 0 to RzListBox1.Items.Count - 1 do // 发送连线客户端列表
          ss := ss + Form2.RzListBox1.ItemCaption(i) + '|';
        ss:=Encrystrings(ss);
        buf.Command := CltList;
        StrCopy(@buf.Data, PChar(ss));
        list:= IdTCPServer1.Contexts.LockList;
        n:= List.Count;
        try
          for i := 0 to n - 1 do
          try
            TMyClass(List.Items[i]).Connection.IOHandler.Write(RawToBytes(buf, SizeOf(TDataPack)), SizeOf(TDataPack));
          except
            //
          end;
        finally
          IdTCPServer1.Contexts.UnlockList;
        end;
      end;
    CltSendMessage:
      begin

      end;
  end;
end;

procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin

  RzListBox1.Clear;
  IdTCPServer1.Active := False;
end;

procedure TForm2.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
var
  List:TList;
  i,n:Integer;
  LContext: TMyClass;
  buf:TDataPack;
begin
  //当有客户端尚未断开连接时,服务器主动断开连接会导致异常
  //所以,在服务器端退出之前,检查时候有客户端尚未断开
  //若有,通知客户端主动断开连接
  List:= IdTCPServer1.Contexts.LockList;
  n:= List.Count;
  try
    if n >0 then
    begin
      CanClose := False;
      FillChar(buf,SizeOf(TdataPack),'');
      buf.Command := SrvCloseQuery;
      for I := 0 to n - 1 do
      begin
        LContext := TMyClass(List.Items[i]);
        LContext.Connection.IOHandler.Write(RawToBytes(buf, SizeOf(TDataPack)), SizeOf(TDataPack));
      end;
    end else CanClose := True;
  finally
    IdTCPServer1.Contexts.UnlockList;
  end;
end;

procedure TForm2.FormCreate(Sender: TObject);
begin
  //在IdCustomTCPServer 单元第302行,定义了类的指针:
  //TIdServerContextClass = class of TIdServerContext;
  //AContext不确定以 TIdServerContext类创建,所以定义了一个类的指针TIdServerContextClass,
  //AContext将以TIdServerContextClass指针所指向的类来创建,重新赋值指针,将以新类创建实例

  //这里重新赋值AContext 新类,当客户端连接后,AContext将以新类TMyClass的实例创捷
  //AContext 被创建后,将包含TMyClass类的新属性 TCltInfo
  //详见IdCustomTCPServer 单元第956行
  //如果不重新赋值AContext新类,AContext 在IdCustomTCPServer初始化时(TIdCustomTCPServer.InitComponent方法),
  //以默认类TIdServerContext创建
  //详见 IdCustomTCPServer 单元第812行
  //这里我们需要给AContext 添加新属性 TCltInfo 用来保存客户端信息
  //所以,以TIdServerContext 为基类,我们扩展出  TMyClass 子类
  //每个客户端连接后,AContext即被创建,并把每个AContext地址(对象指针)保存在IdTCPServer.Contexts属性中
  //当服务器端需要与某个客户端回话时,可以遍历Contexts属性
  IdTCPServer1.ContextClass := TMyClass;
  IdTCPServer1.Active := True;
  if IdTCPServer1.Active then
  begin
    RzMemo1.Lines.Add('服务器开启成功...('+ DateTimeToStr(Now) + ')');
  end;
  CriticalSection:=TCriticalSection.Create;
end;

procedure TForm2.FormDestroy(Sender: TObject);
begin
  CriticalSection.Free;
end;

procedure TForm2.IdTCPServer1Disconnect(AContext: TIdContext);
begin
  SendMessage(Handle,CustMsg,CltDisconnect,LongInt(AContext));
end;

procedure TForm2.IdTCPServer1Execute(AContext: TIdContext);
var
  BByte: TIdBytes;
  buf: TDataPack;
  i,n: Integer;
  s,ss,ds,nr,Nc,ip:string;
  List:Tlist;
begin
  FillChar(buf, SizeOf(TDataPack), '');
  AContext.Connection.IOHandler.ReadBytes(BByte, SizeOf(TDataPack), False);
  BytesToRaw(BByte, buf, SizeOf(TDataPack));
//---------------------------------------------------------------------------------------
  case buf.Command of
    CltConnect:
      begin
        ss:='';
        s:= string(buf.CltInfo.CltName);
        Nc :=Uncrystrings(s);
        ip:=AContext.Binding.PeerIP;
        StrCopy(@TMyClass(AContext).CltInfo.CltName,PChar(Nc)) ;
        StrCopy(@TMyClass(AContext).CltInfo.CltIP,PChar(ip));
        Nc :=Uncrystrings(s);
        for i := 0 to RzListBox1.Items.Count - 1 do
        begin
          if RzListBox1.Items[i]=Nc then
          begin
            buf.Command := CltDisconnect;
            AContext.Connection.IOHandler.Write(RawToBytes(buf, SizeOf(TDataPack)), SizeOf(TDataPack));
            Exit;
          end;
        end;
        SendMessage(Handle,CustMsg,CltConnect,LongInt(AContext));
      end;
//------------------------------------------------------------------------------------------------
    CltSendMessage:
      begin
        s:= Uncrystrings(string(buf.CltInfo.CltName));
        ds:=Uncrystrings(string(buf.DstInfo.CltName));
        nr:=Uncrystrings(string(buf.Data)) +#13+#10;
        List := form2.IdTCPServer1.Contexts.LockList;
        n:= List.Count;
        try
          for i := 0 to n - 1 do
          begin
            if TMyClass(List.Items[i]).CltInfo.CltName = ds then
            begin
              try
                CriticalSection.Enter;
                try
                  TMyClass(List.Items[i]).Connection.IOHandler.Write(RawToBytes(buf, SizeOf(TDataPack)), SizeOf(TDataPack));
                  RzMemo1.Lines.Add(s + '对 '+ds + ' 说:'+ nr);
                finally
                  CriticalSection.Leave;
                end;
              except
                buf.Command := SrvMessage;
                AContext.Connection.IOHandler.Write(RawToBytes(buf, SizeOf(TDataPack)), SizeOf(TDataPack));
              end;
              Exit;
            end;
          end;
        finally
          form2.IdTCPServer1.Contexts.UnlockList;
        end;
      end;
//--------------------------------------------------------------------------------------------------------
    CltTimer :
    begin
      AContext.Connection.IOHandler.Write(RawToBytes(buf, SizeOf(TDataPack)), SizeOf(TDataPack));
    end;
//---------------------------------------------------------------------------------------------------------
    CltClear :
    begin
      s:= Uncrystrings(string(buf.CltInfo.CltName));
      ds:=Uncrystrings(string(buf.DstInfo.CltName));
      List := form2.IdTCPServer1.Contexts.LockList;
      n:= List.Count;
      try
        for i := 0 to n - 1 do
        begin
          if TMyClass(List.Items[i]).CltInfo.CltName = ds then
          begin
            try
              CriticalSection.Enter;
              try
                TMyClass(List.Items[i]).Connection.IOHandler.Write(RawToBytes(buf, SizeOf(TDataPack)), SizeOf(TDataPack));
                RzMemo1.Lines.Add(s + ' 清除了 '+ds + ' 的屏幕'+#13+#10);
              finally
                CriticalSection.Leave;
              end;
            except
              //
            end;
            Exit;
          end;
        end;
      finally
        form2.IdTCPServer1.Contexts.UnlockList;
      end;
    end;
//-------------------------------------------------------------------------------------------------------
    CltLockSrc:
    begin
      s:= Uncrystrings(string(buf.CltInfo.CltName));
      List := form2.IdTCPServer1.Contexts.LockList;
      n:= List.Count;
      try
        for i := 0 to n - 1 do
        begin
          if TMyClass(List.Items[i]).CltInfo.CltName <> s then
          begin
            try
              CriticalSection.Enter;
              try
                TMyClass(List.Items[i]).Connection.IOHandler.Write(RawToBytes(buf, SizeOf(TDataPack)), SizeOf(TDataPack));
              finally
                CriticalSection.Leave;
              end;
            except
              //
            end;
          end;
        end;
      finally
        form2.IdTCPServer1.Contexts.UnlockList;
      end;
      RzMemo1.Lines.Add(s + ' 锁定了屏幕 '+#13+#10);
    end;
//-------------------------------------------------------------------------------------------------------
    CltUnlockSrc :
    begin
      s:= Uncrystrings(string(buf.CltInfo.CltName));
      List := form2.IdTCPServer1.Contexts.LockList;
      n:= List.Count;
      try
        for i := 0 to n - 1 do
        begin
          if TMyClass(List.Items[i]).CltInfo.CltName <> s then
          begin
            try
              TMyClass(List.Items[i]).Connection.IOHandler.Write(RawToBytes(buf, SizeOf(TDataPack)), SizeOf(TDataPack));

            except
              //
            end;
          end;
        end;
      finally
        form2.IdTCPServer1.Contexts.UnlockList;
      end;
      RzMemo1.Lines.Add(s + ' 解锁了屏幕 '+#13+#10);
    end;
//---------------------------------------------------------------------------------------------------------------
    CltMessage :
    begin
      ds:=Uncrystrings(string(buf.DstInfo.CltName));
      List := form2.IdTCPServer1.Contexts.LockList;
      n:= List.Count;
      try
      for i := 0 to n - 1 do
      begin
        if TMyClass(List.Items[i]).CltInfo.CltName = ds then
        begin
          try
            TMyClass(List.Items[i]).Connection.IOHandler.Write(RawToBytes(buf, SizeOf(TDataPack)), SizeOf(TDataPack));
          except
            //
          end;
          Exit;
        end;
      end;
      finally
        form2.IdTCPServer1.Contexts.UnlockList;
      end;
    end;
//-----------------------------------------------------------------------------------------------------------------
  end;
end;

procedure TForm2.RzButton1Click(Sender: TObject);
begin
  RzMemo1.Clear;
end;

end.

  客户端

program Project3;

uses
  Forms,
  windows,
  Unit3 in 'Unit3.pas' {Form3},
  Unit1 in 'Unit1.pas' {Form1},
  Unit2 in 'Unit2.pas',
  Unit4 in 'Unit4.pas';

{$R *.res}

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := False ;
  Application.CreateForm(TForm3, Form3);
  SetWindowLong(Application.Handle,GWL_EXSTYLE,WS_EX_TOOLWINDOW);

  Application.Run;
end.

  

unit Unit3;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, RzLstBox, ExtCtrls, ShellAPI, ImgList, RzTray, IdGlobal,
  Unit2,Clipbrd,
  IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, RzButton,
  RzRadChk, RzPanel, Mask, RzEdit, RzLabel, ComCtrls, Menus, RzBHints, RzSplit,
  RzAnimtr, IdZLibCompressorBase, IdCompressorZLib,RxRichEd, RzListVw,Buttons,
  RzSpnEdt ;

type
  TForm3 = class(TForm)
    RzListBox1: TRzListBox;
    Timer1: TTimer;
    RzTrayIcon1: TRzTrayIcon;
    ImageList1: TImageList;
    IdTCPClient1: TIdTCPClient;
    RzCheckBox1: TRzCheckBox;
    RzPanel1: TRzPanel;
    RzPanel2: TRzPanel;
    RzMemo2: TRzMemo;
    RzLabel1: TRzLabel;
    RzEdit1: TRzEdit;
    RzButton2: TRzButton;
    RzLabel2: TRzLabel;
    RzEdit2: TRzEdit;
    Timer2: TTimer;
    PopupMenu1: TPopupMenu;
    N1: TMenuItem;
    RzButton3: TRzButton;
    BalloonHint1: TBalloonHint;
    RzLabel5: TRzLabel;
    RzEdit3: TRzEdit;
    RzSplitter1: TRzSplitter;
    RzSplitter2: TRzSplitter;
    RzAnimator1: TRzAnimator;
    ImageList2: TImageList;
    RzToolButton1: TRzToolButton;
    PopupMenu2: TPopupMenu;
    N2: TMenuItem;
    N3: TMenuItem;
    N4: TMenuItem;
    ImageList3: TImageList;
    RzButton4: TRzButton;
    RzButton5: TRzButton;
    RxRichEdit1: TRxRichEdit;
    LabeledEdit1: TLabeledEdit;
    RzPanel3: TRzPanel;
    Image01: TImage;
    Image02: TImage;
    Image03: TImage;
    Image04: TImage;
    Image05: TImage;
    Image06: TImage;
    Image07: TImage;
    Image08: TImage;
    Image09: TImage;
    Image10: TImage;
    Image11: TImage;
    Image12: TImage;
    Image13: TImage;
    Image14: TImage;
    Image15: TImage;
    Image16: TImage;
    Image17: TImage;
    Image18: TImage;
    Image19: TImage;
    Image20: TImage;
    Image21: TImage;
    Image22: TImage;
    Image23: TImage;
    Image24: TImage;
    Image25: TImage;
    Image26: TImage;
    Image27: TImage;
    Image28: TImage;
    Image29: TImage;
    Image30: TImage;
    Image31: TImage;
    Image32: TImage;
    Image33: TImage;
    Image34: TImage;
    Image35: TImage;
    Image36: TImage;
    Image37: TImage;
    Image38: TImage;
    Image39: TImage;
    Image40: TImage;
    Image41: TImage;
    Image42: TImage;
    Image43: TImage;
    Image44: TImage;
    Button1: TButton;
    RzButton1: TRzButton;
    ScrollBox1: TScrollBox;
    Image1: TImage;
    Image45: TImage;
    Image46: TImage;
    Image47: TImage;
    Image48: TImage;
    Image49: TImage;
    Image50: TImage;
    Image51: TImage;
    Timer3: TTimer;
    Image2: TImage;
    FontDialog1: TFontDialog;
    procedure FormCreate(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure WMMOVING(var Msg: TMessage); message WM_MOVING;
    procedure wmsizing(var Msg: TMessage); message WM_SIZING;
    procedure RevCustMsg(var Msg:TMessage);message CustMsg;
    procedure SetBarHeight;
    procedure RzListBox1DblClick(Sender: TObject);
    procedure RzCheckBox1Click(Sender: TObject);
    procedure IdTCPClient1Connected(Sender: TObject);
    procedure IdTCPClient1Disconnected(Sender: TObject);
    procedure RzButton1Click(Sender: TObject);
    procedure RzButton2Click(Sender: TObject);
    procedure RzMemo2KeyPress(Sender: TObject; var Key: Char);
    procedure Timer2Timer(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure RzTrayIcon1RestoreApp(Sender: TObject);
    procedure RzTrayIcon1MinimizeApp(Sender: TObject);
    procedure RzMemo2MouseEnter(Sender: TObject);
    procedure FormMouseEnter(Sender: TObject);
    function MousePosion:Boolean;
    procedure RzListBox1MouseEnter(Sender: TObject);
    procedure N1Click(Sender: TObject);
    procedure RzButton3Click(Sender: TObject);
    procedure LabeledEdit1KeyPress(Sender: TObject; var Key: Char);
    procedure RzEdit3KeyPress(Sender: TObject; var Key: Char);
    procedure RzEdit1KeyPress(Sender: TObject; var Key: Char);
    procedure PopupMenu1Popup(Sender: TObject);
    procedure N4Click(Sender: TObject);
    procedure RzButton4Click(Sender: TObject);
    procedure RzButton5Click(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure RxRichEdit1MouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure Image01Click(Sender: TObject);
    procedure RzSpinButtons1DownLeftClick(Sender: TObject);
    procedure RzSpinButtons1UpRightClick(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure RxRichEdit1URLClick(Sender: TObject; const URLText: string;
      Button: TMouseButton);
    procedure Image1Click(Sender: TObject);
    function  MouseInScrollBox:Boolean;
    procedure Timer3Timer(Sender: TObject);
    procedure Image2Click(Sender: TObject);
  private
    { Private declarations }
    FAnchors: TAnchors;
  public
    { Public declarations }
  end;

  TRevDataThread = class(TThread)
  private
    buf: TDataPack;
  protected
    procedure Execute; override;
    procedure ShowMsg;
    procedure AddCltList;
    procedure DoDiscnt;
    procedure ClearScr;
    procedure AddMessage;
    procedure CltMessageIn;
    procedure DoSrvMessage;
    procedure DoSrvCloseQuery;
  end;
  // HidePosKind = (hpTop, hpLeft, hpBottom, hpRight);
  // THidePos = set of HidePosKind;

var
  Form3: TForm3;
  Lst_Height: Integer; // 记录窗体隐藏前的高度
  Lst_Width: Integer; // 记录窗体隐藏前的宽度
  Rec_Position: Boolean; // 是否启动窗体宽高记录标志
  Cur_Top, Cur_Bottom: Integer; // 隐藏后窗体的顶端和底部位置
  RevDataThread:TRevDataThread;
  BoolEnable:Boolean;
implementation

uses Math, types, Unit1,StrUtils,Unit4;
{$R *.dfm}

procedure TForm3.WMMOVING(var Msg: TMessage);
begin
  inherited;
  with PRect(Msg.LParam)^ do
  begin
    if (akLeft in FAnchors) or (akRight in FAnchors) then
    begin
      if (Left > 0) and (Right < Screen.Width) then
      begin
        if Rec_Position then
        begin
          Bottom := top + Lst_Height;
          Right := Left + Lst_Width;
          Height := Lst_Height;
          Width := Lst_Width;
        end;
      end
      else
      begin
        SetBarHeight;
        top := Cur_Top;
        Bottom := Cur_Bottom;
        exit;
      end;
    end;
    Left := Min(Max(0, Left), Screen.Width - Width);
    top := Min(Max(0, top), Screen.Height - Height);
    Right := Min(Max(Width, Right), Screen.Width);
    Bottom := Min(Max(Height, Bottom), Screen.Height);
    if not Rec_Position then
    begin
      Lst_Height := Form3.Height;
      Lst_Width := Form3.Width;
    end;
    FAnchors := [];
    if Left = 0 then
      Include(FAnchors, akLeft);
    if Right = Screen.Width then
      Include(FAnchors, akRight);
    if top = 0 then
      Include(FAnchors, akTop);
    if Bottom = Screen.Height then
      Include(FAnchors, akBottom);
    Timer1.Enabled := FAnchors <> [];
    if (akLeft in FAnchors) or (akRight in FAnchors) then
    begin
      Rec_Position := True;
      SetBarHeight;
      top := Cur_Top;
      Bottom := Cur_Bottom;
    end
    else
      Rec_Position := False;
    Timer1.Enabled := FAnchors <> [];

  end;
end;

procedure TForm3.Button1Click(Sender: TObject);
var
  c:TComponent;
  s:string;
begin
  s:='01';
  c:= FindComponent('Image'+s);
            Clipboard.Assign(TImage(c).Picture);
            RxRichEdit1.PasteFromClipboard;
end;

procedure TForm3.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  if Assigned(RevDataThread) then FreeAndNil(RevDataThread);
  IdTCPClient1.Disconnect;
end;

procedure TForm3.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  CanClose := False;
  RzButton3.Click;
end;

procedure TForm3.FormCreate(Sender: TObject);
begin
  Timer1.Enabled := False;
  Timer1.Interval := 200;
  //FormStyle := fsStayOnTop;
  BoolEnable:= False;
  RzListBox1.Clear;
  UnLcokTimes :=0;
  LockStatus := False;
  RxRichEdit1.Paragraph.LineSpacingRule:=lsSpecified;
  RxRichEdit1.Paragraph.LineSpacing:=20;
  ScrollBox1.VertScrollBar.Position :=0;
end;

procedure TForm3.FormMouseEnter(Sender: TObject);
begin
  RzTrayIcon1.Animate := False;
  RzTrayIcon1.IconIndex := 0;
end;

procedure TForm3.Timer1Timer(Sender: TObject);
const
  cOffset = 2;
begin
  if MousePosion then
  begin
    if akLeft in FAnchors then
      Left := 0;
    if akTop in FAnchors then
      top := 0;
    if akRight in FAnchors then
      Left := Screen.Width - Width;
    if akBottom in FAnchors then
      top := Screen.Height - Height;
  end
  else
  begin
    if akLeft in FAnchors then
    begin
      Left := -Width + cOffset;
      SetBarHeight;
      top := Cur_Top;
      Height := Cur_Bottom;
    end;
    if akTop in FAnchors then
      top := -Height + cOffset;
    if akRight in FAnchors then
    begin
      Left := Screen.Width - cOffset;
      SetBarHeight;
      top := Cur_Top;
      Height := Cur_Bottom;
    end;
    if akBottom in FAnchors then
      top := Screen.Height - cOffset;
  end;

end;

procedure TForm3.Timer2Timer(Sender: TObject);
var
  buf:TDataPack;
  bbyte:TIdBytes;
begin
  FillChar(buf,SizeOf(TDataPack),'');
  buf.Command := CltTimer;
  BByte := RawToBytes(buf, SizeOf(TDataPack));
  try
    IdTCPClient1.IOHandler.Write(BByte, SizeOf(TDataPack));
  except
    Timer2.Enabled := False;
    RzAnimator1.Animate := False;
    RzAnimator1.ImageIndex :=1;
    ShowMessage('与服务器断开连接');
  end;
end;

procedure TForm3.Timer3Timer(Sender: TObject);
begin
  if not MouseInScrollBox  then
  begin
    if ScrollBox1.Visible  then ScrollBox1.Visible := False;
  end;
  Timer3.Enabled := ScrollBox1.Visible;
end;

procedure TForm3.IdTCPClient1Connected(Sender: TObject);
//var
//  BByte: TIdBytes;
//  buf: TDataPack;
begin
//  FillChar(buf, SizeOf(TDataPack), '');
//  buf.Command := CltConnect;
//  buf.CltInfo.CltName := 'ZZPC';
//  BByte := RawToBytes(buf, SizeOf(TDataPack));
//  IdTCPClient1.IOHandler.Write(BByte, SizeOf(TDataPack));
//  if Assigned(RevDataThread)  then RevDataThread.Terminate;

end;

procedure TForm3.IdTCPClient1Disconnected(Sender: TObject);
begin
  if Assigned(RevDataThread)  then RevDataThread.Terminate;
  RzListBox1.Items.Clear;
  RzEdit2.ReadOnly := False;
  RzToolButton1.Enabled := False;
  RzButton4.Enabled := False;
  RzCheckBox1.Checked := False;
end;


procedure TForm3.Image01Click(Sender: TObject);
var
  s:String;
begin
  s:=RightStr(TImage(Sender).Name,2);
  RzMemo2.Text := '['+s+']';
  ScrollBox1.Visible := False;
  RzToolButton1.Click;
end;

procedure TForm3.Image1Click(Sender: TObject);
begin
  ScrollBox1.Visible := not ScrollBox1.Visible;
  Timer3.Enabled := ScrollBox1.Visible;
end;

procedure TForm3.Image2Click(Sender: TObject);
begin
  if FontDialog1.Execute then  RxRichEdit1.Font := FontDialog1.Font;

end;

procedure TForm3.LabeledEdit1KeyPress(Sender: TObject; var Key: Char);
begin
  if ((Key = #13) and (((hi(GetKeyState(VK_CONTROL))) and $80) <> $80))  then
  begin
    Key :=#0;
    RzButton3.Click;
  end;
end;

function TForm3.MouseInScrollBox: Boolean;
begin
  Result := False;
  if WindowFromPoint(Mouse.CursorPos) = ScrollBox1.Handle then Result := True;
end;

function TForm3.MousePosion: Boolean;
begin
  Result := False;
  if (WindowFromPoint(Mouse.CursorPos) = Handle) or
    (WindowFromPoint(Mouse.CursorPos) = RzListBox1.Handle) or
    (WindowFromPoint(Mouse.CursorPos) = RzPanel1.Handle) or
    (WindowFromPoint(Mouse.CursorPos) = RzPanel2.Handle) or
    (WindowFromPoint(Mouse.CursorPos) = RxRichEdit1.Handle) or
    (WindowFromPoint(Mouse.CursorPos) = RzMemo2.Handle) or
    (WindowFromPoint(Mouse.CursorPos) = RzCheckBox1.Handle) or
    (WindowFromPoint(Mouse.CursorPos) = RzEdit1.Handle) or
    (WindowFromPoint(Mouse.CursorPos) = RzEdit2.Handle) or
    (WindowFromPoint(Mouse.CursorPos) = RzEdit3.Handle) or
    (WindowFromPoint(Mouse.CursorPos) = RzAnimator1.Handle)  or
    (WindowFromPoint(Mouse.CursorPos) = RzButton2.Handle) or
    (WindowFromPoint(Mouse.CursorPos) = RzButton3.Handle) or
    (WindowFromPoint(Mouse.CursorPos) = RzSplitter1.Handle) or
    (WindowFromPoint(Mouse.CursorPos) = RzSplitter2.Handle) or
    (WindowFromPoint(Mouse.CursorPos) = LabeledEdit1.Handle)  or
    (WindowFromPoint(Mouse.CursorPos) = RzButton4.Handle) or
    (WindowFromPoint(Mouse.CursorPos) = RzButton5.Handle) or
    (WindowFromPoint(Mouse.CursorPos) = RzPanel3.Handle) or
    (WindowFromPoint(Mouse.CursorPos) = ScrollBox1.Handle) then
    Result := True;
end;

procedure TForm3.N1Click(Sender: TObject);
begin
  RzButton5.Click;
end;

procedure TForm3.N4Click(Sender: TObject);
begin
  RzButton3.Click;
end;

procedure TForm3.PopupMenu1Popup(Sender: TObject);
begin
  N3.Visible :=RzButton3.Caption = '锁定';
  N4.Visible := RzButton3.Caption = '锁定';
end;

procedure TForm3.RevCustMsg(var Msg: TMessage);
var
  s:string;
  buf:TDataPack;
begin
  FillChar(buf,SizeOf(TDataPack),'');
  s:=string(PDatapack(Pointer(msg.LParam))^.Data);
  form1.RzMemo1.Lines.Add(s);
end;

procedure TForm3.RxRichEdit1MouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  if RzTrayIcon1.Animate  then
  begin
    RzTrayIcon1.Animate := False;
    RzTrayIcon1.IconIndex := 0;
  end;
end;

procedure TForm3.RxRichEdit1URLClick(Sender: TObject; const URLText: string;
  Button: TMouseButton);
begin
  ShellExecute(Application.Handle, nil, PChar(URLText), nil, nil, SW_SHOWNORMAL);
end;

procedure TForm3.RzButton1Click(Sender: TObject);
var
  buf:TDataPack;
  Bbyte:TIdBytes;
  s,tm,bm:string;
  pt:TPoint;
  ctl:TComponent;
begin
  if Trim(RzMemo2.Text) <>'' then
  begin
    if RzListBox1.ItemIndex <> -1 then
    begin
      s:=RzListBox1.SelectedItem;
      if s= form3.RzEdit2.Text then
      begin
        RzListBox1.CustomHint.Title :='提示';
        RzListBox1.CustomHint.Description :='您不能跟自己聊天,那是欲魔行为!';
        pt.X :=RzListBox1.Width div 2;
        pt.Y :=RzListBox1.Height div 6;
        RzListBox1.CustomHint.ImageIndex :=1;
        RzListBox1.CustomHint.HideAfter :=5000;
        RzListBox1.CustomHint.ShowHint(RzListBox1.ClientToScreen(pt));
        Exit;
      end;

      FillChar(buf, SizeOf(TDataPack), '');
      buf.Command := CltSendMessage;
      StrCopy(@buf.CltInfo.CltName, PChar(Encrystrings(form3.RzEdit2.Text)));
      StrCopy(@buf.DstInfo.CltName, PChar(Encrystrings(s)));
      tm:= RzMemo2.Text + '   (' +datetimetostr(Now)+ ')';
      StrCopy(@buf.Data, PChar(Encrystrings(tm)));
      BByte := RawToBytes(buf, SizeOf(TDataPack));
      try
        IdTCPClient1.IOHandler.Write(BByte, SizeOf(TDataPack));
        if CheckBmp(tm) then
        begin
          bm := Copy(tm,2,2);
          RxRichEdit1.Lines.Add('你对 ' +RzListBox1.SelectedItem + ' 说:');
          ctl:= FindComponent('Image'+bm);
          //ShowMessage(TImage(ctl).Name);
          if ctl <> nil then
          begin
            Clipboard.Assign(TImage(ctl).Picture);
            RxRichEdit1.PasteFromClipboard;
          end;
        end else RxRichEdit1.Lines.Add('你对 '+ RzListBox1.SelectedItem + '说: '+ tm);
        PostMessage(RxRichEdit1.Handle, WM_VSCROLL, SB_BOTTOM, 0);
        RzMemo2.Clear;
      except
//        if not  IdTCPClient1.IOHandler.Opened  then
//        begin
          ShowMessage('已与服务器断开连接,消息发送不成功');
          RzListBox1.Items.Clear;
          RzEdit2.ReadOnly := False;
          RzToolButton1.Enabled := False;
          RzButton4.Enabled := False;
          RzCheckBox1.Checked := False;
//        end;

      end;
    end  else begin
      RzListBox1.CustomHint.Title :='提示';
      RzListBox1.CustomHint.Description :='请在这里选择一个聊天对象';
      pt.X :=RzListBox1.Width div 2;
      pt.Y :=RzListBox1.Height div 6;
      RzListBox1.CustomHint.ImageIndex :=1;
      RzListBox1.CustomHint.HideAfter :=3000;
      RzListBox1.CustomHint.ShowHint(RzListBox1.ClientToScreen(pt));
    end;
  end else begin
    RzMemo2.CustomHint.Title :='提示';
    RzMemo2.CustomHint.Description :='不能发送空消息哦';
    pt.X :=RzMemo2.Width div 2;
    pt.Y :=RzMemo2.Height div 2;
    RzMemo2.CustomHint.ImageIndex :=0;
    RzMemo2.CustomHint.HideAfter :=2000;
    RzMemo2.CustomHint.ShowHint(RzMemo2.ClientToScreen(pt));
  end;
end;

procedure TForm3.RzButton2Click(Sender: TObject);
begin
  RxRichEdit1.Clear;
end;

procedure TForm3.RzButton3Click(Sender: TObject);
var
  pt:TPoint;
  buf:TDataPack;
  Bbyte:TIdBytes;
begin
  if RzButton3.Caption = '锁定' then
  begin
    FillChar(buf, SizeOf(TDataPack), '');
    buf.Command := CltLockSrc;
    StrCopy(@buf.CltInfo.CltName, PChar(Encrystrings(form3.RzEdit2.Text)));
    BByte := RawToBytes(buf, SizeOf(TDataPack));
    try
      try
        IdTCPClient1.IOHandler.Write(BByte, SizeOf(TDataPack));
      except
        //
      end;
    finally
      RxRichEdit1.Visible := False;
      RzMemo2.Visible := False;
      RzListBox1.Visible := False;
      RzToolButton1.Visible := False;
      RzButton4.Visible := False;
      RzButton2.Visible := False;
      RzCheckBox1.Visible := False;
      RzLabel5.Visible := False;
      RzEdit3.Visible := False;
      RzTrayIcon1.MinimizeApp;
      RzButton3.Caption :='解锁';
      LabeledEdit1.Visible := True;
      RzLabel1.Visible := False;
      RzLabel2.Visible := False;
      RzEdit1.Visible := False;
      RzEdit2.Visible := False;
      RzPanel3.Visible := False;
      LabeledEdit1.SetFocus;
      LockStatus :=True;     //屏幕锁定状态
      ScrollBox1.Visible := False;
    end;
//    except
//      RzButton3.CustomHint.Title :='错误';
//      RzButton3.CustomHint.Description :='锁屏失败,请重试';
//      pt.X :=RzButton3.Width div 2;
//      pt.Y :=RzButton3.Height div 2;
//      RzButton3.CustomHint.ImageIndex :=1;
//      RzButton3.CustomHint.HideAfter :=3000;
//      RzButton3.CustomHint.ShowHint(RzButton3.ClientToScreen(pt));
//    end;
  end else begin
      if LabeledEdit1.Text = UnLockString then
      begin
        FillChar(buf, SizeOf(TDataPack), '');
        buf.Command := CltUnlockSrc;
        StrCopy(@buf.CltInfo.CltName, PChar(Encrystrings(form3.RzEdit2.Text)));
        BByte := RawToBytes(buf, SizeOf(TDataPack));
        try
          try
            IdTCPClient1.IOHandler.Write(BByte, SizeOf(TDataPack));
          except
            //
          end;
        finally
          UnLcokTimes :=0;
          RxRichEdit1.Visible := True ;
          RzMemo2.Visible := True ;
          RzListBox1.Visible := True ;
          RzToolButton1.Visible := True ;
          RzButton4.Visible := True;
          RzButton2.Visible := True ;
          RzCheckBox1.Visible := True;
          RzPanel3.Visible := True;
          RzButton3.Caption :='锁定';
          LabeledEdit1.Text :='';
          LabeledEdit1.Visible := False;
          if not RzCheckBox1.Checked  then
          begin
            RzLabel5.Visible := True;
            RzEdit3.Visible := True;
            RzLabel1.Visible := True;
            RzLabel2.Visible := True;
            RzEdit1.Visible := True;
            RzEdit2.Visible := True;
            RzPanel3.Visible := False;
          end;
          LockStatus := False;   //屏幕锁定状态
//          RzButton3.CustomHint.Title :='错误';
//          RzButton3.CustomHint.Description :='解锁失败,请重试';
//          pt.X :=RzButton3.Width div 2;
//          pt.Y :=RzButton3.Height div 2;
//          RzButton3.CustomHint.ImageIndex :=1;
//          RzButton3.CustomHint.HideAfter :=3000;
//          RzButton3.CustomHint.ShowHint(RzButton3.ClientToScreen(pt));
        end;
      end else begin
        UnLcokTimes := UnLcokTimes+1;
        LabeledEdit1.Text :='';
        LabeledEdit1.CustomHint.Title :='错误';
        LabeledEdit1.CustomHint.Description :='解锁密码不正确';
        pt.X :=LabeledEdit1.Width div 2;
        pt.Y :=LabeledEdit1.Height div 2;
        LabeledEdit1.CustomHint.ImageIndex :=0;
        LabeledEdit1.CustomHint.HideAfter :=2000;
        LabeledEdit1.CustomHint.ShowHint(LabeledEdit1.ClientToScreen(pt));
        LabeledEdit1.SetFocus;
        if UnLcokTimes >=3 then
        begin
          ShowMessage('解锁密码尝试3次均不正确,程序退出');
          if IdTCPClient1.Connected  then  IdTCPClient1.Disconnect;
          if Assigned(RevDataThread ) then RevDataThread.Terminate;
          Close;
        end;
      end;
  end;
end;

procedure TForm3.RzButton4Click(Sender: TObject);
var
  buf:TDataPack;
  Bbyte:TIdBytes;
  s:string;
  pt:TPoint;
begin
  if RzListBox1.ItemIndex <>-1 then
  begin
    FillChar(buf, SizeOf(TDataPack), '');
    s:=RzListBox1.SelectedItem;
    StrCopy(@buf.CltInfo.CltName, PChar(Encrystrings(form3.RzEdit2.Text)));
    StrCopy(@buf.DstInfo.CltName, PChar(Encrystrings(s)));
    buf.Command :=CltClear;
    BByte := RawToBytes(buf, SizeOf(TDataPack));
    try
      IdTCPClient1.IOHandler.Write(BByte, SizeOf(TDataPack));
      RxRichEdit1.CustomHint.Title :='提示';
      RxRichEdit1.CustomHint.Description :='您已清除自己和对方聊天记录';
      pt.X :=RxRichEdit1.Width div 2;
      pt.Y :=RxRichEdit1.Height div 2;
      RxRichEdit1.CustomHint.ImageIndex :=1;
      RxRichEdit1.CustomHint.HideAfter :=8000;
      RxRichEdit1.CustomHint.ShowHint(RxRichEdit1.ClientToScreen(pt));
      RxRichEdit1.Clear;
    except
      ShowMessage('已与服务器断开连接,清除屏幕不成功');
      RzListBox1.Items.Clear;
      RzEdit2.ReadOnly := False;
      RzToolButton1.Enabled := False;
      RzButton4.Enabled := False;
      RzCheckBox1.Checked := False;
    end;
  end else begin
      RzListBox1.CustomHint.Title :='提示';
      RzListBox1.CustomHint.Description :='请在这里选择一个清除屏幕对象';
      pt.X :=RzListBox1.Width div 2;
      pt.Y :=RzListBox1.Height div 6;
      RzListBox1.CustomHint.ImageIndex :=1;
      RzListBox1.CustomHint.HideAfter :=3000;
      RzListBox1.CustomHint.ShowHint(RzListBox1.ClientToScreen(pt));
  end;

end;

procedure TForm3.RzButton5Click(Sender: TObject);
begin
  Application.Terminate;
end;

procedure TForm3.RzCheckBox1Click(Sender: TObject);
var
  pt:TPoint;
begin
  IdTCPClient1.Host := RzEdit1.Text;
  if RzEdit3.Text <>'' then IdTCPClient1.Port := StrToInt(RzEdit3.Text)
  else begin
    RzEdit3.CustomHint.Title :='提示';
    RzEdit3.CustomHint.Description :='服务器端口不能为空';
    pt.X :=RzEdit3.Width div 2;
    pt.Y :=RzEdit3.Height div 2;
    RzEdit3.CustomHint.ImageIndex :=0;
    RzEdit3.CustomHint.HideAfter :=2000;
    RzEdit3.CustomHint.ShowHint(RzEdit3.ClientToScreen(pt));
    RzCheckBox1.Checked := False;
    Exit;
  end;
  if (RzEdit2.Text ='') then
  begin
    RzEdit2.CustomHint.Title :='提示';
    RzEdit2.CustomHint.Description :='聊天昵称不能为空';
    pt.X :=RzEdit2.Width div 2;
    pt.Y :=RzEdit2.Height div 2;
    RzEdit2.CustomHint.ImageIndex :=0;
    RzEdit2.CustomHint.HideAfter :=2000;
    RzEdit2.CustomHint.ShowHint(RzEdit2.ClientToScreen(pt));
    RzCheckBox1.Checked := False;
    Exit;
  end;
  if Pos(' ',RzEdit2.Text)<>0 then
  begin
    RzEdit2.CustomHint.Title :='提示';
    RzEdit2.CustomHint.Description :='聊天昵称中不能包含空格和 | 字符';
    pt.X :=RzEdit2.Width div 2;
    pt.Y :=RzEdit2.Height div 2;
    RzEdit2.CustomHint.ImageIndex :=0;
    RzEdit2.CustomHint.HideAfter :=2000;
    RzEdit2.CustomHint.ShowHint(RzEdit2.ClientToScreen(pt));
    RzCheckBox1.Checked := False;
    Exit;
  end;
  if (RzEdit1.Text ='') then
  begin
    RzEdit1.CustomHint.Title :='提示';
    RzEdit1.CustomHint.Description :='服务器地址不能为空';
    pt.X :=RzEdit1.Width div 2;
    pt.Y :=RzEdit1.Height div 2;
    RzEdit1.CustomHint.ImageIndex :=0;
    RzEdit1.CustomHint.HideAfter :=2000;
    RzEdit1.CustomHint.ShowHint(RzEdit1.ClientToScreen(pt));
    RzCheckBox1.Checked := False;
    Exit;
  end;
  try
    if  RzCheckBox1.Checked  then
    begin
      IdTCPClient1.Connect;
      RevDataThread := TRevDataThread.Create(True);
      RevDataThread.FreeOnTerminate := True;
      RevDataThread.Start;
      RzToolButton1.Enabled := True;
      RzButton4.Enabled := True;
      RzCheckBox1.Checked := True;
      RzEdit2.ReadOnly := True;
      Timer2.Enabled := True;
      RzEdit3.Visible := False;
      RzLabel5.Visible := False;
      RzLabel1.Visible := False;
      RzLabel2.Visible := False;
      RzPanel3.Visible := True;
      RzEdit1.Visible := False;
      RzEdit2.Visible := False;
      RzAnimator1.Animate := True;
    end
    else
    begin
      IdTCPClient1.Disconnect;
      if Assigned(RevDataThread)  then  RevDataThread.Terminate;
      RzCheckBox1.Checked := False;
      RzToolButton1.Enabled :=False;
      RzButton4.Enabled := False;
      RzEdit2.ReadOnly := False;
      Timer2.Enabled := False;
      RzEdit3.Visible := True;
      RzLabel5.Visible := True;
      RzLabel1.Visible := True;
      RzLabel2.Visible := True;
      RzPanel3.Visible := False;
      RzEdit1.Visible := True;
      RzEdit2.Visible := True;
      RzAnimator1.Animate := False;
      RzAnimator1.ImageIndex :=1;
    end;
  except
    RzEdit2.ReadOnly := False;
    RzCheckBox1.Checked := False;
    RzToolButton1.Enabled :=False;
    RzButton4.Enabled := False;
    if Assigned(RevDataThread)  then  RevDataThread.Terminate;
    if IdTCPClient1.Connected then IdTCPClient1.Disconnect;
    ShowMessage('连接服务器失败,请确认服务器地址是否正确');
  end;
end;

procedure TForm3.RzEdit1KeyPress(Sender: TObject; var Key: Char);
var
  tmp: string;
begin
  tmp := '0123456789.' + Char(VK_BACK) + Char(VK_DELETE);
  if Pos(Key, tmp) = 0 then Key := #0;
end;

procedure TForm3.RzEdit3KeyPress(Sender: TObject; var Key: Char);
var
  tmp: string;
begin
  tmp := '0123456789' + Char(VK_BACK) + Char(VK_DELETE);
  if Pos(Key, tmp) = 0 then Key := #0;
end;

procedure TForm3.RzListBox1DblClick(Sender: TObject);
begin
//  form1.Show;
end;

procedure TForm3.RzListBox1MouseEnter(Sender: TObject);
begin
  if RzTrayIcon1.Animate  then
  begin
    RzTrayIcon1.Animate := False;
    RzTrayIcon1.IconIndex := 0;
  end;
end;



procedure TForm3.RzMemo2KeyPress(Sender: TObject; var Key: Char);
begin
  if (Key = #13)   then
  begin
    if (((hi(GetKeyState(VK_CONTROL))) and $80) <> $80) and n2.Checked then
    begin
      Key :=#0;
      if RzToolButton1.Enabled  then RzToolButton1.Click;
    end;
  end;
end;

procedure TForm3.RzMemo2MouseEnter(Sender: TObject);
begin
  if RzTrayIcon1.Animate  then
  begin
    RzTrayIcon1.Animate := False;
    RzTrayIcon1.IconIndex := 0;
  end;
end;


procedure TForm3.RzSpinButtons1DownLeftClick(Sender: TObject);
begin
  if RzPanel3.Height > 40 then  RzPanel3.Height := (RzPanel3.Height -4) div 3;
end;

procedure TForm3.RzSpinButtons1UpRightClick(Sender: TObject);
begin
  if RzPanel3.Height <40 then RzPanel3.Height := RzPanel3.Height *3 +4;
end;

procedure TForm3.RzTrayIcon1MinimizeApp(Sender: TObject);
begin
  BoolEnable:= True;
end;

procedure TForm3.RzTrayIcon1RestoreApp(Sender: TObject);
begin
  BoolEnable:= False;
  RzTrayIcon1.Animate:= False;
  RzTrayIcon1.IconIndex := 0;
end;

procedure TForm3.SetBarHeight;
var
  AppBarData: TAPPBARDATA;
begin
  AppBarData.cbSize := SizeOf(AppBarData);
  If SHAppBarMessage(ABM_GETSTATE, AppBarData) AND (ABS_AUTOHIDE) <> 0 then
  begin
    Cur_Top := 1;
    Cur_Bottom := Screen.Height - 1;
  end
  else
  begin
    SHAppBarMessage(ABM_GETTASKBARPOS, AppBarData);
    case AppBarData.uEdge of
      ABE_TOP:
        begin
          Cur_Top := AppBarData.rc.Bottom + 1;
          Cur_Bottom := Screen.Height - 1;
        end;
      ABE_LEFT:
        begin
          Cur_Top := 1;
          Cur_Bottom := Screen.Height - 1;
        end;
      ABE_RIGHT:
        begin
          Cur_Top := 1;
          Cur_Bottom := Screen.Height - 1;
        end;
      ABE_BOTTOM:
        begin
          Cur_Top := 1;
          Cur_Bottom := Screen.Height -
            (AppBarData.rc.Bottom - AppBarData.rc.top) - 1;
        end;
    end;
  end;
end;

procedure TForm3.wmsizing(var Msg: TMessage);
begin
  inherited;
  if (akRight in FAnchors) then
  begin
    with PRect(Msg.LParam)^ do
    begin
      Left := Screen.Width - Width;
      top := Cur_Top;
      Right := Screen.Width;
      Bottom := Cur_Bottom
    end;
  end
  else if (akLeft in FAnchors) then
  begin
    with PRect(Msg.LParam)^ do
    begin
      Left := 0;
      top := Cur_Top;
      Right := Width;
      Bottom := Cur_Bottom;
    end;
  end;
end;

{ TRevDataThread }

procedure TRevDataThread.AddCltList;
var
  t,s:string;
  List:TStringList;
  OldCount,NewCount:Integer;
begin
  list:= TStringList.Create;
  OldCount := Form3.RzListBox1.Count;
  Form3.RzListBox1.Clear;
  t:= string(buf.Data);
//  count:=0;                     // dak|dkej|dinna|
//  for i:= 0 to strlen(pchar(s)) do if copy(s,i,1)='|' then count:=count+1;  //计算字符串中包含几个分隔符 |
//  for I := 0 to Count do
//  begin
//    ss:= LeftStr(s,Pos('|',s)-1);
//  end;
  s:= Uncrystrings(t);
  s:=LeftStr(s,StrLen(PChar(s))-1);
  List.Delimiter:='|';
  List.DelimitedText:=s;
  //Form3.RzTrayIcon1.Hint := List.Text;
  Form3.RzListBox1.Items.Assign(list);
  NewCount := form3.RzListBox1.Count;
  List.Free;
  if NewCount > OldCount  then form3.RzTrayIcon1.ShowBalloonHint('提示','有用户登录',bhiInfo,10)
  else if NewCount < OldCount then form3.RzTrayIcon1.ShowBalloonHint('提示','有用户下线',bhiInfo,10);
end;

procedure TRevDataThread.AddMessage;
var
  ss:string;
begin
  ss:= DecryStr(UncrypKey(string(buf.CltInfo.CltName),TKey),mkey);
  case buf.Command  of
    CltLockSrc: Form3.RxRichEdit1.Lines.Add(ss + ' 锁定了屏幕');

    CltUnlockSrc : Form3.RxRichEdit1.Lines.Add(ss + ' 解锁了屏幕');
  end;
  PostMessage(Form3.RxRichEdit1.Handle, WM_VSCROLL, SB_BOTTOM, 0);
end;

procedure TRevDataThread.ClearScr;
var
  pt:TPoint;
  ss:string;
begin
  Form3.RxRichEdit1.Clear;
  ss:= Uncrystrings(string(buf.CltInfo.CltName));
  Form3.RxRichEdit1.CustomHint.Title :='提示';
  Form3.RxRichEdit1.CustomHint.Description := ss+' 清除了您的聊天记录';
  pt.X :=Form3.RxRichEdit1.Width div 2;
  pt.Y :=Form3.RxRichEdit1.Height div 2;
  Form3.RxRichEdit1.CustomHint.ImageIndex :=1;
  Form3.RxRichEdit1.CustomHint.HideAfter :=8000;
  Form3.RxRichEdit1.CustomHint.ShowHint(Form3.RxRichEdit1.ClientToScreen(pt));
  Form3.RxRichEdit1.Clear;
  Form3.RxRichEdit1.Lines.Add(ss+' 清除了您的聊天记录');
end;

procedure TRevDataThread.CltMessageIn;
var
  s:string;
begin
  s:= Uncrystrings(string(buf.CltInfo.CltName));
  form3.RxRichEdit1.Lines.Add(s + ' 可能离开,TA的屏幕是锁定状态') ;
  PostMessage(Form3.RxRichEdit1.Handle, WM_VSCROLL, SB_BOTTOM, 0);
end;

procedure TRevDataThread.DoDiscnt;
begin
  form3.RzCheckBox1.Checked := False;
  Form3.IdTCPClient1.Disconnect;
  ShowMessage(Form3.RzEdit2.Text +' 已经存在,请更名重新登录');
end;

procedure TRevDataThread.DoSrvCloseQuery;
begin
  Form3.IdTCPClient1.Disconnect;
  Form3.RzCheckBox1.Checked := False;
end;

procedure TRevDataThread.DoSrvMessage;
var
  nr,ds:string;
begin
  nr:=Uncrystrings(string(buf.Data));
  ds:= Uncrystrings(string(buf.DstInfo.CltName));
  Form3.RxRichEdit1.Lines.Add('[服务器消息]:您发送给 ['+ ds +'] 的消息: “'+ nr +'",转发不成功,请重新发送');
  PostMessage(Form3.RxRichEdit1.Handle, WM_VSCROLL, SB_BOTTOM, 0);
end;

procedure TRevDataThread.Execute;
var
  BByte: TIdBytes;
  Nc:string;
begin
  inherited;
  FillChar(buf, SizeOf(TDataPack), '');
  buf.Command := CltConnect;
  Nc := Encrystrings(form3.RzEdit2.Text);
  StrCopy(@buf.CltInfo.CltName, PChar(Nc));
  BByte := RawToBytes(buf, SizeOf(TDataPack));
  Form3.IdTCPClient1.IOHandler.Write(BByte, SizeOf(TDataPack));
  while (not Terminated) and (Form3.IdTCPClient1.Connected) do
  begin
    FillChar(buf, SizeOf(TDataPack), '');
    Form3.IdTCPClient1.IOHandler.ReadBytes(BByte, SizeOf(TDataPack), False);
    BytesToRaw(BByte, buf, SizeOf(TDataPack));
    case buf.Command of
      CltSendMessage:
        begin
          //SendMessage(Handle,CustMsg,CltSendMessage,Integer(PDataPack(buf)));
          Synchronize(showmsg);
          if LockStatus  then
          begin
            buf.DstInfo.CltName := buf.CltInfo.CltName;
            buf.Command := CltMessage;
            StrCopy(@buf.CltInfo.CltName, PChar(Encrystrings(form3.RzEdit2.Text)));
            BByte := RawToBytes(buf, SizeOf(TDataPack));
            Form3.IdTCPClient1.IOHandler.Write(BByte, SizeOf(TDataPack));
          end;
        end;
      CltList :                   Synchronize(AddCltList);

      CltDisconnect :             Synchronize(DoDiscnt);

      CltTimer :  ;

      CltClear :                  Synchronize(clearscr);

      CltLockSrc,CltUnlockSrc  :  Synchronize(Addmessage);

      CltMessage :                Synchronize(cltmessageIn);

      SrvMessage :                Synchronize(DoSrvMessage);

      SrvCloseQuery :             Synchronize(DoSrvCloseQuery);
    end;
  end;
end;

procedure TRevDataThread.ShowMsg;
var
  s,ss,bm:string;
  ctl:TComponent;
begin
  s:=Uncrystrings(string(buf.Data));
  ss:= Uncrystrings(string(buf.CltInfo.CltName));
  if CheckBmp(s) then
  begin
    bm := Copy(s,2,2);
    Form3.RxRichEdit1.Lines.Add(ss + ' 对你说:');
    //Clipboard.Assign(form3.Image1.Picture);
    ctl:= Form3.FindComponent('Image'+bm);
    if ctl <> nil then
    begin
      Clipboard.Assign(TImage(ctl).Picture);
      form3.RxRichEdit1.PasteFromClipboard;
    end;
  end else Form3.RxRichEdit1.Lines.Add(ss + ' 对你说:'+s );
  PostMessage(Form3.RxRichEdit1.Handle, WM_VSCROLL, SB_BOTTOM, 0);
  if BoolEnable or ((form3.Timer1.Enabled) and (not form3.MousePosion))  then
  begin
    if not Form3.RzTrayIcon1.Animate then Form3.RzTrayIcon1.Animate:=True;
  end;

end;

end.

  公共单元

unit Unit2;

interface

uses Windows,Messages,Classes,SysUtils,StrUtils;

 const CustMsg = WM_USER + 2110;
       CltConnect = 1;
       CltDisconnect =2;
       CltSendMessage =3;
       CltList=4;
       CltTimer =5;
       CltClear = 6;
       CltLockSrc =7;
       CltUnlockSrc = 8;
       CltMessage    = 9;
       SrvMessage  =10;
       SrvTimer =11;
       SrvCloseQuery =12;
       DataSize = 1024 *5;     //数据缓冲区大小
       UnLockString = '123456';
 type
  TCltInfo = packed record
    CltIP:array[0..14] of Char;
    CltName:array[0..255] of Char;
  end;

  TDataPack = record
    CltInfo:TCltInfo;
    DstInfo:TCltInfo;
    Command:Integer;
    Data:array[0..DataSize -1] of Char;
  end;

  PDataPack = ^TDataPack;
function Encrystrings(str:string):string;
function Uncrystrings(str:string):string;
function EncrypKey(Src: String; Key: String): string;
function UncrypKey(Src: String; Key: String): string;
function GetTMkey:string;
function CheckBmp(Str:string):Boolean;
var
  UnLcokTimes:Integer;
  LockStatus:Boolean;
implementation
  uses Unit4;

function CheckBmp(Str:string):Boolean;
begin
  Result := False;
  if Length(Str) < 4 then  Exit;
  if (LeftStr(Str,1) ='[') and (Copy(Str,4,1) = ']') then Result :=True;
end;
function Encrystrings(str:string):string;
var
  tmp:string;
begin
  tmp := EncryStr(str,MKey);
  Result := EncrypKey(tmp,TKey);
end;

function Uncrystrings(str:string):string;
var
  tmp:string;
begin
  tmp:= UncrypKey(str,TKey);
  Result := DecryStr(tmp,MKey);
end;
// 加密函数
function EncrypKey(Src: String; Key: String): string;
var
  KeyLen: integer;
  KeyPos: integer;
  offset: integer;
  dest: string;
  SrcPos: integer;
  SrcAsc: integer;
  Range: integer;
begin
  //此处省略,自己写
end;

// 解密函数
function UncrypKey(Src: String; Key: String): string;
var
  //idx: integer;
  KeyLen: integer;
  KeyPos: integer;
  offset: integer;
  dest: string;
  SrcPos: integer;
  SrcAsc: integer;
  TmpSrcAsc: integer;
begin
 //此处省略,自己写
end;

function GetTMkey:string;
var
  ss: string;
  n: Integer;
begin
  ss := '';
  Randomize;
  repeat
    n := Random(127);
    if n>=34 then ss := ss + char(n);
  until (Length(ss)>=12);
  Result  := ss;
end;
end.

  

  

你可能感兴趣的:(Indy10 即时通讯Demo)