Window handle from process handle?

Just starting out? Need help? Post your questions and find answers here.
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Window handle from process handle?

Post by Mistrel »

I wrote this function to try and get a window handle from the process handle but it can't find any matching processes. Is there some subtle error in my code or is it my logic?

Code: Select all

Procedure EnumWindowsProc(hWnd,lParam)
	GetWindowThreadProcessId_(hWnd,@lpProc)
	If lpProc=PeekL(lParam) ; process passed to EnumWindows is process found at this hwnd
		PokeL(lParam,hWnd) ; set ptr to hwnd
		ProcedureReturn 0
	EndIf
	ProcedureReturn 1
EndProcedure

Procedure GetProcHwnd(lpProc)
	ptr=lpProc
	Repeat : Until EnumWindows_(@EnumWindowsProc(),@ptr)
	If ptr=lpProc ; if no handle is returned no matching process was found
		ProcedureReturn 0
	EndIf
	; some form of GetWindow_() here for top-level window..
	ProcedureReturn ptr
EndProcedure

program=RunProgram("Notepad.exe","","",#PB_Program_Open)
id=ProgramID(program)
hproc=OpenProcess_(#PROCESS_ALL_ACCESS,0,id)
Debug hproc
If Not GetProcHwnd(hproc)
	Debug "GetProcHwnd failed."
Else
	Debug "Success!"
EndIf
PB
PureBasic Expert
PureBasic Expert
Posts: 7581
Joined: Fri Apr 25, 2003 5:24 pm

Re: Window handle from process handle?

Post by PB »

Here's my code that I've been using for years:

Code: Select all

; RunProgramEx by PB: Retrieves handle of an app's opened window.

Procedure RunProgramEx(file$,param$,dir$,showflag)
  If Left(param$,1)<>" " : param$=" "+param$ : EndIf
  Info.STARTUPINFO : Info\cb=SizeOf(STARTUPINFO) : Info\dwFlags=1
  Info\wShowWindow=showflag : ProcessInfo.PROCESS_INFORMATION
  If CreateProcess_(@file$,@param$,0,0,0,#NORMAL_PRIORITY_CLASS,0,@dir$,@Info,@ProcessInfo)
    ProcessID=ProcessInfo\dwProcessId
    Repeat
      win=FindWindow_(0,0)
      While win<>0
        GetWindowThreadProcessId_(win,@pid)
        If pid=ProcessID : WinHandle=win : Break : EndIf
        win=GetWindow_(win,#GW_HWNDNEXT)
      Wend
    Until WinHandle
  EndIf
  ProcedureReturn WinHandle
EndProcedure

app$="c:\windows\notepad.exe" ; Full path needed!
hWnd=RunProgramEx(app$,"",GetPathPart(app$),#SW_SHOW)
Sleep_(2000) ; Give it a couple of seconds to open.
PostMessage_(hWnd,#WM_CLOSE,0,0) ; Now close it.
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Post by Mistrel »

The notepad window does not close and I'm receiving an error when trying to use your code, PB. This is my debug output:

Code: Select all

4068130
1
Invalid window handle.
After adding these lines to the end:

Code: Select all

Procedure.s ShowAPIError(CheckReturnValue) ; forum code, unsure of original author
	Buffer.s=Space (4096)
	NumberOfChars=FormatMessage_(#FORMAT_MESSAGE_FROM_SYSTEM,0,CheckReturnValue,0,Buffer.s,Len(Buffer.s),0)
	ProcedureReturn Left(Buffer.s,NumberOfChars-2)
EndProcedure

Debug hWnd
Debug IsWindow_(hWnd)
Debug ShowAPIError(GetLastError_())
Getting the window to close has been very sporadic. Sometimes it will close and sometimes it won't. Sometimes it closes but IsWindow returns false. Other times IsWindow returns true but the window doesn't close.

This is all from the same source.
PB
PureBasic Expert
PureBasic Expert
Posts: 7581
Joined: Fri Apr 25, 2003 5:24 pm

Re: Window handle from process handle?

Post by PB »

Hmm, this code has been 100% reliable for me for many years. Don't know
why it's failing for you. I've used it with Win98SE, Win2K and WinXP with no
problems. Even running it now, with your code added, always returns this:

Code: Select all

10683168
0
The operation completed successfully.
All I can suggest is try putting Delay(500) after the CreateProcess line, in
case a faster CPU than mine is doing something too fast. Or maybe it's due
to dual-processors or something. Don't know. Just guessing. Try the delay.
And ensure the correct full path for Notepad.exe is being used (looks like it
is though, as you said sometimes it starts but other times it doesn't).
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Post by Mistrel »

This time the window did not close, IsWindow returned false, and ShowAPIError returned successful. :?

I tried to use this code to force it to close but I never get more than one output to the debug window and it's still hit or miss.

Code: Select all

Repeat
	Debug SendMessage_(hWnd,#WM_CLOSE,0,0)
Until Not IsWindow(hWnd)

End
I tried uninstalling and reinstalling PB too.

I'm also concerned with this line of my code:

Code: Select all

OpenProcess_(#PROCESS_ALL_ACCESS,0,id)
Even though I pass a unique PID every time the function only returns either 1952 or 1956 as the process handle. This still occurs even if I pass a completely different file. How can this be accurate?

***

Upon further testing I found that adding this to your code will make the application close every time.

Code: Select all

Repeat
hWnd=FindWindow_("Notepad","Untitled - Notepad")
Until hWnd
So this is an issue with the speed of my computer.
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8425
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Post by netmaestro »

Repeat
hWnd=FindWindow_("Notepad","Untitled - Notepad")
Until hWnd
That's going to close it without any other code hehe. After you send the message, of course. Try this modification:

Code: Select all

Procedure EnumProc(hwnd, param)
  *handle.LONG = param
  *handle\l = hwnd
  ProcedureReturn 0
EndProcedure

Procedure RunProgramEx(file$,param$,dir$,showflag) 
  If Left(param$,1)<>" " : param$=" "+param$ : EndIf 
  Info.STARTUPINFO : Info\cb=SizeOf(STARTUPINFO) : Info\dwFlags=1 
  Info\wShowWindow=showflag : ProcessInfo.PROCESS_INFORMATION 
  If CreateProcess_(@file$,@param$,0,0,0,#NORMAL_PRIORITY_CLASS,0,@dir$,@Info,@ProcessInfo) 
    ThreadID=ProcessInfo\dwThreadID
  EndIf 
  ProcedureReturn ThreadID 
EndProcedure 

app$="c:\windows\notepad.exe" ; Full path needed! 
ThreadID=RunProgramEx(app$,"",GetPathPart(app$),#SW_SHOW) 
Sleep_(2000) 
EnumThreadWindows_(ThreadId, @EnumProc(), @hWnd.l)
PostMessage_(hWnd,#WM_CLOSE,0,0) ; Now close it. 
BERESHEIT
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Post by Mistrel »

Here is an improvement to your method that does not rely upon a delay.

Code: Select all

Procedure EnumProc(hwnd, param)
  *handle.LONG = param
  *handle\l = hwnd
  ProcedureReturn 0
EndProcedure

Procedure RunProgramEx(file$,param$,dir$,showflag)
  If Left(param$,1)<>" " : param$=" "+param$ : EndIf
  Info.STARTUPINFO : Info\cb=SizeOf(STARTUPINFO) : Info\dwFlags=1
  Info\wShowWindow=showflag : ProcessInfo.PROCESS_INFORMATION
  If CreateProcess_(@file$,@param$,0,0,0,#NORMAL_PRIORITY_CLASS,0,@dir$,@Info,@ProcessInfo)
    ThreadID=ProcessInfo\dwThreadID
  EndIf
  ProcedureReturn ThreadID
EndProcedure

app$="c:\windows\notepad.exe" ; Full path needed!
ThreadID=RunProgramEx(app$,"",GetPathPart(app$),#SW_HIDE)
Repeat
EnumThreadWindows_(ThreadId, @EnumProc(), @hWnd.l)
Until ShowWindow_(hWnd,#SW_SHOW)
Debug hWnd
Debug SendMessage_(hWnd,#WM_CLOSE,0,0) ; Now close it.
PB
PureBasic Expert
PureBasic Expert
Posts: 7581
Joined: Fri Apr 25, 2003 5:24 pm

Re: Window handle from process handle?

Post by PB »

Can somebody else confirm if my code works for them? Because I'd like to
know if it's indeed reliable or just a problem at Mistrel's end.
omit59
User
User
Posts: 63
Joined: Fri Mar 11, 2005 8:41 am
Location: Finland

Post by omit59 »

@PB,

works here ok, Windows XP, SP2

Timo
PurePWNRER
User
User
Posts: 45
Joined: Mon Mar 27, 2006 5:44 am

Post by PurePWNRER »

I'd say Karma kicked in when he saw the wrapper this guy is writing :wink:
:/ My kingdom for a snippet!
I might bash, But I provide.
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Post by Mistrel »

Here is another slight variation.

Code: Select all

Procedure EnumProc(hwnd, param)
  *handle.LONG = param
  *handle\l = hwnd
  ProcedureReturn 0
EndProcedure

Procedure RunProgramEx(file$,param$,dir$,showflag,*lpProcessInformation.PROCESS_INFORMATION)
  If Left(param$,1)<>" " : param$=" "+param$ : EndIf
  Info.STARTUPINFO : Info\cb=SizeOf(STARTUPINFO) : Info\dwFlags=1
  Info\wShowWindow=showflag
  If Not CreateProcess_(@file$,@param$,0,0,0,#NORMAL_PRIORITY_CLASS,0,@dir$,@Info,*lpProcessInformation)
    ProcedureReturn 0
  EndIf
  ProcedureReturn 1
EndProcedure

app$="c:\windows\notepad.exe" ; Full path needed!
If Not RunProgramEx(app$,"",GetPathPart(app$),#SW_HIDE,@ProcessInfo.PROCESS_INFORMATION)
	MessageRequester("Error","Failed to load process.")
	End
EndIf
Repeat
	EnumThreadWindows_(ProcessInfo\dwThreadID, @EnumProc(), @hWnd.l)
Until ShowWindow_(hWnd,#SW_SHOW)
Debug SendMessage_(hWnd,#WM_CLOSE,0,0) ; Now close it.
User avatar
kryptonn
User
User
Posts: 47
Joined: Wed Apr 18, 2007 7:23 pm

Re: Window handle from process handle?

Post by kryptonn »

PB wrote:Can somebody else confirm if my code works for them?
Very good works on my Win Xp.
User avatar
blueb
Addict
Addict
Posts: 1041
Joined: Sat Apr 26, 2003 2:15 pm
Location: Cuernavaca, Mexico

Post by blueb »

PB wrote:
Can somebody else confirm if my code works for them?
It works... but not every time. It appears as if the PostMessage command loses the hWnd handle and does nothing. I tried to increase the time to 4 seconds and that made no difference.


Latest XP Pro on a Dual Core 6600 :?

--blueb
- It was too lonely at the top.

System : PB 6.10 Beta 9 (x64) and Win Pro 11 (x64)
Hardware: AMD Ryzen 9 5900X w/64 gigs Ram, AMD RX 6950 XT Graphics w/16gigs Mem
Alquerian
New User
New User
Posts: 2
Joined: Fri Aug 10, 2007 6:22 pm
Location: Northern California

Post by Alquerian »

Can somebody else confirm if my code works for them?
It worked fine here as well.

I am running XP SP2
AMD Athlon 64 3000+

I think Mistrel is running a Core2 Duo 4350 (or something like that), I am not sure if the dual core has any impact on it.
SFSxOI
Addict
Addict
Posts: 2970
Joined: Sat Dec 31, 2005 5:24 pm
Location: Where ya would never look.....

Post by SFSxOI »

Something like this maybe? A few older things I collected from the forum a while back, don't remember who did what but the credit goes to them. Might need to be updated a little for PB 4.10. Uses some API but you dont need the path or anything and have always been reliable for me.

Code: Select all

; will return the correct hWnd for a PID if the PiD only runs a single thread.
; for apps that run more then one thread will return the handle from the first thread belonging to the PiD
; which may not be the handle you want.

Procedure InstanceToWnd(target_pid.l)
;Find the first window
test_hwnd = FindWindow_(0,0)
While test_hwnd <> 0
  ;Check If the window isn't a child
        If GetParent_(test_hwnd) = 0 ;Then
          ;Get the window's thread
            test_thread_id = GetWindowThreadProcessId_(test_hwnd, @test_pid)
              If test_pid = target_pid ;Then
                InstanceToWnd = test_hwnd
                Break ;Exit Do
              EndIf
        EndIf
        ;retrieve the Next window
        test_hwnd = GetWindow_(test_hwnd, #GW_HWNDNEXT)
Wend
ProcedureReturn test_hwnd
EndProcedure
or if you got a name, or even a partial name, of the file you can simply find the window handle by name?

Code: Select all

Procedure FindPartWin(part$)
  r=GetWindow_(GetDesktopWindow_(),#GW_CHILD) 
  Repeat 
    t$=Space(999) : GetWindowText_(r,t$,999)
    If FindString(t$,part$,1)<>0 And IsWindowVisible_(r)=#True
      w=r 
    Else 
      r=GetWindow_(r,#GW_HWNDNEXT) 
    EndIf 
  Until r=0 Or w<>0
  ProcedureReturn w 
EndProcedure

hWnd = FindPartWin("note") ; notepad used as example here, name is case sensitive i.e. "notepad" and not "Notepad"
Or...am i on the wrong track and the train already left the station?
Last edited by SFSxOI on Wed Dec 12, 2007 9:51 pm, edited 1 time in total.
Post Reply