如何根据在服务器端绘制的区域在远程屏幕(客户端)上的两个表单中创建相同的孔?

How make the same hole in two Form's on remote screen (client side), based on area drawn on server side?

我有以下代码,想根据在服务器端绘制的区域,在远程屏幕(客户端)的两个表单中绘制相同的孔。

我在两侧(服务器和客户端)都有相同的表单 (Form3),这是一个 "mirror",我在其中绘制了一个必须留在内部的区域在客户端使用相同的表格。

The Form3 in server side have 50% of max AlphaBlend value, this is necessary to see remote screen behind Form3.


Before all, i want say that i'm receiving the remote screen on server side and and mouse click positions works like expected.

那我就麻烦了:

以下代码产生上图所示的结果。我认为这段代码是正确的,但缺少将此孔与 Form3.

对齐

有人可以帮忙吗?抱歉,如果这是一个糟糕的问题,但这是我真正的麻烦,我尝试用我能做到的更好的方式表达这个问题中的所有内容。

这是所有相关代码:

服务器端:

Form2(我看到远程屏幕的地方):

unit Unit2;

interface

uses
 Unit1;

type
  TForm2 = class(TForm)
  Panel1: TPanel;
  CheckBox1: TCheckBox;
  ScrollBox1: TScrollBox;
  Image1: TImage;
  PaintBox1: TPaintBox;
  procedure PaintBox1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
  procedure PaintBox1MouseMove(Sender: TObject; Shift: TShiftState;
      X, Y: Integer);
  procedure PaintBox1MouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
  procedure PaintBox1Paint(Sender: TObject);

  private
    { Private declarations }
    FSelecting: Boolean;
    FSelection: TRect;
    pos1, pos2, pos3, pos4: Integer;
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

procedure TForm2.PaintBox1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin

  if CheckBox1.Checked then
  begin
    FSelection.Left := X;
    FSelection.Top := Y;
    FSelecting := true;
  end;

end;

procedure TForm2.PaintBox1MouseMove(Sender: TObject; Shift: TShiftState;
  X, Y: Integer);
begin

  if FSelecting then
  begin
    FSelection.Right := X;
    FSelection.Bottom := Y;
    pbRec.Invalidate;
  end;

end;

procedure TForm2.PaintBox1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin

  if CheckBox1.Checked then
  begin
    FSelecting := false;
    FSelection.Right := X;
    FSelection.Bottom := Y;
    PaintBox1.Invalidate;

    FSelection.NormalizeRect;
    if FSelection.IsEmpty then
    begin
      // None selection was made on PaintBox
    end
    else
    begin
      pos1 := FSelection.Left;
      pos2 := FSelection.Top;
      pos3 := X;
      pos4 := Y;
    end;
  end;

end;

procedure TForm2.PaintBox1Paint(Sender: TObject);
begin
  if CheckBox1.Checked then
  begin
    PaintBox1.Canvas.Brush.Style := bsClear;
    PaintBox1.Canvas.Pen.Style := psSolid;
    PaintBox1.Canvas.Pen.Color := clRed;
    PaintBox1.Canvas.Rectangle(FSelection);
  end;
end;

procedure TForm2.Button1Click(Sender: TObject);
var
  Socket: TCustomWinSocket;
begin
  Socket := TCustomWinSocket(Form1.LV1.Selected.SubItems.Objects[0]);
  if CheckBox1.Checked then
  begin
      Socket.SendText(intToStr(pos1) + ';' + intToStr(pos2) + ';' +
        intToStr(pos3) + ';' + intToStr(pos4));
  end;
end;

procedure TForm2.Button2Click(Sender: TObject);
begin
  Form3 := TForm3.Create(Self);
  Form3.Show;
end;

Form2 .DFM:

object Panel1: TPanel
    Left = -1
    Top = 0
    Width = 773
    Height = 89
    Anchors = [akTop]
    BevelEdges = [beLeft, beRight]
    ParentDoubleBuffered = False
    TabOrder = 0
    end

object ScrollBox1: TScrollBox
    Left = 0
    Top = 0
    Width = 765
    Height = 472
    HorzScrollBar.Smooth = True
    HorzScrollBar.Tracking = True
    VertScrollBar.Smooth = True
    VertScrollBar.Tracking = True
    Align = alClient
    TabOrder = 1
    object Image1: TImage
      Left = 0
      Top = 0
      Width = 1362
      Height = 621
      AutoSize = True
    end

object PaintBox1: TPaintBox
      Left = 0
      Top = 0
      Width = 1362
      Height = 621
      Align = alClient
      OnMouseDown = PaintBox1MouseDown
      OnMouseMove = PaintBox1MouseMove
      OnMouseUp = PaintBox1MouseUp
      OnPaint = PaintBox1Paint
      ExplicitWidth = 1364
      ExplicitHeight = 622
    end

Form3(在客户端也是一样的"mirror"Form),这个Form根据远程屏幕分辨率集中:

unit Unit3;

interface

uses
 ...

type
  TForm3 = class(TForm)
    Panel1: TPanel;
    Image1: TImage;
    Label1: TLabel;
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    procedure CreateParams(var pr: TCreateParams); override;
  public
    { Public declarations }
  end;

var
  Form3: TForm3;

implementation

uses 
 Unit1;

{$R *.dfm}

procedure TForm3.FormCreate(Sender: TObject);
var
  MyString: String;
  Splitted: TArray<String>;
begin
  MyString := Form1.LV1.Selected.SubItems[6]; // Resolution of remote screen
  Splitted := MyString.Split(['x']);

  Self.Left := (Integer(Splitted[0]) - Self.Width) div 2;
  Self.Top := (Integer(Splitted[1]) - Self.Height) div 2;
end;

procedure TForm3.CreateParams(var pr: TCreateParams);
begin
  inherited;
  pr.WndParent := Form2.Handle;
  pr.ExStyle := pr.ExStyle or WS_EX_TOPMOST or WS_EX_TRANSPARENT;
  pr.ExStyle := WS_EX_TRANSPARENT or WS_EX_TOPMOST;
end;

Form3 .DFM:

object Form3: TForm3
  Left = 328
  Top = 143
  BorderStyle = bsNone
  ClientHeight = 567
  ClientWidth = 526
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  Position = poScreenCenter
  OnCreate = FormCreate
  PixelsPerInch = 96
  TextHeight = 13
  object Panel1: TPanel
    Left = 0
    Top = 0
    Width = 801
    Height = 569
    TabOrder = 0
    object Image1: TImage
      Left = 1
      Top = 1
      Width = 799
      Height = 567
      Align = alClient
      ExplicitLeft = 2
      ExplicitTop = 0
      ExplicitHeight = 447
    end

    object Label1: TLabel
      Left = 92
      Top = 69
      Width = 28
      Height = 13
      Caption = 'Nome'
      Color = clBtnFace
      Font.Charset = DEFAULT_CHARSET
      Font.Color = clBlack
      Font.Height = -11
      Font.Name = 'MS Sans Serif'
      Font.Style = []
      ParentColor = False
      ParentFont = False
    end

客户端:

Form2 ("locker" 表格):

unit Unit2;

private
    { Private declarations }
    procedure CreateParams(var Params: TCreateParams); override;

  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

procedure TForm2.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  Params.WndParent := Application.Handle;
  Params.ExStyle := Params.ExStyle or WS_EX_TOPMOST or WS_EX_TRANSPARENT;
  Params.ExStyle := WS_EX_TRANSPARENT or WS_EX_TOPMOST;
end;

procedure TForm2.FormCreate(Sender: TObject);
begin
  windowstate := wsmaximized;
  Top := 0;
  Left := 0;
  Height := Screen.Height;
  Width := Screen.Width;
end;

{

Properties of Form2:

Align => alNone
AlphaBlend => True
BorderStyle => BsNone

}

end.

Form3(服务器端相同):

unit Unit3;

interface

uses
 ...

type
  TForm3 = class(TForm)
    Panel1: TPanel;
    Label1: TLabel;
    procedure FormShow(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    procedure CreateParams(var pr: TCreateParams); override;
  public
    { Public declarations }
  end;

var
  Form3: TForm3;

implementation

uses 
 Unit2;

{$R *.dfm}

procedure TForm3.FormCreate(Sender: TObject);
begin
  Self.Left := (GetSystemMetrics(SM_CXSCREEN) - Self.Width) div 2;
  Self.Top := (GetSystemMetrics(SM_CYSCREEN) - Self.Height) div 2;
end;

procedure TForm3.FormShow(Sender: TObject);
begin
  ShowWindow(Application.Handle, SW_HIDE);
end;

procedure TForm3.CreateParams(var pr: TCreateParams);
begin
  inherited;
  pr.WndParent := Form2.Handle;
end;

{

Properties of Form3:

Align => alNone
BorderStyle => BsNone

}

end.

客户端接收区域:

procedure CS1_Read(Self: Pointer; Sender: TObject; Socket: TCustomWinSocket);
var
 X1, X2, Y1, Y2: Integer;
 List: TStrings;
 FormRegion, HoleRegion: HRGN;
 StrCommand: string;

begin

if Pos(';', StrCommand) > 0 then
begin

    List := TStringList.Create;
    try

      ExtractStrings([';'], [], PChar(StrCommand), List);

      Form3 := TForm3.Create(Form2); // The Form2 already was created and is showing

      X1 := Round(StrToIntDef(List[0], 0) - Form2.Left);
      Y1 := Round(StrToIntDef(List[1], 0) - Form2.Top);
      X2 := Round(StrToIntDef(List[2], 0) - Form2.Left);
      Y2 := Round(StrToIntDef(List[3], 0) - Form2.Top);


      FormRegion := CreateRectRgn(0, 0, Form3.Width, Form3.Height);
      HoleRegion := CreateRectRgn(X1, Y1, X2, Y2);
      CombineRgn(FormRegion, FormRegion, HoleRegion, RGN_DIFF);
      SetWindowRgn(Form3.handle, FormRegion, true);

      FormRegion := CreateRectRgn(0, 0, Form2.Width, Form2.Height);
      HoleRegion := CreateRectRgn(X1, Y1, X2, Y2);
      CombineRgn(FormRegion, FormRegion, HoleRegion, RGN_DIFF);
      SetWindowRgn(Form2.handle, FormRegion, true);

      Form3.ShowModal;
      Form3.Release;

    finally
      List.Free;
    end;
  end;
end;

在客户端,您有一个具有屏幕大小的半透明灰色表单 (Form2)。在该窗体之上,您有一个位于屏幕中心的不透明白色窗体 (Form3)。在 Form3 中,您在 Top = YLeft = X 处有一个矩形孔,坐标为 Form3.

我了解到您的问题是您想在 Form2 中绘制一个与 Form3 中的孔对齐的孔。

您需要将 Form3 的坐标系转换为 Form2 的坐标系,只需简单地添加:

Form2.Hole.Left := Form3.Left + Form3.Hole.Left;
Form2.Hole.Top  := Form3.Top  + Form3.Hole.Top;

这将使孔对齐。你似乎在你的计算中尝试了类似的东西,但你指的是 Form2.LeftForm2.Top 这是无用的,因为它们都是 0.

如果我误解了你的问题,而你实际上希望 Form3 孔与 Form2 孔对齐,那么你需要将 Form3 移动到左上角屏幕而不是居中 ...

... 或者,考虑您的评论:如果我在远离 Form3(客户端)的区域的服务器端绘图,例如在屏幕左侧更多,请绘图只有 Form2 的孔,如果我在屏幕中间绘制更多,绘制对齐的两个孔 将通过简单地交换术语来完成:

Form3.Hole.Left := Form2.Hole.Left - Form3.Left
Form3.Hole.Top  := Form2.Hole.Top  - Form3.Top

这会将 Form2 坐标转换为 Form3 坐标,在您的例子中可能会变成负值(在表格之外)。

根据您的代码调整上述内容,您需要首先用收到的 Form2.Hole 坐标处理 Form2 区域 ,然后减去 Form3 坐标(Left and Top) from X1..Y2 and 然后处理Form3 region*.

  X1 := Round(StrToIntDef(List[0], 0) - Form2.Left); // Form2 props can be removed as hardcoded to 0
  Y1 := Round(StrToIntDef(List[1], 0) - Form2.Top);  // -"-
  X2 := Round(StrToIntDef(List[2], 0) - Form2.Left); // -"-
  Y2 := Round(StrToIntDef(List[3], 0) - Form2.Top);  // -"-

  FormRegion := CreateRectRgn(0, 0, Form2.Width, Form2.Height);
  HoleRegion := CreateRectRgn(X1, Y1, X2, Y2);
  CombineRgn(FormRegion, FormRegion, HoleRegion, RGN_DIFF);
  SetWindowRgn(Form2.handle, FormRegion, true);

  X1 := X1 - Form3.Left;
  Y1 := Y1 - Form3.Top;
  X2 := X2 - Form3.Left;
  Y2 := Y2 - Form3.Top;

  FormRegion := CreateRectRgn(0, 0, Form3.Width, Form3.Height);
  HoleRegion := CreateRectRgn(X1, Y1, X2, Y2);
  CombineRgn(FormRegion, FormRegion, HoleRegion, RGN_DIFF);
  SetWindowRgn(Form3.handle, FormRegion, true);

编辑

您的 Server.Form2 与屏幕尺寸不同(因此 Client.Form2)似乎违反直觉。但也许我从来没有真正理解设置的实际用途。

无论如何,如果 Form3 的大小相同,居中,但服务器和客户端的屏幕尺寸不同,您需要将客户端的 Form3.Hole 坐标调整为一半的差异服务器和客户端屏幕范围,或者,由于 Form3 表格居中,您可以像

一样计算水平和垂直校正
ResolutionCorrectionX := Server.Form3.Left - Client.Form3.Left;
ResolutionCorrectionY := Server.Form3.Top - Client.Form3.Top;

然后添加到 Form3 HoleRegion 的 X 和 Y 坐标。

X1 := X1 - Form3.Left + ResolutionCorrectionX; // and similar for X2, Y1 and Y2

顺便说一句,出于好奇,您为什么使用 Round() 进行基于整数的计算?