Jump to content

How to get Process Handle from PID, OR How to make _WinAPI_WaitForInputIdle() Work?


Recommended Posts

I am using Run("some application.exe") and getting a PID,

and then I need to get a Process Handle from that PID, so I can use some APIs that require a ProcessHandle...

Edited by Zohar
Link to comment
Share on other sites

look at _WinAPI_OpenProcess

*GERMAN* [note: you are not allowed to remove author / modified info from my UDFs]My UDFs:[_SetImageBinaryToCtrl] [_TaskDialog] [AutoItObject] [Animated GIF (GDI+)] [ClipPut for Image] [FreeImage] [GDI32 UDFs] [GDIPlus Progressbar] [Hotkey-Selector] [Multiline Inputbox] [MySQL without ODBC] [RichEdit UDFs] [SpeechAPI Example] [WinHTTP]UDFs included in AutoIt: FTP_Ex (as FTPEx), _WinAPI_SetLayeredWindowAttributes

Link to comment
Share on other sites

oh..

What I wanted to do, is to use _WinAPI_WaitForInputIdle() in order to know when an Application that I Run(), has finished loading.

But if it requires OpenProcess, CloseProcess, and all that mess, then maybe I'll skip it :]

Too bad AutoIt doesn't have function that gives a similar functionality to _WinAPI_WaitForInputIdle().

Edited by Zohar
Link to comment
Share on other sites

As ProgAndy said, use _WinAPI_OpenProcess to get a handle to the process, then use DllCall to call the WaitForInputIdle function inside user32.dll. It's quite easy :D

Nvm. that.

there's _WinAPI_WaitForInputIdle

Edited by FreeFry
Link to comment
Share on other sites

really?

OK, here's my attempt:

#Include <WinAPI.au3>


Local   Const   $PROCESS_ALL_ACCESS =0x1F0FFF


Local   $PID    =Run("SOME SLOW LOADING APPLICATION")
Local   $Process_Handle =_WinAPI_OpenProcess($PROCESS_ALL_ACCESS,1,$PID)
_WinAPI_WaitForInputIdle($Process_Handle)
Beep(400,100)

Unfortunately it's not waiting.

The beep is played immediately after the application is launched, and not after it finishes loading itself :/

(as if the _WinAPI_WaitForInputIdle($Process_Handle) line is not there at all)

Anyone has done that before?

Edited by Zohar
Link to comment
Share on other sites

Use The _WinAPI_CreateProcess function before calling _WinAPI_WaitForInputIdle. You'll have to give up using Run doing so.

but why?

OpenProcess() exists so it will be used.

There should be a way to do it using Run()+OpenProcess().

Link to comment
Share on other sites

well

since it has been many times that you told people "it's impossible", and it turned out that "it is possible",

then allow me to say thanks but no:)

If anyone here has an idea, please write.

If I need to replace OpenProcess() with something else, it's cool,

As long as the Run() can stay.

basically, it means that I need a different way to get a ProcessHandle from a PID, than what I currently have.

Edited by Zohar
Link to comment
Share on other sites

Try this and tell me what you think:

#include <WinAPI.au3>

Dim $sProcess = 'C:\Program Files\Windows Media Player\wmplayer.exe'
Dim $hProcess
Dim $tPI = DllStructCreate($tagPROCESS_INFORMATION), $pPI = DllStructGetPtr($tPI)
Dim $tSI = DllStructCreate($tagSTARTUPINFO), $pSI = DllStructGetPtr($tSI)
    DllStructSetData($tSI, 'Size', DllStructGetSize($tSI))

Dim $iSuccess = _WinAPI_CreateProcess('', $sProcess, 0, 0, False, 0, 0, 0, $pSI, $pPI)

If Not $iSuccess Then
    MsgBox(0x10, 'Error!', 'Could not create the process!!!')
Else
    _WinAPI_CloseHandle(DllStructGetData($tPI, 'hThread'))
    $hProcess = DllStructGetData($tPI, 'hProcess')
    
    _WinAPI_WaitForInputIdle($hProcess)
    MsgBox(0x40, 'Success!', 'process initialization has completed!!!')
EndIf

$tPI = 0
$tSI = 0

Hope you got the wmplayer.exe ;]

P.S. I'm not trying to get people down. Believe me.

Link to comment
Share on other sites

Authenticity:

1)

I know you're not trying to get people down,

but you must know that your way of thinking which is "I don't know, then it's impossible" is basically wrong.

(you do that often, and it's not good.. If you don't know, someone else might know,

and when yous upply a "it's impossible" answer, the person who asks might give up)

2)

Specifically here:

Thank you again for trying to help me, but as I said this(CreateProcess()) is not the direction I wish to go.

I am looking for a way that will work, without using CreateProcess(),

a way that will get a PID,

and will be able to Wait for the Process to be Idle.

3)

Unfortunately, the code you write here, with CreateProcess() is not working.

It immediately shows the messagebox, which is the same problem that my original code that uses Run() suffers from.

Edited by Zohar
Link to comment
Share on other sites

Try this(I'm not 100% sure if it actually works in the way you need it to):

#include <WinAPI.au3>
#include <Constants.au3>

Dim $iPID = Run("calc.exe")
Dim $hProcess = _WinAPI_OpenProcess($PROCESS_ALL_ACCESS, 0, $iPID, False)

_WinAPI_WaitForInputIdle($hProcess, 5500)

MsgBox(0, "Ready", "Process is now ready for use. :)")
Link to comment
Share on other sites

Hello FreeFry :D

Thank you very much for your help.

I tested it, and it too, is not waiting till the process has finished loading..

Is it just me?

Or maybe my expectation from WaitForInputIdle() is incorrect?

What I expect from WaitForInputIdle(), is to wait until the program has finished loading,

that means, it has gone past the part that consumes the most CPU in the beginning, it has created its window, etc.

Simply put, "It's ready".

Isn't that what WaitForInputIdle() should do?

And if yes, why all 3 attempts(mine, authenticity's, and freefry's) do not achieve it?

Link to comment
Share on other sites

I would say it works as meant.

Thing is _WinAPI_WaitForInputIdle is written so it returns true for original 0 and false for anything else. This why, for example you cannot see that WAIT_TIMEOUT is returned for some situations.

#include <WinAPI.au3>
#include <Constants.au3>

Global $iPID = Run('"' & @ProgramFilesDir & '\Internet Explorer\iexplore.exe"')
Global $hProcess = _WinAPI_OpenProcess($PROCESS_ALL_ACCESS, 0, $iPID, False)

Global $a = TimerInit()

Global $aCall = DllCall("user32.dll", "dword", "WaitForInputIdle", "hwnd", $hProcess, "dword", 30)

; RETURN VALUES ARE:
;     All_OK = 0
;     WAIT_TIMEOUT = 258
;     WAIT_FAILED = -1

ConsoleWrite("Return value: " & $aCall[0] & @CRLF & "Elapsed time: " & TimerDiff($a) & " ms" & @CRLF)

Likely your expectations are incorrect.

p.s.

Authenticity's code is nice.

Link to comment
Share on other sites

Hello trancexx

mmm

when I say "it's working/not working", I am not talking about the Return Value, I am talking about the Behavior that results from the Wait method.

So I think I did not fully understand what you mean.

If the return value satisfies you, but you did not see any wait, then where would you use this function(WaitForInputIdle())?

Edited by Zohar
Link to comment
Share on other sites

Try calling twice for _WinAPI_WaitForInputIdle(), worked on Microsoft Visual C++ while loading previous opened project, using one call returns as fast as the environment is ready and another call returns when the project is fully loaded. If you can explain the load procedure it may help.

Edit: Or if the mouse cursor is changed while the process is loaded you can do like this:

_WinAPI_WaitForInputIdle($hProc)
Do
    Sleep(500)
Until MouseGetCursor() <> 15
Edited by Authenticity
Link to comment
Share on other sites

Hi

I tried calling twice, and unfortunately I did not succeed.

I tried adding a Sleep(1000) between the two Wait calls, and did not succeed..

I even changed the sleep to 4000(the total time for the application+datafile is 15secs), and that too did not succeed.

Is that WaitForInputIdle() function supposed to work? :D

maybe the application I try it on is not "good"?

Regarding the MouseCursor,

I must say this is definitely a clever and creative idea.

It works good for applications where the application first shows the window, and only then loads the data file.

But, there are many applications that do not display themselves until "everything is ready" and loaded, and there the MouseCursor won't give us a clue.

I must admit it's quite disappointing that WaitForInputIdle() is not performing as the developer needs..

Link to comment
Share on other sites

Well, use AutoIt Window Info or WinSpy and get a class name that can help you to uniquely identify the window and loop until the window exists and visible and then try the aforementioned.

Something like this:

#include <Constants.au3>
#include <WinAPI.au3>
#include <WindowsConstants.au3>

Dim $hWin

While 1
    $hWin = WinGetHandle('[CLASS:Unique]')
    If $hWin And BitAND(_WinAPI_GetWindowLong($hWin, $GWL_STYLE), $WS_VISIBLE) Then ExitLoop
    Sleep(500)
WEnd
Link to comment
Share on other sites

yeah I use those workarounds. thanks.

note that you can use WinGetState() to check if it's visible.

Altho, that will not tell when the window has finished loading, coz sometimes the window has a listview that loads items.

There's a trick for that too: we can check if the ListView(or whatever control) exists, and check the Number of Items it has.

That's what I do now, when I need it to wait for a window.

But I thought to make the code better If the WaitForInputIdle() function had worked..

I wonder if anyone managed to make it work nicely

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...