?
Solved

Control has Input Focus? (from Handle)

Posted on 2004-08-25
11
Medium Priority
?
784 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
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 
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

New feature and membership benefit!

New feature! Upgrade and increase expert visibility of your issues with Priority Questions.

Question has a verified solution.

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

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
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…
When cloud platforms entered the scene, users and companies jumped on board to take advantage of the many benefits, like the ability to work and connect with company information from various locations. What many didn't foresee was the increased risk…
Is your OST file inaccessible, Need to transfer OST file from one computer to another? Want to convert OST file to PST? If the answer to any of the above question is yes, then look no further. With the help of Stellar OST to PST Converter, you can e…
Suggested Courses

829 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