?
Solved

Control has Input Focus? (from Handle)

Posted on 2004-08-25
11
Medium Priority
?
774 Views
Last Modified: 2010-04-04
I have a handle to a control external to my program and I want to know if it has keyboard input focus. Basically I need a function like HasFocus(Hwnd) that returns true/false.

Note: I don't want to use AttachThreadInput because it resets the key state.

Thanks in advance,

D2.
0
Comment
Question by:wavget
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 6
  • 3
  • 2
11 Comments
 
LVL 27

Expert Comment

by:kretzschmar
ID: 11894731
?? maybe

function hasfocus(AHandle : THandle) : Boolean;
var
  vHandle : Thandle;
begin
  vHandle := GetFocus;
  result := vHandle = AHandle;
end;

just from head

meikl ;-)
0
 
LVL 27

Expert Comment

by:kretzschmar
ID: 11894768
hmm, sorry, overlooked external, so getfocus may not work :-(
0
 
LVL 1

Author Comment

by:wavget
ID: 11895404
Nope, all those wonderful functions like GetActiveWindow, GetFocus, and GetTopWindow don't work for what I am looking for.

I tried using EnumWindows and GetNextWindow to explore the Z-order of things, but also without any luck.
0
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 14

Expert Comment

by:Pierre Cornelius
ID: 11903311
Try this

function ControlHasFocus(ControlHandle: HWND): Boolean;
var FGW: HWND;
begin
  FGW:= GetForeGroundWindow;
  AttachThreadInput(GetCurrentThreadID, //thread to attach
                    GetWindowThreadProcessID(FGW) //thread to attach to
                    true); //attach or detach
  result:= (ControlHandle = GetFocus);
  AttachThreadInput(GetCurrentThreadID, //thread to attach
                    GetWindowThreadProcessID(FGW) //thread to attach to
                    false); //attach or detach
end;

Regards
Pierre Cornelius
0
 
LVL 14

Expert Comment

by:Pierre Cornelius
ID: 11903552
I have put together a demo for you:

APP 1:
====

program ExternalControlHasFocusDemo;

uses
  Forms,
  main in 'main.pas' {Form1};

{$R *.res}

begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

~~~~~
unit main;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, AppEvnts, ExtCtrls;

type
  TForm1 = class(TForm)
    Label1: TLabel;
    inpHandle: TEdit;
    lblResult: TLabel;
    Timer1: TTimer;
    procedure Timer1Timer(Sender: TObject);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

function ControlHasFocus(ControlHandle: HWND): Boolean;
var FGW: HWND;
begin
  FGW:= GetForeGroundWindow;
  AttachThreadInput(GetCurrentThreadID, //thread to attach
                    GetWindowThreadProcessID(FGW), //thread to attach to
                    true); //attach or detach
  result:= (ControlHandle = GetFocus);
  AttachThreadInput(GetCurrentThreadID, //thread to attach
                    GetWindowThreadProcessID(FGW), //thread to attach to
                    false); //attach or detach
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var h: HWND;
begin
  h:= StrToInt(inpHandle.Text);
  if ControlHasFocus(h)
    then lblResult.Caption:= 'Control has focus.'
    else lblResult.Caption:= 'Control does not have focus.';
end;

end.

object Form1: TForm1
  Left = 192
  Top = 114
  Width = 696
  Height = 93
  Caption = 'Form1'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object Label1: TLabel
    Left = 16
    Top = 24
    Width = 79
    Height = 13
    Caption = 'Handle to check'
  end
  object lblResult: TLabel
    Left = 264
    Top = 32
    Width = 36
    Height = 13
    Caption = 'Result: '
  end
  object inpHandle: TEdit
    Left = 112
    Top = 24
    Width = 121
    Height = 21
    TabOrder = 0
    Text = '0'
  end
  object Timer1: TTimer
    OnTimer = Timer1Timer
    Left = 312
    Top = 8
  end
end


APP 2:
====

program ExternalHandleApp;

uses
  Forms,
  Main2 in 'Main2.pas' {Form2};

{$R *.res}

begin
  Application.Initialize;
  Application.CreateForm(TForm2, Form2);
  Application.Run;
end.

~~~~~
unit Main2;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm2 = class(TForm)
    Button1: TButton;
    lblHandle: TLabel;
    Button2: TButton;
    Edit1: TEdit;
    Button3: TButton;
    CheckBox1: TCheckBox;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

procedure TForm2.Button1Click(Sender: TObject);
begin
  lblHandle.Caption:= IntToStr(ActiveControl.Handle);
end;

end.

~~~~~
object Form2: TForm2
  Left = 196
  Top = 240
  Width = 565
  Height = 342
  Caption = 'Form2'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object lblHandle: TLabel
    Left = 24
    Top = 64
    Width = 105
    Height = 13
    AutoSize = False
  end
  object Button1: TButton
    Left = 16
    Top = 24
    Width = 185
    Height = 25
    Caption = 'Button1'
    TabOrder = 0
    OnClick = Button1Click
    OnEnter = Button1Click
  end
  object Button2: TButton
    Left = 312
    Top = 40
    Width = 75
    Height = 25
    Caption = 'Button2'
    TabOrder = 1
    OnClick = Button1Click
    OnEnter = Button1Click
  end
  object Edit1: TEdit
    Left = 328
    Top = 96
    Width = 121
    Height = 21
    TabOrder = 2
    Text = 'Edit1'
    OnEnter = Button1Click
  end
  object Button3: TButton
    Left = 344
    Top = 152
    Width = 75
    Height = 25
    Caption = 'Button3'
    TabOrder = 3
    OnClick = Button1Click
    OnEnter = Button1Click
  end
  object CheckBox1: TCheckBox
    Left = 344
    Top = 192
    Width = 97
    Height = 17
    Caption = 'CheckBox1'
    TabOrder = 4
    OnEnter = Button1Click
  end
end

Regards
Pierre
0
 
LVL 1

Author Comment

by:wavget
ID: 11904612
PierreC, thanks for all the code, but I have tried something similar myself using AttachThreadInput.

Strange things happen when I use this function though. Depending on the timing of the function it sometimes alters mouse input behavior. For example, sometimes double click doesn't work. You can observe this when you set the Timer Interval to a small value (like 50mS) so the function gets called a lot.

I'm guessing it has something to do with KeyState since the help file states "Note that key state is reset after a call to AttachThreadInput."

Any other ideas? :)
0
 
LVL 14

Expert Comment

by:Pierre Cornelius
ID: 11907001
It worked fine when setting to 50ms on my machine, but when setting to 10 ms I also had problems with activating the double-click event.

I doubt it has anything to do with the fact that the "key state is reset". I tested this hunch/theroy of mine by calling the AttachThreadInput only upon form creation and Detaching it upon destruction instead of everytime the ControlHasFocus function is called. The result was the same problem.

I think the problem lies in the processing of messages in your applications main thread. I.e. once the ControlHasFocus function is called, your application thread executes it first before processing or receiving any further messages. Even though in the demo such processing lies in two separate threads, they share input processing which may prevent other input messages from being dispatched. Perhaps running the check in a separate thread could provide a solution. I'm looking into it at the moment.

Regards
Pierre
0
 
LVL 14

Accepted Solution

by:
Pierre Cornelius earned 1000 total points
ID: 11907786
Using another thread does not solve our problem. You were right about the resetting issue. I think the AttachThreadInput not only resets the key state, but the entire message queue. My suggestion is to not call the AttachthreadInput repeatedly, but only once for each process to be checked i.e. change the function as follows:

...
var FGW: HWND;
...
function ControlHasFocus(ControlHandle: HWND): Boolean;
begin
  if FGW <> GetForeGroundWindow then
  begin
    FGW:= GetForeGroundWindow;
    AttachThreadInput(GetCurrentThreadID, //thread to attach
                      GetWindowThreadProcessID(FGW), //thread to attach to
                      true); //attach or detach
  end;
  result:= (ControlHandle = GetFocus);
end;

Notice that I changed the FGW from local to global and no longer detach from the process after each time the ControlHasFocus function is called.

I've tested this and it seems to work just fine.

Regards
Pierre
0
 
LVL 14

Expert Comment

by:Pierre Cornelius
ID: 11907818
Further Note:

I also considered using a Application.ProcessMessages before calling the AttachThreadInput function and in essence before the queue is cleared but this would result in a constant loop.
0
 
LVL 14

Expert Comment

by:Pierre Cornelius
ID: 11907841
I just thought about this:

We are never detaching from the other thread(s). It may be a good idea to do so before attaching to another one e.g.

function ControlHasFocus(ControlHandle: HWND): Boolean;
begin
  if FGW <> GetForeGroundWindow then
  begin
    if FGW <> 0
      then   AttachThreadInput(GetCurrentThreadID, //thread to attach
                                           GetWindowThreadProcessID(FGW), //thread to attach to
                                           false); //attach or detach
    FGW:= GetForeGroundWindow;
    AttachThreadInput(GetCurrentThreadID, //thread to attach
                      GetWindowThreadProcessID(FGW), //thread to attach to
                      true); //attach or detach
  end;
  result:= (ControlHandle = GetFocus);
end;
0
 
LVL 1

Author Comment

by:wavget
ID: 11921625
PierreC, you certainly have done a lot of digging on the subject! I'm increasing the points for this question.

I'm tried your code in a little test application and it seems to work well. Of course when I tried adding the code to the main application, things didn't work as expected, but that's probably my fault :)

Once I get it to work, if I find it didn't work because of how AttachThreadInput affects the operation of my program, I'll post my findings here.

Thanks :)
0

Featured Post

Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
Objective: - This article will help user in how to convert their numeric value become words. How to use 1. You can copy this code in your Unit as function 2. than you can perform your function by type this code The Code   (CODE) The Im…
In this brief tutorial Pawel from AdRem Software explains how you can quickly find out which services are running on your network, or what are the IP addresses of servers responsible for each service. Software used is freeware NetCrunch Tools (https…
How to fix incompatible JVM issue while installing Eclipse While installing Eclipse in windows, got one error like above and unable to proceed with the installation. This video describes how to successfully install Eclipse. How to solve incompa…
Suggested Courses
Course of the Month12 days, 7 hours left to enroll

777 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question