Solved

Control has Input Focus? (from Handle)

Posted on 2004-08-25
11
763 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
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
Independent Software Vendors: 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 250 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

Secure Your Active Directory - April 20, 2017

Active Directory plays a critical role in your company’s IT infrastructure and keeping it secure in today’s hacker-infested world is a must.
Microsoft published 300+ pages of guidance, but who has the time, money, and resources to implement? Register now to find an easier way.

Question has a verified solution.

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

Suggested Solutions

Title # Comments Views Activity
code issue 8 187
Delphi: how to implement a User Shortcut mapper? 1 138
Printing problem 2 111
Moving (cutting/pasting) controls in a TTabbedNotebook... 7 48
Introduction The parallel port is a very commonly known port, it was widely used to connect a printer to the PC, if you look at the back of your computer, for those who don't have newer computers, there will be a port with 25 pins and a small print…
Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
Nobody understands Phishing better than an anti-spam company. That’s why we are providing Phishing Awareness Training to our customers. According to a report by Verizon, only 3% of targeted users report malicious emails to management. With compan…
Finds all prime numbers in a range requested and places them in a public primes() array. I've demostrated a template size of 30 (2 * 3 * 5) but larger templates can be built such 210  (2 * 3 * 5 * 7) or 2310  (2 * 3 * 5 * 7 * 11). The larger templa…

730 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