Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

Thread: GetForegroundWindow from a DLL


This question is answered.


Permlink Replies: 3 - Last Post: Oct 27, 2015 4:36 PM Last Post By: Kevin Heaton
Kevin Heaton

Posts: 3
Registered: 3/13/02
GetForegroundWindow from a DLL  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Oct 27, 2015 2:01 PM
We have a program that consists of an EXE file and a DLL file. This program displays the X and Y position of the mouse pointer and the color of the pixel under the mouse. This program has been around for a long time and has always worked until now.

Now the newly compiled version of this program freezes if the mouse is moved over the window of the program that is running. Attempts to move the program by clicking and dragging from the title bar fails more often that it works. Attempts to click on the [x] to close the program usually fails. We have to close it using the task manager.

I have narrowed the problem down to the calls to GetCursorPos, GetForegroundWindow and GetWindowRect APIs. (A code snipped appears below.) If these are commented out then the program does not stall, can be dragged and closes via the [x].

The EXE/DLL files compiled in 1/31/14 still work fine. This has been tested on Windows XP, Windows 7, Windows 8.1 and Windows 10.

We had been compiling this program using Delphi 2. When that did not work I tweaked a few things to get it to compile it using Delphi 2007. It still does not work. I reverted to the original code and that does not work.

Since this program was last compiled I installed Delphi Studio XE7. Due to difficulties keeping versions of components for both Delphi 2007 and Studio XE7 on the same computer we purchased a new computer and installed Studio XE7 on it. Uninstalling Studio XE7 did not help the problem.

Also since this program was last compiled many Microsoft updates have been applied. However, since the existing version of the program still works, it does not seem to me that the problem is caused by Windows patches.

Summary:
- API calls GetCursorPos, GetForegroundWindow and GetWindowRect are not working anymore.
- Older version works fine on all versions of windows.
- Recompiled version does not work when compiled with either Delphi 2 or Delphi 2007.
- Studio XE7 was uninstalled but that didn't help.

I am stuck and at a loss about how to fix this. Any and all suggestions would be appreciated.

Code snippet:
{-----------------------------------------------------------------------------}
{ MouseHook - The code called by the mouse hook
{=============================================================================}
function MouseHook (Code: integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
var
M : THandle;
Wind : THandle;
THook : HHook;
WaitResult : dword;
Pt : TPoint;
R : TRect;
X, Y : integer;
begin
MaskX86Exceptions;

Result := 0;
THook := 0;

if(GlobData = nil) then
exit;

{----------------------}
{ Get the Memory Mutex }
{----------------------}
M := OpenMutex(MUTEX_ALL_ACCESS, FALSE, FMutName);
M := OpenMutex(SYNCHRONIZE, FALSE, FMutName);
if(M = 0) then
exit;

try {finally}
{------------------------}
{ Syncronize the Threads }
{------------------------}
WaitResult := WaitForSingleObject(M, 500);
if(WaitResult <> WAIT_OBJECT_0) then
exit;

THook := GlobData^.HookHandle;

{-----------------------------------------}
{ MSDN says we need to call the next hook }
{-----------------------------------------}
if(Code < 0) then
exit;

{------------------------}
{ Handle Mouse Movements }
{------------------------}
if((wParam = WM_MOUSEMOVE) or (wParam = WM_NCMOUSEMOVE)) then
begin
GetCursorPos(Pt);
X := Pt.X;
Y := Pt.Y;

{-----------------------------}
{ Send the screen coordinates }
{-----------------------------}
PostMessage(GlobData^.ActiveHandle, WM_APP+2, X, Y); // This sends a message to the EXE part of the program so the window can be updated

{-----------------------------}
{ Send the window coordinates }
{-----------------------------}
Wind := GetForegroundWindow();
GetWindowRect(Wind, R);
X := X - R.Left;
Y := Y - R.Top;
PostMessage(GlobData^.ActiveHandle, WM_APP+3, X, Y); // This sends a message to the EXE part of the program so the window can be updated
end;
{-----------------------------------------------}
{ If necessary, call the next hook in the chain }
{-----------------------------------------------}
if(THook <> 0) then
Result := CallNextHookEx(THook, Code, wParam, lParam);
end;
end;

Edited by: Kevin Heaton on Oct 27, 2015 3:07 PM
Added a longer code snippet
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: GetForegroundWindow from a DLL [Edit]
Correct
Click to report abuse...   Click to reply to this thread Reply
  Posted: Oct 27, 2015 3:01 PM   in response to: Kevin Heaton in response to: Kevin Heaton
Kevin wrote:

Now the newly compiled version of this program freezes if the mouse
is moved over the window of the program that is running. Attempts to
move the program by clicking and dragging from the title bar fails more
often that it works. Attempts to click on the [x] to close the program
usually fails. We have to close it using the task manager.

The only way that can happen is if your program's main UI thread is being
blocked from retreiving and processing new messages from its message queue.

I have narrowed the problem down to the calls to GetCursorPos,
GetForegroundWindow and GetWindowRect APIs.

There is no possible way that those API functions can be causing your problem.
Something else is happening to block your program.

We had been compiling this program using Delphi 2. When that did not
work I tweaked a few things to get it to compile it using Delphi 2007.

The Delphi version is irrelevant. You are calling Win32 API functions, which
are not affected by the Delphi version, only the OS version.

- API calls GetCursorPos, GetForegroundWindow and GetWindowRect are
not working anymore.
- Older version works fine on all versions of windows.

If that were true, the problem would be affecting every program that uses
them, not just your new program. Your older program, and other program,
would all be affected the same way.

MouseHook - The code called by the mouse hook

Why are you using a mouse hook at all? What does your program do with that
tracking data? When you have to resort to low-level hooks, you should think
about what you are really trying to accomplish. There might be a bette way
(and in this case, there is).

MaskX86Exceptions

You only need that for DLLs that are loaded into other processes. But a
mouse hook does not do that. It is called in the context of the thread that
installs the hook (ie, your program). So your hook is not affected by cross-process
floating-point issues. Not that your code is doing anything that involves
floating-point operations anyway. Plus, you shouldn't be modifying the exception
mask every time the hook procedure is called anyway, only once when the DLL
is loaded into memory.

M := OpenMutex(MUTEX_ALL_ACCESS, FALSE, FMutName);
M := OpenMutex(SYNCHRONIZE, FALSE, FMutName);

Why are you opening the same mutex twice? You are also leaking both handles,
as you are not calling CloseHandle() on either of them. You must close them
when you are done using them. It would be better to load the mutex once
when the DLL is loaded into memory, not every time the hook procedure is
called.

Any why are using a mutex at all? To serialize access to your GlobData?
Your MouseProc is called in the context of the thread that installs the
hook, so there is no need to protect access to GlobData since only one thread
will ever access it.

{ MSDN says we need to call the next hook }

So why aren't you? You are skipping CallNextHookEx() when GlobData is nil
or Code is negative, violating the documentation, and potetially breaking
other apps that have also installed mouse hooks.

PostMessage(GlobData^.ActiveHandle, WM_APP+2, X, Y);
// This sends a message to the EXE part of the program so the
window can be updated

I would strongly suggest you use RegisterRawInputDevices() instead of SetWindowsHookEx():

RegisterRawInputDevices function
https://msdn.microsoft.com/en-us/library/windows/desktop/ms645600.aspx

That way, the mouse driver itself will send WM_INPUT messages directly to
any window you choose, which can be your program's own window:

WM_INPUT message
https://msdn.microsoft.com/en-us/library/windows/desktop/ms645590.aspx

No DLL needed, no global hook needed, no deadlock potential.

GetCursorPos(Pt);
...
Wind := GetForegroundWindow();

You don't actually need GetCaretPos() and GetForegroundWindow() at all, since
the hook data gives you that information directly (assuming you are using
a WH_MOUSE hook and not a WH_MOUSE_LL hook):

MOUSEHOOKSTRUCT structure
https://msdn.microsoft.com/en-us/library/windows/desktop/ms644968.aspx

pt
Type: POINT

The x- and y-coordinates of the cursor, in screen coordinates.
hwnd
Type: HWND

A handle to the window that will receive the mouse message corresponding
to the mouse event.

if(THook <> 0) then
Result := CallNextHookEx(THook, Code, wParam, lParam);

You don't need to check THook for 0. In fact, you don't even need to pass
THook to CallNextHookEx() at all, because it will just be ignored anyway.
This is documented behavior:

CallNextHookEx function
https://msdn.microsoft.com/en-us/library/windows/desktop/ms644974.aspx

hhk [in, optional]
Type: HHOOK

This parameter is ignored.

If you do not want to switch to RegisterRawInputDevices(), then at least
try this hook procedure instead:

function MouseHook (Code: integer; wParam: WPARAM; lParam: LPARAM): LRESULT; 
stdcall;
var
  ClientPt : TPoint;
begin
  {------------------------}
  { Handle Mouse Movements }
  {------------------------}
  if (GlobData <> nil) and
     (Code = HC_ACTION) and
     ((wParam = WM_MOUSEMOVE) or (wParam = WM_NCMOUSEMOVE)) then
  begin
    with PMouseHookStruct(lParam)^ do
    begin
      {-----------------------------}
      { Send the screen coordinates }
      {-----------------------------}
      PostMessage(GlobData^.ActiveHandle, WM_APP+2, Pt.X, Pt.Y);
 
      {-----------------------------}
      { Send the window coordinates }
      {-----------------------------}
      ClientPt := Pt;
      ScreenToClient(hwnd, ClientPt);
      PostMessage(GlobData^.ActiveHandle, WM_APP+3, ClientPt.X, ClientPt.Y);
    end;
  end;
  {-----------------------------------------------}
  { Call the next hook in the chain }
  {-----------------------------------------------}
  Result := CallNextHookEx(0, Code, wParam, lParam);
end;
 
function InstallMouseHook (Wnd: HWND): Boolean; stdcall;
begin
  Result := False;
  if (GlobData = nil) then Exit;
  if (GlobData^.THook = 0) then
  begin
    GlobData^.THook := SetWindowsHookEx(WH_MOUSE, @MouseHook, HInstance, 0);
    if GlobData^.THook = 0 then Exit;
  end;
  GlobData^.ActiveHandle := Wnd;
  Result := True;
end;
 
function UninstallMouseHook: Boolean; stdcall;
begin
  Result := True;
  if (GlobData = nil) then Exit;
  if (GlobData^.THook <> 0) then
  begin
    if not UnhookWindowsHookEx(GlobData^.THook) then Exit;
    GlobData^.THook = 0;
  end;
  GlobData^.ActiveHandle := 0;
  Result := True;
end;


--
Remy Lebeau (TeamB)
Kevin Heaton

Posts: 3
Registered: 3/13/02
Re: GetForegroundWindow from a DLL [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Oct 27, 2015 3:53 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Thank you Remy. I will study your response carefully, make changes and report back. One general answer to your questions (other than the duplicated Mutex - my mistake) is "I don't know". This program was written by other programmers and, until now, I have not had to do anything with the source other than to occasionally recompile it. (I am still confused why it suddenly stopped working.) Now I get to learn how it should work. Thanks again for your speedy response.
Kevin Heaton

Posts: 3
Registered: 3/13/02
Re: GetForegroundWindow from a DLL [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Oct 27, 2015 4:36 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
I revised the hook code according to your example and the program no longer freezes. Thank you!
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02