Как получить дескриптор окна другого приложения, переданный в Delphi с помощью щелчка мышью - PullRequest
1 голос
/ 10 апреля 2011

Как получить дескриптор окна для передачи в Delphi пользователем, который выбирает окно (может быть окном любого другого приложения), щелкая мышью по нему.В моем приложении Delphi у меня может быть кнопка, которую пользователь нажимает, чтобы запустить этот процесс обнаружения, а также метка, отображающая заголовок окна, на который нажали, в приложении Delphi.Когда пользователь удовлетворен, он выбрал правильное окно и может щелкнуть кнопку в моем приложении Delphi (которое будет модальным), чтобы остановить процесс выбора и позволить моему приложению начать делать с другим окном то, что ему нужно сделать ...

Ответы [ 3 ]

5 голосов
/ 10 апреля 2011

Подход, который пользователь STATUS_ACCESS_DENIED изложил в комментарии, вероятно, самый простой способ пойти сюда.Я бы порекомендовал использовать захват мыши поверх перехвата, так как это несколько проще в реализации.

Вот чуть более подробное описание того, что происходит:

Первое, что изменит способ выбораработает.Вместо того, чтобы пользователь нажимал кнопку в вашем приложении, чтобы запустить процесс, а затем щелкните целевое окно и, наконец, нажмите еще раз для подтверждения;Это намного проще реализовать, если пользователь нажимает на конкретную область в приложении, затем перетаскивает в целевое окно и затем отпускает кнопку мыши, находясь над целью.Это связано с тем, что Windows считает нажатие другого приложения принадлежащим этому приложению, и вам необходимо выполнить дополнительную работу, чтобы перехватить его.Но есть простой способ - захват мыши - для получения информации о перетаскивании / выпуске , если он запускается как щелчок по вашему собственному приложению .

. Это также подход, которым WindowsSDK Spy ++ использует инструмент;так что, делая это таким образом, вы также соответствуете общеизвестному инструменту.(Рис. Spy ++ здесь - обратите внимание на инструмент поиска перекрестия в диалоговом окне - это то, что вы щелкаете и перетаскиваете на цель. Настоятельно рекомендую загрузить Windows SDK и поиграть с этим инструментом, если вы этого еще не сделалираньше, это также очень полезный способ увидеть, как другие приложения создаются так же хорошо, как инструмент обучения Windows API.)

Необходимые шаги:

  • В вашем приложении есть некоторый контроль, которыйответ на события нажатия мыши (WM_LBUTTONDOWN в Win32 / C, OnMouseDown в delphi).Возможно, вы захотите нарисовать значок перекрестия или подобное, чтобы пользователь знал, где щелкнуть мышью.
  • Когда вы нажимаете мышь, используйте SetCapture, чтобы «захватить» мышь.Это означает, что элемент управления будет получать все сообщения мыши, пока мышь движется - до тех пор, пока пользователь не отпустит кнопку, - даже если она выходит за пределы элемента управления.
  • Установите значок, похожий на перекрестие, чтобы пользовательзнает, что они находятся в режиме перетаскивания
  • Когда пользователь перемещает мышь, вы получите сообщение WM_MOUSEMOVE (OnMouseMove в Delphi), которое имеет координаты указателя.Вам нужно будет использовать ClientToScreen, чтобы преобразовать их в координаты экрана, затем WindowFromPoint, чтобы найти окно в этой точке.(Обратите внимание, что это находит самое внутреннее окно в этой точке, вы можете использовать ChildWindowFromPoint, начиная с окна рабочего стола, чтобы просто получить окно верхнего уровня, если вы этого хотите.) Вам решать, хотите ли вы обновлять свой пользовательский интерфейс при каждомдвижение мыши по всему перетаскиванию или просто когда пользователь отпускает кнопку мыши.
  • Когда пользователь отпускает кнопку мыши, вы получаете WM_LBUTTONUP / OnMouseUp;на этом этапе оберните вещи, вызвав ReleaseCapture и вернув курсор в нормальную форму.

Обратите внимание, что вы будете получать события перемещения мыши как во время перетаскивания, так и в том случае, если пользователь просто произнеспереместите указатель мыши на элемент управления, возможно, на пути к другому элементу управления.Самый простой способ отделить эти два случая друг от друга - это использовать флаг в вашем элементе управления, который вы устанавливаете, когда нажимаете кнопку мыши, и очищать, когда вы поднимаете мышь, и обрабатывать события перемещения мыши, только если этот флаг установлен.

Выше описан процесс в терминах простых API Win32, которые вы вызываете из C / C ++;но похоже, что Delphi обеспечивает прямую поддержку большинства или всех из них.

edit: Возможная реализация Delphi:

type
  TForm1 = class(TForm)
    Label1: TLabel;
    procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
    procedure FormPaint(Sender: TObject);
    procedure FormMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
  private
    FCacheWnd: HWND;
    FCaptured: Boolean;
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

const  // the first item, the place where the crosshair is
  ClickRect: TRect = (Left: 10; Top: 10; Right: 44; Bottom: 44);

procedure TForm1.FormPaint(Sender: TObject);
begin
  // draw the control and the crosshair if no capturing
  if GetCapture <> Handle then begin  
    DrawFrameControl(Canvas.Handle, ClickRect, 0, DFCS_BUTTONPUSH);
    DrawIcon(Canvas.Handle, ClickRect.Left, ClickRect.Top,
              Screen.Cursors[crCross]);
  end;
end;

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if (Button = mbLeft) and (Shift = [ssLeft])
      and PtInRect(ClickRect, Point(X, Y)) then begin
    // the second item, draw the control pressed,
    // set the flag and the capture. FCacheWnd is used not to get
    // window information for every mouse move - if the window under the
    // mouse is not changed.
    DrawFrameControl(Canvas.Handle, ClickRect, 0, DFCS_PUSHED);
    FCacheWnd := 0;
    FCaptured := True;
    SetCapture(Handle);
    Screen.Cursor := crCross; // the third item, set the cursor to crosshair.
  end;
end;

function GetWndFromClientPoint(ClientWnd: HWND; Pt: TPoint): HWND;
begin
  MapWindowPoints(ClientWnd, GetDesktopWindow, Pt, 1);
  Result := WindowFromPoint(Pt);
end;

function GetWndInfo(Wnd: HWND): string;
var
  ClassName: array [0..256] of Char;
begin
  Result := '';
  if IsWindow(Wnd) then begin
    GetClassName(Wnd, ClassName, 256);
    Result := Format('Window: %x [%s]', [Wnd, ClassName]);
    if (GetWindowLong(Wnd, GWL_STYLE) and WS_CHILD) = WS_CHILD then begin
      Wnd := GetAncestor(Wnd, GA_ROOT);
      GetClassName(Wnd, ClassName, 256);
      Result := Format(Result + sLineBreak + 'Top level: %x [%s]', [Wnd, ClassName]);
    end;
  end;
end;

procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
var
  Wnd: HWND;
begin
  if FCaptured then begin
    // fourth item, convert coordinates and find the window under the cursor
    Wnd := GetWndFromClientPoint(Handle, Point(X, Y));
    if Wnd <> FCacheWnd then 
      Label1.Caption := GetWndInfo(Wnd);
    FCacheWnd := Wnd;
  end;
end;

procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if FCaptured then begin
    // fifth item
    FCaptured := False;
    ReleaseCapture;
    InvalidateRect(Handle, @ClickRect, False); // invalidate pressed look
    Screen.Cursor := crDefault;
  end;
end;
4 голосов
/ 10 апреля 2011

если вы знаете, какой текст находится в заголовке окна, этот код поможет вам:

var
  WindowList: TList;

function GetHandle (windowtitle: string): HWND;
var
  h, TopWindow: HWND;
  Dest: array[0..80] of char;
  i: integer;
  s: string;

  function getWindows(Handle: HWND; Info: Pointer): BOOL; stdcall;
    begin
      Result:= True;
      WindowList.Add(Pointer(Handle));
    end;

begin
  result:= 0;

  try
    WindowList:= TList.Create;
    TopWindow:= Application.Handle;
    EnumWindows(@getWindows, Longint(@TopWindow));
    i:= 0;
    while (i < WindowList.Count) and (result = 0) do
      begin
        GetWindowText(HWND(WindowList[i]), Dest, sizeof(Dest) - 1);
        s:= dest;
        if length(s) > 0 then
          begin
            if (Pos(UpperCase(Windowtitle), UpperCase(s)) >= 1) then
              begin
                h:= HWND(WindowList[i]);
                if IsWindow(h) then
                  result:= h
             end
           end;
        inc(i)
      end
    finally
      WindowList.Free;
    end;
end;

Использование в вашем примере (блокнот помещает имя открытого файла в заголовок окна):

h:= getHandle('text.txt');
if (h = 0)
  // Oops not found
else 
  begin
    // you got the handle!
  end;

Я использовал этот код, чтобы проверить, запущено ли уже мое приложение. Но его можно использовать в любом запущенном приложении.

1 голос
/ 10 апреля 2011

Редактировать: Прошло, но вы раньше могли загружать Delphi Window Spy от Эдди Шипмана с сайта delphipages.com, который превратился в кучу бесполезных приманок для ссылок.

...