Page 1 of 2

Window handle from process handle?

Posted: Sun Dec 09, 2007 10:03 am
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

Re: Window handle from process handle?

Posted: Sun Dec 09, 2007 10:19 am
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.

Posted: Sun Dec 09, 2007 10:34 am
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.

Re: Window handle from process handle?

Posted: Sun Dec 09, 2007 10:49 am
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).

Posted: Sun Dec 09, 2007 10:51 am
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.

Posted: Sun Dec 09, 2007 3:06 pm
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. 

Posted: Mon Dec 10, 2007 9:10 am
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.

Re: Window handle from process handle?

Posted: Mon Dec 10, 2007 10:01 am
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.

Posted: Mon Dec 10, 2007 10:17 am
by omit59
@PB,

works here ok, Windows XP, SP2

Timo

Posted: Mon Dec 10, 2007 12:20 pm
by PurePWNRER
I'd say Karma kicked in when he saw the wrapper this guy is writing :wink:

Posted: Mon Dec 10, 2007 9:11 pm
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.

Re: Window handle from process handle?

Posted: Tue Dec 11, 2007 8:14 am
by kryptonn
PB wrote:Can somebody else confirm if my code works for them?
Very good works on my Win Xp.

Posted: Tue Dec 11, 2007 4:02 pm
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

Posted: Tue Dec 11, 2007 6:44 pm
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.

Posted: Wed Dec 12, 2007 1:08 am
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?