Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

set EXE on foreground using PID 1

Status
Not open for further replies.

Gerrit Broekhuis

Programmer
Aug 16, 2004
313
1
18
NL
Hi

If I know the PID of another .EXE that's running, how can I bring that .EXE to the foreground to make sure it's visible?

Regards, Gerrit
 
Gerrit,
You can bring an .EXE to the foreground by using the Win32 API's FindWindow() and SetForeGroundWindow() through Platform invoke. This will set the running .EXE application as the foreground window. The FindWindow() API will search for the window with the given PID and the SetForeGroundWindow() API will set the window as the foreground window.

Code:
* Declare the necessary API functions
DECLARE INTEGER FindWindow IN user32.dll STRING lpClassName, STRING lpWindowName
DECLARE INTEGER SetForegroundWindow IN user32.dll INTEGER hWnd

* Set the PID of the target .EXE application
targetPID = 1234  && Replace with the actual PID of the .EXE process

* Use the FindWindow API function to get the handle of the application's window
windowHandle = FindWindow(0, "")
DO WHILE windowHandle > 0
    IF GetWindowThreadProcessId(windowHandle, @lpdwProcessId) = 0
        * Error getting the process ID, handle it here
        EXIT
    ENDIF

    IF lpdwProcessId = targetPID
        * This is the window we're looking for, bring it to the foreground
        SetForegroundWindow(windowHandle)
        EXIT
    ENDIF

    * Get the next window handle
    windowHandle = GetWindow(windowHandle, GW_HWNDNEXT)
ENDDO


Best Regards,
Scott
MSc ISM, MIET, MASHRAE, CDCAP, CDCP, CDCS, CDCE, CTDC, CTIA, ATS, ATD

"I try to be nice, but sometimes my mouth doesn't cooperate.
 
Hi Scott,

I get an error: Variable 'LPDWPROCESSID' is not found
It seems something is missing in your code sample.

Regards, Gerrit
 
The declaration of GetWindowThreadProcessId is missing, also that of GetWindow.



Chriss
 
Here's something based on which lists all child windows of the Windows desktop.
I added the filtering by process ID and concentrated on the Hwnd, not titles, position and size, etc.:

Code:
#Define GW_HWNDLAST 1
#Define GW_HWNDNEXT 2
#Define GW_CHILD 5
Do Declare

Local lnProcessHwnd, lnPID

lnPID = 8120 && for my test, this was _vfp.processid of a second VFP IDE.
lnProcessHwnd = FindProcessWindow(lnPID)
SetForegroundWindow(lnProcessHwnd)

Function FindProcessWindow()
   Lparameters tnTargetPID

   Local lnHDesktop, lnHFirstChild, lnHLastChild, lnVisible, lnPID, lnReturnHwnd, lpdwProcessId 
   lnHDesktop = GetDesktopWindow()
   lnHFirstChild = GetWindow(lnHDesktop, GW_CHILD)
   lnHLastChild = GetWindow(lnHFirstChild, GW_HWNDLAST)
   lnHCurrent = lnHFirstChild
   lpdwProcessId = 0
   Do While .T.
      lnVisible = IsWindowVisible(lnHCurrent)
      If lnVisible=1
         GetWindowThreadProcessId(lnHCurrent, @lpdwProcessId)
         If lpdwProcessId = tnTargetPID
            lnReturnHwnd = lnHCurrent
            Exit
         Endif
      Endif
      If lnHCurrent = lnHLastChild
         lnReturnHwnd = -1
         Exit
      Endif
      lnHCurrent = GetWindow(lnHCurrent, GW_HWNDNEXT)
   Enddo

   Return lnReturnHwnd

Procedure Declare
   Declare Integer GetDesktopWindow In user32
   Declare Integer GetWindow In user32 Integer HWnd, Integer wFlag
   Declare Integer IsWindowVisible In user32 Integer HWnd
   Declare Integer GetWindowThreadProcessId In user32.Dll Integer HWnd, Integer @lpdwProcessId
   Declare Integer SetForegroundWindow In user32.Dll Integer HWnd

Notice: The overall code sample needs all DLL declaratons, the FindProcessWindow function needs all except SetForegroundWindow, because I designed the function to only return the first Hwnd it finds for the given processid, not to make it the foreground window. That#s done with the result hwnd. Feel free to integrate it, but I thought it's a good functionality usable not only for the goal of setting the foregroundwindow.

Also notice: It's based on the assumption that the first window you find in this enumeration of all desktop windows will be the major window handle for a process, the usual enumeration order goes from parent to child windows, and you start with the top parent window, the desktop itself.

I tested it with the process ID of a second VFP IDE and that worked fine, but that's no proof it always works. Besides quite recent threads that also had the topic of setting foreground windows just of the process itself by a known HWND. And that reveals timing problems, if you start several processes and set one to foreground, the next moment that can change again, especially at the start of Windows. That's another aspect needing caution and understanding about the situation you're in.

Chriss
 
Hi Chriss,

I got it working - at least in my test.prg - making a few changes:

Code:
	lnProcessHwnd = FindProcessWindow(lnPID)
	WAIT WINDOW "lnProcessHwnd = " + TRANSFORM(lnProcessHwnd) timeout(1)
	&& SetForegroundWindow(lnProcessHwnd)
	DECLARE INTEGER BringWindowToTop IN Win32API INTEGER lhWnd
	IF lnProcessHwnd#0
		BringWindowToTop(lnProcessHwnd)
	ENDIF

So far so good, I will now continue to try this in my project.
Thanks a lot!

Regards, Gerrit
 
Hi Chris,

Another update: it's working with my changes now in the project too.
At first it didn't do anything (nothing brought to foreground), but when I commented a few WAIT WINDOW it is now finally working.
I cannot explain this, so I activated a WAIT WINDOW again. And indeed it doesn't bring the window to the foreground.
At least I know what to do now.

Thanks again!

Regards, Gerrit
 
Gerrit Broekhuis said:
IF lnProcessHwnd#0
Notice if my function does not find a hwnd for the passed in process id I return -1, not 0. Well, maybe you changed that.

From the recent discussion I know SetForegroundWindow is actually the more relevant function for making it the foreground process at the same time of making it the foreground window. BringWindowToTop is of course, simply by name, an alternative. I don't know why it would work better for you, but may it be. Another reason to limit the function to only determine a Hwdn, to make use of it for different things.

I can imagine what happens when you do the wait window. It's not problematic that the wait window exists, but after the timeout of 1 second it will be released again, and that probably causes the parent VFP window to become foreground again, because the wait window is part of the VFP IDE. That also shows how shortlived this status of foreground can be and why it can change.

Chriss
 
Hi Chriss,

I used

Code:
	LOCAL lnProcessHwnd
	lnProcessHwnd = FindProcessWindow(lnPID)

I'm still confused about the WAIT WINDOW commands. After all the PID's don't change.

Regards, Gerrit
 
Well, in short, any window can play a role in the processs of the Windows OS maintaining a foreground process and window. And wait window is a window, too. Anytime a window is becoming shown, it also becomes foreground. My assumption now is that anytime a window closes, that means Windows also monitors such moments to possibly decide for a new top contender.

I wouldn't bother finding out why. As you know not using a wait window solves the problem, that's the solution. You don't really want to show a hwnD to an end user, do you? You just did that for testing purposes.

Chriss
 
Is the handle you get bei using

lnProcessHwnd = FindProcessWindow(lnPID)

a different "handle category" than the window handle you get by using FindWindow() ?

Thanks,
Manni

 
Thank you Chris, so it's a window handle. The reason why I ask is I don't understand the difference when you use the PID to find a window handle or if you find the same window handle by issuing FindWindow() using the window caption.

If it's the same handle in the end, why does SetForegroundWindow() not work in both cases?

Thanks,
Manni

 
You're perhaps overlooking that the information Gerrit has is the PID and not a title?

The goal is to get a HWnd when you know a PID, a process ID, as per question of Gerrit. That's not a windows title, caption or class name. FindWindow() doesn't offer that. So, no, you don't find a window by a process id directly with FindWindow(). There also isn't another Windows API function that does so directly. Since Scotts solution was incomplete I based mine on the news2news sample I referred to. If you think there's something simpler, feel free to add it. I don't see that you could directly get to a HWnd from a PID somehow and other solutions in C++ or C# you find also use either Enumwindows or other means to iterate all windows and GetWindowThreadProcessId to find out to which process a window hwnd belongs. For example
What you do with the Hwnd is up to you. I already explained why I thought such a function better returns the Hwnd than directly doing the BringWindowToTop or SetForegroundWindow. The next step could also be sending Windows messages to the Hwnd, for example.

Chriss
 
Hi Chriss,

no I understood that, but I thought the reason for using the PID instead of window handle to begin with was because for some reason using the window handle to put the window to the foreground wasn't working. Hope this makes sense.

Thanks,
Manni

 
Hi Manni,

I could not use the window title, as multiple instances of the same executable will have similar titles. Therefore I used the PID. I could list the PID’s and pick the ‘oldest’ one, being the first execution of the executable. Just what I needed for my purpose.

I use this to archive e-mails from Outlook into MySQL and I had to be sure no other mail was left unsaved to prevent database / content errors.

Regards, Gerrit
 
Manni,

well, if you have a hwnd you're at the goal of doing anything with the window as far as your privileges go, i.e. you can do more by being owner of a process.

Gerrit,

I don't know how you pick the oldest pid I just hope you don't pick the lowest, the PIDs are not ascending.
If you work with Outlook isn't the easiest way to automate it by using Outlook.Application?

Chriss
 
Hi Chriss,

I add all win32 processes into a cursor and sort on creationdate. I hope I use the right terms here, I don’t have my office computer with me right now.

I understand very well what you mean and I checked the outcome several times. I get 100% reliable results and checked with various .exe’s to be sure. For example you can open notepad and enter “1” in it’s window. Wait a few seconds, open another notepad and enter “2”. You can add more instances of notepad if you want. If you bring a notepad instance to the foreground using it’s PID, you will immediately see if you get the right notepad.

Regards, Gerrit
 
Okay, assuming you use WMi to get a list of win32_process classes, they have a creationdate which is a datetime.
That will work well to get the oldest process. I don't know why that's relevant for your Outlook mail backup, but you know best.

I just remembered Christoph Wollenhaupt has OLIndex.zip in his downloads: it does not look like it's what you need, as its goal is to buid a full text index on all mail items for fast searching. You can skip the part creatng such an index, the first part of the code is to get all mail items from outlook, exactly what you'd need to backup them. In this case they are all read into DBFs.


Chriss
 
Hi Chriss,

Thanks for the link to OLIndex. However it’s not relevant for my project that archives a single e-mail message into a MySQL database and saves the .msg file to disk. The message can then be deleted from Outlook. After all Outlook is not suitable for storing lot’s of mail.

Regards, Gerrit
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top