Welcome to MSDN Blogs Sign in | Join | Help

There can be more than one (or zero): Converting a process to a window

A common question I see is "How do I find the window that corresponds to an HINSTANCE?"

This question comes pre-loaded with the assumption that there is only one window that corresponds to an HINSTANCE, which is true for only the most rudimentary of Win32 programs. Even a simple program like Notepad has more than one window with the same HINSTANCE, as Spy quickly reveals.

But when I hear this question, I smell something suspicious. First, instance handles are meaningful only when you specify which process you're referring to, since an instance handle in Win32 is the base address of a module, and different processes can have different DLLs loaded at the same base address (thanks to separate address spaces). Second, I can't think of any normal scenarios where you'd even care about finding all the windows that have a particular instance handle, especially since the instance handle is ignored if the window belongs to a global class, and global classes are typically the only classes you are interested in anyway when you go snooping into another process. (After all, if it's not your process, you don't really know much of anything about its private classes.)

But you probably already know that the person asking the question is asking the wrong question. They almost certainly launched a program with the ShellExecute function and now want to locate its window so they can do, um, something with it. But you already know that you can't do anything meaningful with the HINSTANCE returned by the ShellExecute function aside from compare it against the number 32, since it's not really an instance handle on Win32. That the return value is of type HINSTANCE is just a carry-over from 16-bit Windows.

But the person who asked the question never got that far in reading the documentation and just said, "Well, I've got an HINSTANCE and I need a window, so can somebody help me convert this HINSTANCE to a window?" without understanding what's really going on. They need Y, but have X (a fake X, but still), so obviously the thing they need next is a way to convert your X to a Y.

Even if it doesn't make sense.

It's like asking for double-strength placebos.

Okay, so you get past the initial disconnect of asking for the wrong thing. Say you have a process ID or a thread ID and you want to find the window for that process or thread.

Again, there can be more than one, and there might be zero. If it's a process you're after, you can enumerate the windows on the desktop and use the GetWindowThreadProcessId function to determine what thread and process they belong to. If you are interested in the windows that belong to a thread, you can use the EnumThreadWindows function. Your next task is deciding which of those windows is the one you want.

When you try to explain this to people, you sometimes get stuck in the Why won't you answer my question? cycle.

"I have a thread ID. How do I get the corresponding window?"

You can use the EnumThreadWindows function to get all the windows on the thread.

"Yes, I know about EnumThreadWindows, but how do I get the window that I want?"

Well, you haven't said what you wanted yet.

"I want the window that corresponds to the thread."

But which one? How will you decide among all the windows?

"That's what I'm asking you!"

But you haven't yet described what you want.

"I want the window corresponding to the thread. Why won't you answer my question?"

Note that saying, "I am looking for the top-level unowned window" is a step forward, but it still doesn't uniquely identify a window. There can be multiple top-level unowned windows in a process. For example, Explorer typically has lots of top-level unowned windows. There's the desktop, the taskbar, your open folder windows, and property sheets. If you ask for "the" top-level unowned window of Explorer, which one do you want?

Perhaps people are getting the idea that there is a way to uniquely specify "the" window for a process because the System.Diagnostics.Process object has a property called MainWindowHandle. The documentation for that property doesn't do anything to dispel the notion, either. I have no idea how that property decides among multiple top-level unowned windows.

Published Wednesday, February 20, 2008 7:00 AM by oldnewthing
Filed under:

Comments

# re: There can be more than one (or zero): Converting a process to a window

Wednesday, February 20, 2008 10:29 AM by ERock

I never knew Notepad had a status bar. :)

# re: There can be more than one (or zero): Converting a process to a window

Wednesday, February 20, 2008 10:33 AM by Triangle

"I have a thread ID. How do I get the corresponding window?"

You can use the EnumThreadWindows function to get all the windows on the thread.

"Yes, I know about EnumThreadWindows, but how do I get the window that I want?"

It'll be the first window returned from EnumThreadWindows.

;)

# System.Diagnostics.Process.MainWindowHandle

Wednesday, February 20, 2008 10:45 AM by Anton Tykhyy

Taking a look at the actual code behind the MainWindowHandle property (through Reflector) reveals that the first time you read this property, it returns the first top-level unowned visible window matching the process' id, as enumerated by the EnumWindows function. This rings the alarm bells already, because the enumeration order is unspecified. If no window satisfies all these criteria, the property's value is 0. And now for the funniest part: subsequent reads of the MainWindowHandle property yield the same value which was returned on the first call, even if it is no longer a valid window handle or the actual main window has changed in the meantime.  System.Diagnostics.Process.Refresh() resets the "have main window" flag, so you can call it before every read of the MainWindowHandle property to have it repeat the search, but this does not solve the more-than-one-"main window" problem and anyway there is a race between using the value of MainWindowHandle and the destruction/change of the "main window".

All of this makes the MainWindowHandle property obviously dangerous except for the simplest target processes, but I suppose it's better to make all .NET programs share the same main-window-finding bug than force each program to write its own main window finder, introducing an unknown number of different bugs.

# correction

Wednesday, February 20, 2008 10:58 AM by Anton Tykhyy

A correction — the race I mentioned is, of course, not a .NET artifact but is inherent in the Win32 API. Of course, back in the days of 16-bit Windows this race did not exist since your thread could not be pre-empted.

# re: There can be more than one (or zero): Converting a process to a window

Wednesday, February 20, 2008 11:04 AM by Dan

Anton: The bug you described is actually desired behavior.  Querying processes, especially if done repeatedly, can cause your program to eat up lots of CPU time.  So the Process object caches its results until you explicitly tell it to reload with .Refresh.  The "problem" you mention occurs with all properties on Process, not just MainWindowHandle.  If you create a Process object, and then end the process it points to, you will still be able to access all the projecties in the Process object even though the process no longer exists (IIRC).  Only when you call Refresh will the Process object realize the process has ended.

# re: There can be more than one (or zero): Converting a process to a window

Wednesday, February 20, 2008 11:12 AM by Christopher Walken

Raymond,

Who is asking you these questions? From time to time it seems you receive questions from people that seem pretty low in the food chain.

Thanks,

Chris

# re: There can be more than one (or zero): Converting a process to a window

Wednesday, February 20, 2008 11:16 AM by Anton Tykhyy

Dan: This is very true. I understand the need for caching, but the user of MainWindowHandle still needs to understand the issues involved, including this caching behaviour. MSDN's page on MainWindowHandle does not mention these problems explicitly and the page on Refresh() is somewhat vague, because at least the "main window" handle is recalculated when it is queried and not by the Refresh() call, as the documentation seems to imply.

Also I'd like to correct "bug" to "particular behaviour" in my original post to avoid further discussion of whether the described behaviour is actually a bug.

# re: There can be more than one (or zero): Converting a process to a window

Wednesday, February 20, 2008 12:54 PM by Jim Lyon

Sounds like Raymond is stuck in an infinite loop of:

 Q: (dumb question).

 A: Your question is malformed. Try again.

Depending on circumstances, I find one of the following responses to be more effective:

 A: Your question is malformed. Go away until you get a clue.

or

 A: Your question is malformed. Tell me what you're really trying to do so that I can help you reach a resolution.

# re: There can be more than one (or zero): Converting a process to a window

Wednesday, February 20, 2008 1:23 PM by Ben Cooke

This question falls in to a category of questions I also encounter quite often.

Imagine that someone is driving to the Microsoft campus in Redmond and several miles out they come to a fork in the road. The left road goes directly to Microsoft HQ, while the right road is unnavigable after a mile due to a missing bridge over a huge river.

Sane people would do one of two things:

* Pick a road, and if it turns out to be the wrong road drive back and take the other one.

* Stop at the fork and phone up Raymond and ask him which road to choose. (He doesn't know, but he can guess!)

The sort of person who asks the question Raymond's discussing today would take the wrong road and then after a mile call up Raymond and ask him "How do you convert a car into a boat?"

# re: There can be more than one (or zero): Converting a process to a window

Wednesday, February 20, 2008 1:43 PM by Ray Trent

Interestingly, there *are* different strengths of placebo you can order (so the doctor has something to give when the patient says "this isn't working well enough", believe it or not). Look up "Cebocap" here: http://www.walgreens.com/library/finddrug/druginfo.jsp?particularDrug=Cebocap.

Anyway, the MainWindowHandle is documented to be the *first* window created by the process, which implies that probably EnumThreadWindows' order may end up being frozen for unintentional backwards compatibility reasons :-).

# re: There can be more than one (or zero): Converting a process to a window

Wednesday, February 20, 2008 2:14 PM by Anton Tykhyy

Ray: MainWindowHandle doesn't use EnumThreadWindows unless EnumWindows uses it internally, see above. And the actual code in System.dll does not support the statement that MainWindowHandle returns the first window created by the process. The simplest counterexample is if the first window the process creates is always invisible (e.g. a message-target window). And the documentation for MainWindowHandle is misleading to say the least:

--

The main window is the window that is created when the process is started. After initialization, other windows may be opened, including the Modal and TopLevel windows, but the first window associated with the process remains the main window.

--

In conjunction with Reflector, this paragraph does make one scratch one's head.

# Excuse me but...

Wednesday, February 20, 2008 5:14 PM by Igor Levicki

Isn't the main window the one whose message loop is run by the first (and thus the main) thread of a newly created process?

# re: There can be more than one (or zero): Converting a process to a window

Wednesday, February 20, 2008 5:30 PM by SuperKoko

"

Isn't the main window the one whose message loop is run by the first (and thus the main) thread of a newly created process?

"

There are typically several windows owned by the same thread.

BTW: A window isn't bound to "a message loop" (whatever this means).

# "How do I find the window that corresponds to an HANDLE?"

Wednesday, February 20, 2008 6:15 PM by B&D-OS-fan

Successful mockery, grats.

Now, I'd like to rephrase the question those poor people, who innocently didn't know how annoying lack of sensible and useful (or even, gasp, well-organized?) documentation can be really, meant to ask:

"The child process is safeguarded against multiple execution and has exactly *one* window.

I used ShellExecute**EX** and presumably got a HANDLE hProcess. How do I go from this HANDLE to the ProcessID, so I can match it against all the open windows?"

I can surely OpenProcess all the ProcessIDs across all Windows, but that seems wasteful and noisy and I'll still have to somehow match the process behind OpenProcess with the one behind ShellExecuteEx->ShellExecuteInfo.hProcess, unless of course OpenProcess always yields the same HANDLE for the same ProcessID, which again I cannot believe given there's DuplicateHandle.

# re: There can be more than one (or zero): Converting a process to a window

Wednesday, February 20, 2008 7:21 PM by Miral

"I used ShellExecute**EX** and presumably got a HANDLE hProcess. How do I go from this HANDLE to the ProcessID, so I can match it against all the open windows?"

What, you mean like GetProcessId?

# re: There can be more than one (or zero): Converting a process to a window

Wednesday, February 20, 2008 10:50 PM by Igor Levicki

"What, you mean like GetProcessId?"

Pwned :)

# re: There can be more than one (or zero): Converting a process to a window

Wednesday, February 20, 2008 10:53 PM by Igor Levicki

>>BTW: A window isn't bound to "a message loop" (whatever this means).<<

You mean that poor window doesn't know about his window procedure which is running his message loop?

# re: There can be more than one (or zero): Converting a process to a window

Wednesday, February 20, 2008 11:08 PM by Lucas

As for  the System.Diagnostics.Process.MainWindowHandle property, the documentation yes:

"The main window is the window that is created when the process is started. After initialization, other windows may be opened, including the Modal and TopLevel windows, but the *first* window associated with the process remains the main window."

# re: There can be more than one (or zero): Converting a process to a window

Thursday, February 21, 2008 3:27 AM by Medinoc

So, if the process displays a splash screen before opening its main window, the property becomes useless ?

I think the first *overlapped* window would be a better choice...

And as for GetProcessId(), it's a shame it requires Windows XP SP1 or superior. How could the Win2000 users do ?

# re: There can be more than one (or zero): Converting a process to a window

Thursday, February 21, 2008 3:48 AM by Anton Tykhyy

Lucas: I quoted this same paragraph above, but that's not what the MainWindowHandle code does.

Medinoc: yes, a splash screen is a good example.

# re: There can be more than one (or zero): Converting a process to a window

Thursday, February 21, 2008 4:13 AM by Alex Cohn

in MFC, you can ask for AfxGetApp()->m_pMainWnd

# re: There can be more than one (or zero): Converting a process to a window

Thursday, February 21, 2008 4:50 AM by SuperKoko

"You mean that poor window doesn't know about his window procedure which is running his message loop?"

The window proc doesn't run the message loop.

The message loop runs the window proc.

A window is bound to a message queue so that messages sent to the window are queued on the message queue.

Every thread has a message queue which all its windows are bound to.

A message loop is a piece of code that gets message from the queue of a thread, dispatch them to the window procedures and loop.

Messages don't need to be dispatched in message loops, but typically are.

For example, the MessageBox and DialogBox functions include a message loop.

So, when you call MessageBox, all the windows of your thread gets their messages handled by the message loop of MessageBox.

If you call MessageBox while you're handling a message in a window procedure, there will be TWO message loops running at once.

# System.Diagnostics.Process

Thursday, February 21, 2008 7:30 AM by Matt

Using the excellent Reflector program to poke around in .NET, it would seem that the MainWindowHandle property of System.Diagnostics.Process called EnumThreadWindows, then uses the following logic to determine which of the values returned is the 'correct' one:

private bool IsMainWindow(IntPtr handle)

{

   return (!(NativeMethods.GetWindow(new HandleRef(this, handle), 4) != IntPtr.Zero) && NativeMethods.IsWindowVisible(new HandleRef(this, handle)));

}

Which looks to me like the first non-zero, visible window handle.

# Correction

Thursday, February 21, 2008 7:33 AM by Matt

Actually, the first visible window which has GW_OWNER set.

# re: There can be more than one (or zero): Converting a process to a window

Thursday, February 21, 2008 8:07 AM by Ryan

@Alex: I forget my MFC but isn't m_pMainWnd set by an application at runtime?

# re: There can be more than one (or zero): Converting a process to a window

Thursday, February 21, 2008 8:07 AM by zorba the geek

"in MFC, you can ask for AfxGetApp()->m_pMainWnd"

In MFC, you need to initialize m_pMainWnd yourself in InitInstance.

# re: There can be more than one (or zero): Converting a process to a window

Thursday, February 21, 2008 9:22 AM by Anton Tykhyy

Geh. Matt, did you read the comments before posting? MainWindowHandle uses EnumWindows, not EnumThreadWindows. And that's not a non-zero-handle window, but an unowned window.

# "How do I find the window..."

Thursday, February 21, 2008 9:45 AM by B&D-OS-fan

> And as for GetProcessId(), it's a shame it requires Windows XP SP1 or superior.

/signed. (Didn't find it in my MSVC6.0 doc therefore.)

> How could the Win2000 users do?

Usually by "an alternative means", stupid stuff like having the other process write its Window handle somewhere, or giving it a Window handle to send its own Windows handle to.

Unfortunately, this works not at all if the target process is not in source available...

Anyways, I guess I'm out of luck as this feature only took about a decade to manifest, woohoo! Now, the function "GetHandleInfo(hSpecial, &HandleType, &HandleDataSize, &HandleData)" should be ready right after easter 2017, I presume...

# re: There can be more than one (or zero): Converting a process to a window

Thursday, February 21, 2008 12:29 PM by SuperKoko

"Usually by "an alternative means", stupid stuff like having the other process write its Window handle somewhere."

Much simplier: Using a specific window class name so that the other process can use FindWindow.

"

Unfortunately, this works not at all if the target process is not in source available...

"

FindWindow might "work" even if the target process source code isn't available.

Of course, it's a ugly hack that may fail with any new release of the software.

But, for personal use, that might make sense, sometimes.

And, of course, it's not a generic solution. It must be specially coded for a specific application.

Anyway, I think that a "generic" MainWindowHandle function doesn't make sense.

# re: There can be more than one (or zero): Converting a process to a window

Thursday, February 21, 2008 1:06 PM by Yuhong Bao

>DLL Hell .Net is worse than DLL Hell Classic.

Well, you see, it is like Plug and Play (PnP).

Wonderful when it works, a pain to fix if it doesn't. That is why people have called it "Plug and Pray". BTW, how does that sound as an analogy, Raymond?

# re: There can be more than one (or zero): Converting a process to a window

Thursday, February 21, 2008 2:59 PM by ???

@Yuhong Bao

What the heck are you talking about?

Are you even discussing this topic?

# re: There can be more than one (or zero): Converting a process to a window

Thursday, February 21, 2008 3:10 PM by Yuhong Bao

I was talking about one of the comments in this:

http://blogs.msdn.com/oldnewthing/archive/2007/04/12/2093090.aspx

[In other words, "No, I'm not even talking about the topic." You have a pattern of going off topic. That's one of the reasons why I delete most of your comments. -Raymond]

# re: There can be more than one (or zero): Converting a process to a window

Thursday, February 21, 2008 8:55 PM by name re-choired

Medinoc wrote: "How could the Win2000 users do ?"

Pretty sure you can:

  #include <process.h>

and then:

  int pid = _getpid();

# re: There can be more than one (or zero): Converting a process to a window

Friday, February 22, 2008 8:57 AM by SuperKoko

@name re-choired:

How does that help?

We don't want to get the process id of the current process, but of a process for which we've got a handle.

GetProcessId != GetCurrentProcessId.

# re: There can be more than one (or zero): Converting a process to a window

Friday, February 22, 2008 11:53 AM by KenW

Yuhong Bao has obviously adopted this strategy to get around the "Post is locked and accepts no new comments" issue. Bad Yuhong Bao! Please stop.

# re: There can be more than one (or zero): Converting a process to a window

Friday, February 22, 2008 5:01 PM by Ian Boyd

The window that has neither a parent or owner.

# re: There can be more than one (or zero): Converting a process to a window

Tuesday, February 26, 2008 5:00 PM by BryanK

Ian Boyd: No, you need to re-read the last part of the article.  Specifically:

> There can be multiple top-level unowned windows in a process.

Since these windows are top-level, they have no parent.  Since they're unowned, they have no owner.  That matches your criteria, but there can be more than one of these windows in a process (Raymond's example: Explorer).  So your method of mapping from a process to a window is still flawed.

(Hint: Your process can call CreateWindow as many times as you want [well, within reason, but certainly more than once], with whichever parameters you want.  There is therefore *no way* to go from a process to a (single) window that's always going to work.  Any check you propose can be broken by certain sequences of calls to CreateWindow.)

New Comments to this post are disabled
 
Page view tracker