How to determine if the mouse cursor is inside a control - delphi

I'm adding support for mouse wheel movement to a TScrollBox (using the FormMouseWheel procedure) and I need to determine if the mouse is inside the component.
Basically I need to determine if the mouse is inside the TScrollBox so that I then handle the scrolling code accordingly.
Any idea on how to do this?
EDIT: Here's the code (including the answer to this question) as it might help others:
procedure TForm1.FormMouseWheel(Sender: TObject;
Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint;
var Handled: Boolean);
var
Msg: Cardinal;
Code: Cardinal;
I, ScrollLines: Integer;
ScrollBoxCursosPos: TPoint;
begin
//position of the mouse cursor related to TScrollBox
ScrollBoxCursosPos := ScrollBox1.ScreenToClient(Mouse.CursorPos);
if (PtInRect(ScrollBox1.ClientRect, ScrollBoxCursosPos)) then
begin
Handled := True;
If ssShift In Shift Then
msg := WM_HSCROLL
Else
msg := WM_VSCROLL;
If WheelDelta < 0 Then
code := SB_LINEDOWN
Else
code := SB_LINEUP;
ScrollLines:= Mouse.WheelScrollLines * 3;
for I:= 1 to ScrollLines do
ScrollBox1.Perform(Msg, Code, 0);
ScrollBox1.Perform(Msg, SB_ENDSCROLL, 0);
end;
end;

Mouse.CursorPos returns the mouse position in screen coordinates. You can convert this to "client" coordinates, ie coordinates relative to the control, by calling the control's ScreenToClient method.
So you'll have code something like this:
var
MyPoint : TPoint;
begin
MyPoint := ScrollBox1.ScreenToClient(Mouse.CursorPos);
if PtInRect(ScrollBox1.ClientRect, MyPoint) then
begin
// Mouse is inside the control, do something here
end;
end;
That will let you know if it's inside the control.
From the look of it you're implementing scrolling with the mousewheel? If so don't forget to call SystemParametersInfo with SPI_GETWHEELSCROLLLINES or possibly, if it's in your version of Delphi, Mouse.WheelScrollLines to find out how many lines to scroll per mousewheel increment. What that means to your app probably depends on what you've got in the scrollbox.
If you're planning to also implement middle-click-and-drag scrolling (I'm speculating here, this is well past what you asked about) you might want to get mouse events after the mouse has left the control or form until the user lets go the button, for example. If so, have a look at SetCapture and ReleaseCapture and this article. (That article uses those to see if the mouse is over a control (there, a form) although I think the code I wrote above is a better solution to that specific problem - point is they're handy for getting mouse information even when the mouse is not over your form or control.)
(Edit: I just noticed that Delphi 2010's TMouse has properties that wrap these API calls, WheelScrollLines and Capture. I'm not sure how recently they were added - I might just not have noticed them before - but on the assumption they're new-ish and because you don't say what version of Delphi you're using I'm leaving the above text and WinAPI references. If you're using a recent version have a look at the TMouse documentation.)

I am using the same method to scroll my scrollboxes using the mouse.
This is the event handler for the MouseWheel event of the form. It will scroll horizontally if you press shift key while scrolling:
procedure TForm1.FormMouseWheel(Sender: TObject; Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
var
Msg: Cardinal;
Code: Cardinal;
I, ScrollLines: Integer;
begin
if IsCoordinateOverControl(MousePos, ScrollBox1) then
begin
Handled := True;
If ssShift In Shift Then
Msg := WM_HSCROLL
Else
Msg := WM_VSCROLL;
If WheelDelta < 0 Then
Code := SB_LINEDOWN
Else
Code := SB_LINEUP;
ScrollLines := Mouse.WheelScrollLines * 3;
for I := 1 to ScrollLines do
ScrollBox1.Perform(Msg, Code, 0);
ScrollBox1.Perform(Msg, SB_ENDSCROLL, 0);
end;
end;
You can use this function to check if the screen coordinate of the mouse is over your control:
function IsCoordinateOverControl(screenCoordinate: TPoint; control: TControl): Boolean;
var
p: TPoint;
r: TRect;
begin
Result := False;
p := control.ScreenToClient(screenCoordinate);
r := Rect(0, 0, control.Width, control.Height);
if PtInRect(r, p) then
Result := True;
end;

My Delphi knowledge is a bit rusty, but shouldn't there be MouseEnter, MouseLeave events? A quick google showed this. Does that help you?

Related

How to get the tree-view item which triggered a popup menu?

Its a dumb question maybe but I have a popup menu which is linked with many TTreeViewItems. The problem is that the TTreeView.Selected property never gets set on right click. The GetMousePos is prone to returning the next or the previous TTreeViewItem's coordinates. How can I get the Item which actually triggered the popup?
You can use OnPopup event of TPopupMenu like this:
procedure TForm7.PopupMenu1Popup(Sender: TObject);
var
aNode: TTreeNode;
p: TPoint;
begin
p := TreeView1.ScreenToClient(PopupMenu1.PopupPoint);
aNode := TreeView1.GetNodeAt(p.X, p.Y);
if aNode <> Nil then
caption := aNode.Text;
end;
seems TPopupMenu.PopupPoint returns (0,0) point when you click item in PopupMenu (In Delphi XE2, docwiki says that it is used internally to set position of menu, and seems it is set to 0 when menu dissapears).
so in this situation, seems to me, the easiest way is to handle TreeView.OnMouseDown where you can save reference to selected item, and then use it in Popup item event handler;
so, in the exmaple code below i've added FClickedItem : TTreeViewItem into the form class;
procedure TSampleForm.SampleTreeViewMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
begin
if button = TMouseButton.mbRight then
FClickedItem := SampleTreeView.ItemByPoint(x,y)
else FClickedItem := nil;
end;
procedure TSampleForm.TestMenuItemClick(Sender: TObject);
begin
if Assigned(FClickedItem) then
ShowMessage(Format('Item `%s (%s)` was selected!', [FClickedItem.Text, FClickedItem.Name]))
else ShowMessage('there is nothing to show');
end;
UPDATE: i've just browsed the source code, private variable TPopupMenu.FPopupPoint (readonly property) is not used in implementation code, thats why it is always = (0,0)

TeeChart Series OnMouseEnter event

I am using the version of TeeChart that ships with Rad Studio XE3.
TeeChart provides a TChartSeries event which fires when the mouse pointer moves over a series line. I use this event to display the name of the series under the pointer.
The problem is, give a series line 1 pixel wide, it’s difficult to get the pointer exactly over the line. Is there some way to add ‘padding’ to the event so it fires X number of pixels to each side of the line?
Or is there some other way to accomplish this?
I'm adding a new property to Line (TLineSeries) and FastLine (TFastLineSeries) classes to accomplish this.
Series1.ClickTolerance := 4; // <-- number of pixels around mouse XY
The default value is zero (mouse XY should be exactly over the line), like the current behavior.
As a workaround, if you are using TLineSeries, pointers can be displayed at line point positions, and the internal "clicked" function will consider pointer size:
Series1.Pointer.Visible:=True;
And for more custom control, the code below is very similar to the internal code use to detect mouse clicks. The Tolerance constant specifies the number of extra pixels to consider "in the line".
procedure TForm1.Chart1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
const
Tolerance=4;
var Clicked,
t : Integer;
Position,
P,Old : TPoint;
begin
Clicked:= -1;
Position.X:=X;
Position.Y:=Y;
for t:=Series1.FirstValueIndex to Series1.LastValueIndex do
begin
P.X:=Series1.CalcXPos(t);
P.Y:=Series1.CalcYPos(t);
if t>Series1.FirstValueIndex then
if PointInLine(Position,P.X,P.Y,Old.X,Old.Y,Tolerance) then
begin
Clicked:=t;
break;
end;
Old:=P;
end;
if Clicked = -1 then
Caption:=''
else
Caption:=IntToStr(Clicked);
end;
You can use the PointInLineTolerance function to check it at OnMouseMove event.
However, you have to loop the series points manually to transform the series values into pixels and pass them to this function.
uses Series;
procedure TForm1.FormCreate(Sender: TObject);
var i: Integer;
begin
Chart1.View3D:=false;
for i:=0 to 5 do
Chart1.AddSeries(TLineSeries).FillSampleValues;
end;
procedure TForm1.Chart1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
var series, valueIndex: Integer;
P0, P1: TPoint;
begin
Chart1.Draw;
for series:=0 to Chart1.SeriesCount-1 do
with Chart1[series] do
for valueIndex:=FirstValueIndex to LastValueIndex-1 do
begin
P0.X:=CalcXPos(valueIndex);
P0.Y:=CalcYPos(valueIndex);
P1.X:=CalcXPos(valueIndex+1);
P1.Y:=CalcYPos(valueIndex+1);
if PointInLineTolerance(Point(X, Y), P0.X, P0.Y, P1.X, P1.Y, 5) then
begin
Chart1.Canvas.TextOut(X+5,Y-10,'Series ' + IntToStr(series));
exit;
end;
end;
end;

Delphi: Custom hints for Tree View

Is there a fast way to create 5 custom hints for 5 SubItems of Item of Tree View?
I have TreeView, 1 Item and 5 SubItems. I need a special hint for each SubItem (for first one - "F1", second one -"F2" and so on).
I can not apply this to my purpose: http://delphi.about.com/od/vclusing/a/treenode_hint.htm?
It sounds like you just want the OnHint event:
procedure TMyForm.TreeView1Hint(Sender: TObject; const Node: TTreeNode; var Hint: string);
begin
Hint := Node.Text;
end;
Sometimes this method can be a bit crude and offer up a Node that you aren't obviously hovering over. If you want more control you can use GetNodeAt and GetHitTestInfoAt:
procedure TMyForm.TreeView1Hint(Sender: TObject; const Node: TTreeNode; var Hint: string);
var
P: TPoint;
MyNode: TTreeNode;
HitTestInfo: THitTests;
begin
P := TreeView1.ScreenToClient(Mouse.CursorPos);
MyNode := TreeView1.GetNodeAt(P.X, P.Y);
HitTestInfo := TreeView1.GetHitTestInfoAt(P.X, P.Y) ;
if htOnItem in HitTestInfo then begin
Hint := MyNode.Text;
end else begin
Hint := '';
end;
end;
The definition of THitTests is as follows:
type
THitTest = (htAbove, htBelow, htNowhere, htOnItem, htOnButton, htOnIcon,
htOnIndent, htOnLabel, htOnRight, htOnStateIcon, htToLeft, htToRight);
THitTests = set of THitTest;
As you can see this gives you a lot of fine grained control over when and what you show as a hint.
I would set the hint of the component in response to OnMouseMove (or that other event that gives you mouse coordinates, from which you can get the item the mouse is over - I might have mistaken the name and at the moment I have no Delphi with me).

How do I get another application's window handle passed to Delphi via a mouse click

How can I get the handle of a window to be passed to Delphi by the user selecting the window (could be any other aplication's window) by clicking with the mouse on it. In my Delphi app I could have a button the user clicks that starts this detection process as well as a label displaying the clicked on window's title in the Delphi app. When the user is satisfied he selected the correct window he could click the button in my Delphi app (which will be modal) to stop the selection process and let my app start doing to the other window what it needs to do...
if you know what text is in the title of the window, this code will do the trick for you:
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;
Usage in your example (notepad puts the name of the opened file in the window caption):
h:= getHandle('text.txt');
if (h = 0)
// Oops not found
else
begin
// you got the handle!
end;
I used this code to check if my application was already up and running. But it can be used on any launched application.
The approach that user STATUS_ACCESS_DENIED outlined in the comment is likely the simplest way to go here. I'd recommend using mouse capture over hooking, as it's somewhat simpler to implement.
Here's a slightly more detailed outline of what's involved:
The first thing to change the way that the selection process works. Instead of having the user click a button on your app to start the process, and then click the target window, and finally click again to confirm; it's a lot easier to implement if you have the user click a specific area on your app, then drag to the target window, and then let go of the mouse button while over the target. This is because windows considers a click on another app to belong to that app, and you have to do extra work to intercept it. But there's a simple way - called mouse capture - to get information about a drag/release if it starts off as a click on your own app.
This is also the approach that the Windows SDK Spy++ tool uses; so by doing it this way, you're also being consistent with a well-known tool. (Pic of Spy++ here - note the crosshair Finder Tool in the dialog - that's what you click and drag to the target. Would highly recommend downloading the Windows SDK and playing with this tool if you haven't done so before; it's also a very useful way of seeing how other applications are constructed so great as a Windows API learning tool.)
Steps involved:
Have some control in your app that response to mouse-down events (WM_LBUTTONDOWN in Win32/C, OnMouseDown in delphi). You might want to draw a crosshairs icon or similar here so the user knows where to click.
When you get a mouse down, use SetCapture to 'capture' the mouse. This means that the control will receive all the mouse messages while the mouse is moving - until the user releases the button - even if it moves outside the control.
Set the icon to look like a crosshairs so that the user knows they are in dragging mode
As the user moves the mouse, you'll get WM_MOUSEMOVE message (OnMouseMove in Delphi) that has the pointer coordinates. You'll need to use ClientToScreen to convert these to screen coordinates, then WindowFromPoint to find the window at that point. (Note that this finds the innermost window at that point, you could use ChildWindowFromPoint starting from the desktop window to just get the top-level window if you want that.) It's up to you to decide whether you want to update your UI at every mouse move throughout the drag, or just when the user releases the mouse button.
When the user releases the mouse button, you'll get a WM_LBUTTONUP/OnMouseUp; at that stage, wrap things up by calling ReleaseCapture and putting the cursor back to normal shape.
Note that you'll get mouse move events both during the drag, and also if the user just happens to move the mouse pointer across your control, perhaps on the way to some other control. The simplest way to tell these two cases apart is to use a flag in your control that you set when you get the mouse down, and clear when you get the mouse up, and only process mouse move events if that flag is set.
The above describes the process in terms of plain Win32 APIs that you'd call from C/C++; but it looks like Delphi provides direct support for most or all of them.
edit: Possible Delphi implementation:
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;
Edit: It's gone, but you used to be able to download Delphi Window Spy by Eddie Shipman, from delphipages.com, which has turned into a festering heap of useless linkbait.

How to launch an external application in Delphi and get the window handle of the newly started app? [duplicate]

How can I get the handle of a window to be passed to Delphi by the user selecting the window (could be any other aplication's window) by clicking with the mouse on it. In my Delphi app I could have a button the user clicks that starts this detection process as well as a label displaying the clicked on window's title in the Delphi app. When the user is satisfied he selected the correct window he could click the button in my Delphi app (which will be modal) to stop the selection process and let my app start doing to the other window what it needs to do...
if you know what text is in the title of the window, this code will do the trick for you:
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;
Usage in your example (notepad puts the name of the opened file in the window caption):
h:= getHandle('text.txt');
if (h = 0)
// Oops not found
else
begin
// you got the handle!
end;
I used this code to check if my application was already up and running. But it can be used on any launched application.
The approach that user STATUS_ACCESS_DENIED outlined in the comment is likely the simplest way to go here. I'd recommend using mouse capture over hooking, as it's somewhat simpler to implement.
Here's a slightly more detailed outline of what's involved:
The first thing to change the way that the selection process works. Instead of having the user click a button on your app to start the process, and then click the target window, and finally click again to confirm; it's a lot easier to implement if you have the user click a specific area on your app, then drag to the target window, and then let go of the mouse button while over the target. This is because windows considers a click on another app to belong to that app, and you have to do extra work to intercept it. But there's a simple way - called mouse capture - to get information about a drag/release if it starts off as a click on your own app.
This is also the approach that the Windows SDK Spy++ tool uses; so by doing it this way, you're also being consistent with a well-known tool. (Pic of Spy++ here - note the crosshair Finder Tool in the dialog - that's what you click and drag to the target. Would highly recommend downloading the Windows SDK and playing with this tool if you haven't done so before; it's also a very useful way of seeing how other applications are constructed so great as a Windows API learning tool.)
Steps involved:
Have some control in your app that response to mouse-down events (WM_LBUTTONDOWN in Win32/C, OnMouseDown in delphi). You might want to draw a crosshairs icon or similar here so the user knows where to click.
When you get a mouse down, use SetCapture to 'capture' the mouse. This means that the control will receive all the mouse messages while the mouse is moving - until the user releases the button - even if it moves outside the control.
Set the icon to look like a crosshairs so that the user knows they are in dragging mode
As the user moves the mouse, you'll get WM_MOUSEMOVE message (OnMouseMove in Delphi) that has the pointer coordinates. You'll need to use ClientToScreen to convert these to screen coordinates, then WindowFromPoint to find the window at that point. (Note that this finds the innermost window at that point, you could use ChildWindowFromPoint starting from the desktop window to just get the top-level window if you want that.) It's up to you to decide whether you want to update your UI at every mouse move throughout the drag, or just when the user releases the mouse button.
When the user releases the mouse button, you'll get a WM_LBUTTONUP/OnMouseUp; at that stage, wrap things up by calling ReleaseCapture and putting the cursor back to normal shape.
Note that you'll get mouse move events both during the drag, and also if the user just happens to move the mouse pointer across your control, perhaps on the way to some other control. The simplest way to tell these two cases apart is to use a flag in your control that you set when you get the mouse down, and clear when you get the mouse up, and only process mouse move events if that flag is set.
The above describes the process in terms of plain Win32 APIs that you'd call from C/C++; but it looks like Delphi provides direct support for most or all of them.
edit: Possible Delphi implementation:
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;
Edit: It's gone, but you used to be able to download Delphi Window Spy by Eddie Shipman, from delphipages.com, which has turned into a festering heap of useless linkbait.

Resources