Solved

Monitor for a specific window / process

Posted on 2003-11-23
5
447 Views
Last Modified: 2010-04-03
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
0
Comment
Question by:fibdev
[X]
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
  • 2
  • 2
5 Comments
 
LVL 7

Assisted Solution

by:jconde
jconde earned 25 total points
ID: 9809201
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
 
LVL 5

Expert Comment

by:DeerBear
ID: 9810052
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
 
LVL 3

Author Comment

by:fibdev
ID: 9810238
jCondi,

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

DeerBeer,

Please elaborate  :)
0
 
LVL 5

Accepted Solution

by:
DeerBear earned 25 total points
ID: 9810379
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
 
LVL 3

Author Comment

by:fibdev
ID: 10090568
I didn't really get what I was after... Just cleaning house.  Happy new year.  :)
0

Featured Post

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!

Question has a verified solution.

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

Suggested Solutions

Title # Comments Views Activity
how to update exe applicatio from internet ? 6 95
Adoquery sql  left join does not work 25 105
How to convert memory stream to PDF file 6 186
enhance the following code 3 42
The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
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…

726 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