Monitor for a specific window / process

Hello All,

I know how to get all the current windows or processes, but for some reason I'm having trouble with something that should be much simpler than that.

I want to monitor for a specific caption and then execute an action if the caption becomes visible.  For example, my app will run in the background, if it sees a window named 'Outlook' then I want it to execute an action or run a function.  I would also like this to be friedly with system resources, but spontanious.  In other words, I don't want to hang a slow system with a low interval timer, but I want the app to react instantly to the apearence of 'Outlook'

If possible, I would like to get partial captions.  As-In if part of the caption is 'Outlook'.  So even if the actual caption is 'Microsoft Outlook' or 'Microsoft Outlook - Contacts' It will still fire my function.

I hope I am clear.  :)
// Happy Coding
LVL 3
fibdevAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

jcondeCommented:
Hi!

In my example, button1 shows the list of the executable files currently running.  Button2 displays all of the running window-captions.  I beleive the code for "Button2" will be the one that helps you out the most.

Basically, modify the code and fit it into a timer and parse each string to find the application you're looking for:


unit Unit1;

interface

uses
  Controls, StdCtrls, Classes, Forms, windows, SysUtils, tlhelp32;

type
  TForm1 = class(TForm)
    Button1: TButton;
    ListBox1: TListBox;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  WindowList: TList;

implementation

{$R *.dfm}


procedure TForm1.Button1Click(Sender: TObject);
var
 I: Integer;
 Snapshot: THandle;
 PE: TProcessEntry32;
begin
 ListBox1.Clear;
 Snapshot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
 if (Snapshot = DWORD(-1)) then
     Exit;
 PE.dwSize := SizeOf(TProcessEntry32);
 if Process32First(Snapshot, PE) then
 repeat
   I := ListBox1.Items.Add(PE.szExeFile);
 until not Process32Next(Snapshot, PE);
 CloseHandle (Snapshot);
end;

function GetWindow (Handle: HWND; LParam: longint): bool; stdcall;
begin
  Result := true;
  WindowList.Add (Pointer(Handle));
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  i: integer;
  Hnd: HWND;
  Buffer: array [0..255] of char;
begin
  ListBox1.Clear;
  try
    WindowList := TList.Create;
    EnumWindows (@GetWindow, 0);
    ListBox1.Items.Clear;
    for i := 0 to WindowList.Count - 1 do begin
      Hnd := HWND (WindowList [i]);
      if IsWindowVisible (Hnd) then begin
        GetWindowText (Hnd, Buffer, SizeOf (Buffer) - 1);
        if Buffer [0] <> #0 then
          ListBox1.Items.Add (StrPas(Buffer));
      end;
    end;
  finally
    WindowList.Free;
  end;
end;

end.
0
DeerBearCommented:
Hi,

You can "easily" do it using hooks <g>.

It's enough to use the GetMessage hook, and monitor the WM_SETFOCUS message.
Once you find the window text and compare it to your text, you can issue the
related action.

HTH,

Andrew

P.S. To use Hooks, have a look at SetWindowsHookEx/UnHookWindowsHookEx.
0
fibdevAuthor Commented:
jCondi,

Thanks for the code.  I am already alble to do that.  :)

DeerBeer,

Please elaborate  :)
0
DeerBearCommented:
Hi again,

I was simply saying you should use a hook, specifically the WH_GETMESSAGE hook, which will
monitor the incoming messages for a Window. It can be local or system wide.

In the system-wide version, you can monitor all messages from all the windows in the system,
thus you just retrieve the caption for the Window you're interested in and then issue appropriate
action if the message is WM_SETFOCUS or WM_SETFOREGROUND( which is the one that actually
makes the form visible ).

That's all, in theory.

The problem is in practice <g>.

Now I'll show you a very simple way to implement what you need, but please
beware that this code can be DANGEROUS and you can't debug it as you'd do
with any other DLL, k?

I'll simply write it without testing it, thus you'll have to test it on your own.

If someone has the time to, please look at it and correct it if needed.

Code:


library TestLib;

interface

uses Windows, Messages,
       uWindowUtils; // This unit comes from my own codebase and will be posted just after the
                            // library code.

var Caption : String;

function GetMessageHook (code: Integer; wparam: WPARAM; lparam: LPARAM): LRESULT stdcall;
var Msg : tagMsg;
     TmpCaption : String;
begin
  if code > 0 then
  begin
     @Msg := Pointer( lparam );
     case Msg.message of
        WM_SETFOREGROUND : begin
                                               TmpCaption := uWindowUtils.GetWindowText( Msg.Handle );
                                               if TmpCaption = Caption then
                                                  DoYourActionHere;
                                            end;
    end;
  end
  else Result := CallNextHook( WH_GETMESSAGE,wparam,lparam );
end;

var TheHook : Integer;

procedure InstallHook( CaptionValue : String );stdcall;
begin
   Caption := CaptionValue;
   theHook := SetWindowsHookEx(WH_GETMESSAGE, @MyHookProc, hInstance, 0);
end;

procedure RemoveHook;stdcall;
begin
   UnhookWindowsHookEx(theHook);
end;

exports
 
    InstallHook index 1,
    RemoveHook index 2;

end.

uWindowUtils module:

unit uWindowUtils;

interface

uses Windows;

function GetWindowText( Handle : THandle ) : String;
function GetWindowClass( Handle : THandle ) : String;

implementation

function GetWindowText( Handle : THandle ) : String;
var WText : PChar;
    TxtBuf : String;
begin
  GetMem( WText,255 );
  Windows.GetWindowText( Handle,WText,255 );
  TxtBuf := WText;
  Result := TxtBuf;
  FreeMem( WText );
end;

function GetWindowClass( Handle : THandle ) : String;
var WText : PChar;
    TxtBuf : String;
begin
  GetMem( WText,255 );
  GetClassName( Handle,WText,255 );
  TxtBuf := WText;
  Result := TxtBuf;
  FreeMem( WText );
end;

end.

To make it work appropriately, you'll need to setup a MemoryMappedFile.

Look to my reply history for an example of how to do it.

HTH,

Andrew
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
fibdevAuthor Commented:
I didn't really get what I was after... Just cleaning house.  Happy new year.  :)
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Delphi

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.