Solved

Control has Input Focus? (from Handle)

Posted on 2004-08-25
11
743 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
  • 6
  • 3
  • 2
11 Comments
 
LVL 27

Expert Comment

by:kretzschmar
Comment Utility
?? 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
Comment Utility
hmm, sorry, overlooked external, so getfocus may not work :-(
0
 
LVL 1

Author Comment

by:wavget
Comment Utility
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
 
LVL 14

Expert Comment

by:Pierre Cornelius
Comment Utility
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
Comment Utility
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
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 1

Author Comment

by:wavget
Comment Utility
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
Comment Utility
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 250 total points
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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

How to improve team productivity

Quip adds documents, spreadsheets, and tasklists to your Slack experience
- Elevate ideas to Quip docs
- Share Quip docs in Slack
- Get notified of changes to your docs
- Available on iOS/Android/Desktop/Web
- Online/Offline

Join & Write a Comment

Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have to…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
It is a freely distributed piece of software for such tasks as photo retouching, image composition and image authoring. It works on many operating systems, in many languages.
Access reports are powerful and flexible. Learn how to create a query and then a grouped report using the wizard. Modify the report design after the wizard is done to make it look better. There will be another video to explain how to put the final p…

743 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

Need Help in Real-Time?

Connect with top rated Experts

14 Experts available now in Live!

Get 1:1 Help Now