본문 바로가기
개발

Windos Vista에서 IE7 실행 및 IWebBrowser2 인터페이스 얻기

by 솜씨제이 2007. 2. 23.

Windows Vista에서 IWebBrowser2를 CoCreateInstance로 생성하는 경우 몇가지 제약 사항들이 있다. 먼저  Internet Explorer을 Visible하게 하기 전에 먼저 Navigate를 실행해야 한다. 다음으로 Navigate하고자 하는 URL이 Trusted Domain이 아니면 실행하는 어플리케이션이 관리자 권한으로 실행 중이거나 보호 모드가 설정된 IE7을 실행해야 한다. 그렇지 않으면 IE7은 보호 모드가 설정된 IE 프로세스를 다시 생성하고 기존 프로세스는 종료된다. 문제는 CoCreateInstance로는 보호 모드가 설정된 IE 프로세스를 생성할 수가 없다는 점이다.

이를 해결하기 위해서는 보호 모드가 설정된 IE7에 대한 IWebBrowser2 인터페이스를 생성할 수 있는 방법을 찾아야 하는데 아직까지 Windows Vista에서 그런 API를 제공하지는 않는 듯 하다. 따라서 임시 방편 같지만 아래와 같은 형태로 보호 모드가 설정된 IWebBrowser2를 얻을 수 밖에 없다.

먼저 Vista에 새로 추가된 API인 IELaunchURL을 이용해서 보호 모드가 설정된 IE7을 실행하고 파라미터로 받은 PROCSSS_INFORMATION을 이용해서 해당 프로세스에서 동작 중인 IWebBrowser2의 인터페이스를 가져오는 형태다.

typedef struct _IELAUNCHURLINFO
{
    DWORD cbSize;
    DWORD dwCreationFlags;
} IELAUNCHURLINFO, *LPIELAUNCHURLINFO;

typedef BOOL (WINAPI *PFN_IELaunchURL)(LPCWSTR szUrl, LPPROCESS_INFORMATION pProcInfo, LPIELAUNCHURLINFO lpInfo);

BOOL CALLBACK EnumChildWinProc(HWND hWnd, LPARAM lParam)
{
    TCHAR  szClassName[256];

    GetClassName(hWnd, (LPTSTR)&szClassName, 256);

    if (_tcscmp(szClassName, _T("Internet Explorer_Server")) == 0)
    {
        // 진짜 IE7의 Internet Explorer_Server가 맞나?
        HWND hParent = GetParent(hWnd);
        if (hParent)
        {
            hParent = GetParent(hParent);
            if (hParent)
            {
                GetClassName(hParent, (LPTSTR)&szClassName, 256);
                if (_tcscmp(szClassName, _T("TabWindowClass")) == 0)
                {
                    *(HWND*)lParam = hWnd;
                    return FALSE;
                }
            }
        }
    }
 
    return TRUE;
}

BOOL CALLBACK EnumWinProc(HWND hWnd, LPARAM lParam)
{
    TCHAR  szClassName[256];

    GetClassName(hWnd, (LPTSTR)&szClassName, 256);

    if (_tcscmp(szClassName, _T("IEFrame")) == 0)
    {
        *(HWND*)lParam = hWnd;
        return FALSE;
    }

    return TRUE;
}

bool LaunchPopupBrowser(LPCWSTR szUrl)
{
    HMODULE hModule = LoadLibrary(_T("ieframe.dll"));

    if (!hModule)
        return false;

    PFN_IELaunchURL pfnIELaunchURL = (PFN_IELaunchURL)GetProcAddress(hModule, _T("IELaunchURL"));

    if (!pfnIELaunchURL)
    {
        FreeLibrary(hModule);
        return false;
    }

    PROCESS_INFORMATION pProcInfo;
    HRESULT hr = (pfnIELaunchURL)(szUrl, &pProcInfo, NULL);
    if (FAILED(hr))
    {
        FreeLibrary(hModule);
        return false;
    }

    // 프로세스가 초기화되고 IE창이 뜰 때까지 기다림
    HWND hWndIEFrame = NULL;
    HWND hWnd = NULL;
    int nWait = 100;
    while ((hWnd == NULL) && (nWait > 0))
    {
        nWait--;
        Sleep(1000);
        WaitForInputIdle(pProcInfo.hProcess, 30000);
        if (hWndIEFrame == NULL)
            EnumThreadWindows(pProcInfo.dwThreadId, &EnumWinProc, (LPARAM)&hWndIEFrame);
        if (hWndIEFrame)
            EnumChildWindows(hWndIEFrame, &EnumChildWinProc, (LPARAM)&hWnd);
    }
    WaitForInputIdle(pProcInfo.hProcess, 30000);
    CloseHandle(pProcInfo.hProcess);
    CloseHandle(pProcInfo.hThread);

    if (hWnd == NULL)
    {
        FreeLibrary(hModule);
        return false;
    }

    CComPtr<IHTMLDocument2> spDoc;
    LRESULT lRes;

    UINT nMsg = ::RegisterWindowMessage(_T("WM_HTML_GETOBJECT"));
    ::SendMessageTimeout(hWnd, nMsg, 0L, 0L, SMTO_ABORTIFHUNG, 10000, (DWORD*)&lRes);

    if (FAILED(ObjectFromLresult(lRes, __uuidof(IHTMLDocument2), 0, (void**)&spDoc)))
    {
        FreeLibrary(hModule);
        return false;
    }

    CComQIPtr<IServiceProvider> spsp1;
    CComQIPtr<IServiceProvider> spsp2;
    CComPtr<IWebBrowser2> spwb;

    spsp1 = spDoc;
    spsp1->QueryService(SID_STopLevelBrowser, IID_IServiceProvider, (void**)&spsp2);
    spsp2->QueryService(SID_SWebBrowserApp, IID_IWebBrowser2, (void**)&spwb);
    spwb->put_Resizable(FALSE);
    spwb->put_AddressBar(FALSE);
    spwb->put_MenuBar(FALSE);
    spwb->put_ToolBar(FALSE);
    spwb->put_StatusBar(FALSE);

    // IE7의 Vista Aero 관련 버그 회피(DwmExtendFrameIntoClientArea도 사용 가능)
    PostMessage(hWndIEFrame, 0x031E/*WM_DWMCOMPOSITIONCHANGED*/, 0, 0);

    FreeLibrary(hModule);

    return true;
}

문제점은 Navigate할 때 Post 정보 등의 Parameter를 넘길 수 없고 Navigate 전에 IWebBrowser2의 Property를 변경할 수도 없으며 Navigation에 대한 event도 받을 수 없는 것 등인데 임시로 웹서버 상에 빈페이지를 하나 만들고 해당 URL로 먼저 Navigation한 다음 받은 IWebBrowser를 이용해서 원하는 URL로 Navigation하는 방법을 사용할 수 있다.

덧붙임.
http://still.tistory.com/34
IE8에서는 보호 모드가 LCIE 형태로 변경되어 이러한 작업 없이 그냥 IWebBrowser2를 얻을 수 있다.

댓글