Wednesday, April 6, 2011

How to find the IWebBrowser2 pointer for an IE8 window given a PID?

Hi,

so far, I've successfully used the following function to retrieve the IWebBrowser2 pointer to a running Internet Explorer instance, given it's PID.

static SHDocVw::IWebBrowser2Ptr findBrowserByPID( DWORD pid )
{
    SHDocVw::IShellWindowsPtr ptr;
    ptr.CreateInstance(__uuidof(SHDocVw::ShellWindows));
    if ( ptr == NULL ) {
        return 0;
    }

    // number of shell windows
    const long nCount = ptr->GetCount();

    // iterate over all shell windows
    for (long i = 0; i < nCount; ++i) {
        // get interface to item no i
        _variant_t va(i, VT_I4);
        IDispatchPtr spDisp = ptr->Item(va);

        SHDocVw::IWebBrowser2Ptr spBrowser(spDisp);
        if (spBrowser != NULL) {
            // if there's a document we know this is an IE object
            // rather than a Windows Explorer instance
            HWND browserWindow;
            try {
                browserWindow = (HWND)spBrowser->GetHWND();
            } catch ( const _com_error &e ) {
                // in case ->GetHWND() fails
                continue;
            }

            DWORD browserPID;
            GetWindowThreadProcessId( browserWindow, &browserPID );
            if ( browserPID == pid ) {
                return spBrowser;
            }
        }
    }
    return 0;
}

What I do is to launch an explorer.exe process via CreateProcess and then use the above function to retrieve the IWebBrowser2Ptr to it (so that I can fiddle with the browser).

Unfortunately, this doesn't seem to work with Internet Explorer 8 anymore, since IE8 seems to reuse processes - at least to some degree. For two code sequences like:

PROCESS_INFORMATION pi;
// ...

if ( CreateProcess( ..., &pi ) ) {
    // Wait a bit to give the browser a change to show its window
    // ...

    IWebBrowser2 *pWebBrowser = findBrowserByPID( pi.dwProcessId );
}

The first run of this code works fine, the second one never manages to retrieve the pWebBrowser window.

After a bit of debugging, it was revealed that the findBrowserByPID function does find lots of browser windows (and it finds more after starting a second browser instance), but none of them belong to the newly started process. It seems that all windows belong to the first IE process which was started.

Does anybody know an alternative way to get the IWebBrowser2 pointer to some IE8 instance? Or is there maybe a way to disable this apparent 'reuse' of processes with IE8?

From stackoverflow
  • If you're launching the IE Process yourself, don't use CreateProcess-- instead, use CoCreateInstance. That will return you an object for which you can query for IWebBrowser2, which you can use at will. The one complexity is that if the navigation crosses integrity levels (Vista+) the pointer becomes invalid. To address that problem, sync the NewProcess event, which will allow you to detect this condition.

    See some more info here: http://msdn.microsoft.com/en-us/library/aa752084%28VS.85%29.aspx

    Frerich Raabe : This used to be what I did until I noticed that CoCreateInstance didn't necessarily create a new process. I'm automating multiple IE windows to simulate multiple user logins to a web page - in order to avoid sharing login data and whatnot, it turned out to be necessary that I explicitely create a new process each time using CreateProcess.
    Frerich Raabe : Accepting this answer since I cannot accept the comment you wrote to my original question. :-) The term 'LCIE' was the key, I googled for it and found that you can disable this feature using a registry key. I acknowledge that this might not work in future versions, but at least it makes the above code work again, so now I have a workaround. Thanks!
    EricLaw -MSFT- : CreateProcess alone won't help unless LCIE is disabled, or you pass the -nomerge command line parameter. Which might be enough, even if you don't disable LCIE. :-)
  • A couple of alternative approaches might be:

0 comments:

Post a Comment