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
Solved

Control has Input Focus? (from Handle)

Posted on 2004-08-25
11
758 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
Networking for the Cloud Era

Join Microsoft and Riverbed for a discussion and demonstration of enhancements to SteelConnect:
-One-click orchestration and cloud connectivity in Azure environments
-Tight integration of SD-WAN and WAN optimization capabilities
-Scalability and resiliency equal to a data center

 
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

Free Tool: SSL Checker

Scans your site and returns information about your SSL implementation and certificate. Helpful for debugging and validating your SSL configuration.

One of a set of tools we are providing to everyone 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

This article explains how to create forms/units independent of other forms/units object names in a delphi project. Have you ever created a form for user input in a Delphi project and then had the need to have that same form in a other Delphi proj…
In my programming career I have only very rarely run into situations where operator overloading would be of any use in my work.  Normally those situations involved math with either overly large numbers (hundreds of thousands of digits or accuracy re…
The Email Laundry PDF encryption service allows companies to send confidential encrypted  emails to anybody. The PDF document can also contain attachments that are embedded in the encrypted PDF. The password is randomly generated by The Email Laundr…
In an interesting question (https://www.experts-exchange.com/questions/29008360/) here at Experts Exchange, a member asked how to split a single image into multiple images. The primary usage for this is to place many photographs on a flatbed scanner…

808 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