Подход, который пользователь 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;