search a window on title or filename

Post your FreeBASIC source, examples, tips and tricks here. Please don’t post code without including an explanation.
Post Reply
thrive4
Posts: 70
Joined: Jun 25, 2021 15:32

search a window on title or filename

Post by thrive4 »

This has popped up in various guises in essence
find a hwnd via the windows executable (filename)
or windows title.

Re-tracing my findings:
https://stackoverflow.com/questions/412 ... files-name
post by @Remy Lebeau excellent structural advise
viewtopic.php?p=1510&hilit=getwindowtext#p1510
posting by @chaos regrettably could not get it to work
viewtopic.php?p=8778&hilit=getwindowtext#p8778
posting by @jofers very useful
viewtopic.php?t=6246
posting by @michaelW almost there needed tweak for iteration
https://stackoverflow.com/questions/425 ... 6#42589826
posting by @Gil123 excellent and sly, bingo translate to FB

update 18/06/2022
added rudimentary check on openprocess
and closed the handle
excellent point made by @adeyblue

update 16/07/2022
forgot to reinitialize f and t

Code: Select all

' search a window on title or filename
' based on https://stackoverflow.com/questions/42589496/getting-a-list-of-all-open-windows-in-c-and-storing-them/42589826#42589826
' c++ code from Gil123
' tweaked for fb by Thrive4 2022

#include once "windows.bi"
' needed for process and path related api calls
#include once "win\psapi.bi"

' ghetto work around for GetProcessImageFileName
' returning drive as mount aka \device\hardiskvolume<nr>
' shell "echo list volume | diskpart > vol.tmp"
' parse vol.tmp for drive letters
' tricky be carefull with diskpart  

' match window to filename
function matchwindowbyfilename(needle as string) as hwnd

    dim h as hwnd
    dim o as handle
    dim p as dword
    dim l as integer
    dim t as zstring * 100 ' title
    dim c as zstring * 100 ' class
    dim f as zstring * 100 ' filename

    ' init window count
    h = GetTopWindow(NULL)

    ' iterate through windows with GetNextWindow 
    ' IsWindowVisible(h) = false show non visible windows
    ' defenition https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-iswindowvisible
    do
        if IsWindowVisible(h) then
            f = ""
            t = ""
            l = GetWindowTextLength(h)
            GetWindowText(h, @t, 100)
            GetClassName(h, @c, 100)
            GetWindowThreadProcessId(h, @p)
            o = OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_QUERY_LIMITED_INFORMATION, FALSE, p)
            if o <> null then    
                GetProcessImageFileName(o, @f, 100)
                CloseHandle(o)
            else
                print "could not open process with WindowThreadProcessId " & p
            end if
            if f = "" then
                print "no process image for " + t
            end if
            if t = "" then
                print "no title for " & h
            end if
            if needle = "" then
                print "title  : " + t
                print "hwnd   : " & h
                print "file   : " + f
                print "class  : " + c
                print "hwnd   : " & h
                print "----------"
            end if
            if instr(lcase(t), lcase(needle)) > 0 then
                print "match on file with ";
                print "needle : " + needle
                print "title  : " + t
                print "hwnd   : " & h
                print "file   : " + f
                print "class  : " + c
                print "hwnd   : " & h
                print "----------"
                return h
            end if
        end if
        h = GetNextWindow(h, GW_HWNDNEXT)
    loop until GetNextWindow(h, GW_HWNDNEXT) = 0

    return 0

end function

' match window to window title
function matchwindowbytitle(needle as string) as hwnd

    dim h as hwnd
    dim o as handle
    dim p as dword
    dim l as integer
    dim t as zstring * 100 ' title
    dim c as zstring * 100 ' class
    dim f as zstring * 100 ' filename

    ' init window count
    h = GetTopWindow(NULL)

    ' iterate through windows with GetNextWindow 
    ' IsWindowVisible(h) = false show non visible windows
    ' defenition https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-iswindowvisible
    do
        if IsWindowVisible(h) then
            f = ""
            t = ""
            l = GetWindowTextLength(h)
            GetWindowText(h, @t, 100)
            GetClassName(h, @c, 100)
            GetWindowThreadProcessId(h, @p)
            if needle = "" then
                print "title  : " + t
                print "hwnd   : " & h
                print "file   : " + f
                print "class  : " + c
                print "hwnd   : " & h
                print "----------"
            end if
            if instr(lcase(t), lcase(needle)) > 0 then
                print "match on title with ";
                print "needle : " + needle
                print "title  : " + t
                print "hwnd   : " & h
                print "file   : " + f
                print "class  : " + c
                print "hwnd   : " & h
                print "----------"
                return h
            end if
        end if
        h = GetNextWindow(h, GW_HWNDNEXT)
    loop until GetNextWindow(h, GW_HWNDNEXT) = 0

    return 0

end function

matchwindowbyfilename("")
'matchwindowbytitle("")

sleep
end

/' some notes:
QueryFullProcessImageName returns the drive letter
as c:\, d:\ etc
how ever can not get it to work
QueryFullProcessImageName(1, 0, @f, 100)

print _WIN32_WINNT
print cint(&h0600)

' see line 2970 and 2976 in \win\winabse.ini
#if defined(UNICODE) and (_WIN32_WINNT >= &h0600)
    print "unicode and higher w32version"
	'declare function QueryFullProcessImageName alias "QueryFullProcessImageNameW"(byval hProcess as HANDLE, byval dwFlags as DWORD, byval lpExeName as LPWSTR, byval lpdwSize as PDWORD) as WINBOOL
#elseif (not defined(UNICODE)) and (_WIN32_WINNT >= &h0600)
    print "no unicode and higher w32version"
	'declare function QueryFullProcessImageName alias "QueryFullProcessImageNameA"(byval hProcess as HANDLE, byval dwFlags as DWORD, byval lpExeName as LPSTR, byval lpdwSize as PDWORD) as WINBOOL
#endif

' gets the path of calling app aka this app aka exepath with exename
'GetWindowModuleFileName(h, @f, 100)
'/
usage either, example no search criteria
matchwindowbyfilename("")

or, example with search criteria
matchwindowbytitle("winfbe")

For the record matching windows via a title or filename
is in practice quite error prone due to the nature of
the windows title see as example suggestion by coderJeff:
viewtopic.php?t=31586

with the filename things can get funky to, for example
with games, there is a splash screen, then there can be a
dxproxy window, etc.

*) See notes on GetProcessImageFileName vs QueryFullProcessImageName
Last edited by thrive4 on Jul 16, 2022 12:30, edited 2 times in total.
adeyblue
Posts: 299
Joined: Nov 07, 2019 20:08

Re: search a window on title or filename

Post by adeyblue »

Good job. The only nitpick is that you need to CloseHandle the handle you get from OpenProcess (so that needs checking for success before you do) otherwise the code will stop Windows deleting any app that exits from memory until this app closes.
thrive4
Posts: 70
Joined: Jun 25, 2021 15:32

Re: search a window on title or filename

Post by thrive4 »

@adeyblue

Thanks for the kudos and the feedback!
Should have read this better...:
https://docs.microsoft.com/en-us/window ... penprocess

also thanks for the link to 'Zombie Processes are Eating your Memory'
very useful.

Made a small tweak to the code still need
to deal with the thread and the error checking
is rather simple, but for now it should suffice.
Post Reply