Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

enumWindows with wildcat match?

15 views
Skip to first unread message

NoName NoName

unread,
Nov 16, 2004, 3:13:21 PM11/16/04
to
I get "Exception EAccessViolation" error when trying the following
method. Notice, that it has a nested function.

The following call should match to first "Untitled - Notepad" window.
hnd := findWindowWildcat('', 'Untitled - Note');

It seems I don't know how to use Delphi/win32 callback functions
properly. EnumWindowProc returns only boolean, but I need to return a
matched handle back to the originating function.

---
function findwindowWildcat(const className, windowName: String): HWND;
var
handle: HWND;

{ Nested EnumWindows callback function }
function EnumWindowProc(wnd: HWND; lp: LParam): BOOL; stdcall;
var
buf: array[0..512] of char;
s: string;
begin
if not(className = '') then begin
GetClassName(wnd, buf, 512);
s := buf;
if not(className = s) then begin
// does not match, continue callback loop
result := true;
exit;
end;
end;
GetWindowText(wnd, buf, 512);
s := buf;
if not(uStrings.StartsWith(windowName, s)) then begin
result := true;
exit;
end;

// found matching window, store handle and
// break callback loop
handle := wnd;
result := false;
end;
begin
handle := 0;
EnumWindows(@EnumWindowProc, 0);
result := handle;
end;

Avatar Zondertau

unread,
Nov 17, 2004, 1:06:15 AM11/17/04
to
> I get "Exception EAccessViolation" error when trying the following
> method. Notice, that it has a nested function.
>
> The following call should match to first "Untitled - Notepad" window.
> hnd := findWindowWildcat('', 'Untitled - Note');
>
> It seems I don't know how to use Delphi/win32 callback functions
> properly. EnumWindowProc returns only boolean, but I need to return a
> matched handle back to the originating function.

The callback function cannot be a nested function or it shouldn't refer
to any local variables of the function it's nested in. This is for the
same reason you can't assign a nested function to a procedural type
variable: the stack at the time when it is called will be different
than expected. You should use the parameter for this purpose: make it a
pointer to the handle.

NoName NoName

unread,
Nov 17, 2004, 3:42:25 PM11/17/04
to
"Avatar Zondertau" <avatarz...@hotmail.com> wrote in news:419af867$1
@newsgroups.borland.com:

>> The following call should match to first "Untitled - Notepad" window.
>> hnd := findWindowWildcat('', 'Untitled - Note');
>>
>> It seems I don't know how to use Delphi/win32 callback functions
>> properly. EnumWindowProc returns only boolean, but I need to return a
>> matched handle back to the originating function.
>
> The callback function cannot be a nested function or it shouldn't refer
> to any local variables of the function it's nested in. This is for the
> same reason you can't assign a nested function to a procedural type
> variable: the stack at the time when it is called will be different
> than expected. You should use the parameter for this purpose: make it a
> pointer to the handle.

Uuhhh. "use parameter for this purpose...". What do you mean exactly?
enumWindows callback function has two parameters and it return BOOL.


function EnumWindowProc(wnd: HWND; lp: LParam): BOOL; stdcall;

I still don't see a solution to return windowhandle from the this
procedure. Should I declare a unit-level variable and then assign it in
callback function?

But then it's not a threadsafe variable in .dll function if I understand
it correctly.

Avatar Zondertau

unread,
Nov 18, 2004, 1:40:35 AM11/18/04
to
> Uuhhh. "use parameter for this purpose...". What do you mean exactly?
> enumWindows callback function has two parameters and it return BOOL.
> function EnumWindowProc(wnd: HWND; lp: LParam): BOOL; stdcall;

With the "parameter" i mean the parameter passed to EnumWindows called
"lParam" (the second parameter). This parameter is passed to the
calback procedure:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/w
inui/windowsuserinterface/windowing/windows/windowreference/windowfuncti
ons/enumwindows.asp

> I still don't see a solution to return windowhandle from the this
> procedure. Should I declare a unit-level variable and then assign it
> in callback function?
> But then it's not a threadsafe variable in .dll function if I
> understand it correctly.

If you supply a pointer to the variable keeping the Handle (the local
variable in the function calling EnumWindows) you can modify the handle
by dereferencing the pointer and setting the value. There are not
global variables involved. This is what the extra parameter was
intended for.

If you don't understand what i mean there's another solution though:
use a treadvar. In this case you have a global var that will be
different for each thread.

Threetwosevensixseven

unread,
Nov 18, 2004, 1:02:11 PM11/18/04
to
Avatar Zondertau wrote:
>>Uuhhh. "use parameter for this purpose...". What do you mean exactly?
>>enumWindows callback function has two parameters and it return BOOL.
>> function EnumWindowProc(wnd: HWND; lp: LParam): BOOL; stdcall;
>
> If you supply a pointer to the variable keeping the Handle (the local
> variable in the function calling EnumWindows) you can modify the handle
> by dereferencing the pointer and setting the value. There are not
> global variables involved. This is what the extra parameter was
> intended for.

Here's a similar one I just wrote - uses EnumThreadWindows instead of
EnumWindows, but same principle.

function EnumThreadWindowProc(hWindow: HWND; phPassedWindow: LPARAM):
BOOL; stdcall;
var
aCaption: array[0..18] of char;
begin
Result := true;
SendMessage(hWindow, WM_GETTEXT, 19, LPARAM(@aCaption));
if String(acaption) <> 'Untitled - Notepad' then exit;
LPARAM(Pointer(phPassedWindow)^) := hWindow;
Result := false;
end;

var
hWindow: HWND;
begin
EnumThreadWindows(ThreadID, @EnumThreadWindowProc,
LPARAM(@hWindow));
if hWindow > 0 then ...
end;

Sean B. Durkin

unread,
Nov 19, 2004, 12:34:08 AM11/19/04
to
Sir,

For what its worth, here is my solution. The resultant window
is returned by TMyForm1.FindWindowStartingWith . I hope I have
understood your purpose.

type
TTester = class( TObject) // Abstact tester object
private
FisFound: boolean;
FFoundWindow: HWND;
FPattern: string;
protected
function Test( const Subject: string): boolean; virtual; abstract;
constructor Create( const Pattern: string); virtual;
end;

TTesterClass = class of TTester;

TStartsWithTester = class( TTester)
protected
function Test( const Subject: string): boolean; override;
end;

constructor TTester.Create( const Pattern: string);
begin
FisFound := False;
FPattern := Pattern
end;

function TStartsWithTester.Test( const Subject: string): boolean;
begin
result := uStrings.StartsWith( Subject, FPattern)
end;

function EnumWindowProc(wnd: HWND; lp: LParam): BOOL; stdcall; far;
// The second parameter of EnumWindows is the Tester. This becomes
// the "lp" formal parameter here.
var
Tester: TTester;
WindowCaption: string;
begin
result := False;
Tester := TTester( lp);
if Tester.FisFound then exit; // Stop when a solution has been found.
SetLength( WindowCaption, 1000);
SetLength( WindowCaption, windows.GetWindowText( wnd,
PChar( WindowCaption), Length( WindowCaption)));
Tester.FisFound := Tester.Test( WindowCaption, Tester.FPattern)
end;

FindMatchingWindow( const Pattern: string;
TestClass: TTesterClass): HWND;
var
Tester: TTester;
begin
Tester := TestClass.Create( Pattern);
try
windows.EnumWindows( TFNWndEnumProc( @EnumWindowProc),
LPARAM( Tester);
if Tester.FFisFound then
result := Tester.FFoundWindow
else
result := 0 // or you could put another default solution.
finally
Tester.Free
end end;

TMyForm1.FindWindowStartingWith( const Prefix: string): HWND;
begin
result := FindMatchingWindow( Prefix, TStartsWithTester)
end;

Faithfully,
Sean B. Durkin
sean@{RemoveThis}getdata.com

Noname Noname

unread,
Nov 19, 2004, 8:58:58 AM11/19/04
to
Avatar, SeanB and Threetwosevensixseven,

I really appreciate your comments. About using LPARAM as a pointer to
the _class instance_ is exactly what I need.

I have done some horrible code previously here and there, but your tip
will simplify things a lot.

thx

> Sean B. Durkin wrote:
> For what its worth, here is my solution. The resultant window
> is returned by TMyForm1.FindWindowStartingWith . I hope I have
> understood your purpose.

> function EnumWindowProc(wnd: HWND; lp: LParam): BOOL; stdcall; far;


> // The second parameter of EnumWindows is the Tester. This becomes
> // the "lp" formal parameter here.
> var
> Tester: TTester;
> WindowCaption: string;
> begin
> result := False;
> Tester := TTester( lp);

>Threetwosevensixseven wrote:
> LPARAM(Pointer(phPassedWindow)^) := hWindow;

0 new messages