웹, HTML

IE 8의 독특한 아키텍처(LCIE)와 CreateProcess, IELaunchURL

디버그정 2013. 6. 23. 09:41

IE 8 버전부터 CreateProcess로 IE를 런칭시키는 경우 프로세스 아이디나 핸들 값을 제대로 구하지 못한다.

IE 8부터 IEXPLORER.EXE가 최소 2개가 실행된다. LCIE(Loosely-Coupled IE)라는 독특한 아키텍처를 취한다.


참조) http://blogs.msdn.com/b/ie/archive/2008/03/11/ie8-and-loosely-coupled-ie-lcie.aspx



UI 프레임은 전체 통틀어서 몽땅 하나의 프로세스에서 처리하고,
각각의 탭, 툴바, ActiveX, BHO를 개별적인 프로세스로 처리한다.
그리고  UI 프레임을 처리하는 프로세스는 부모, 개별 프로세스는 자식의 관계를 가지고 있다.
아래는 웹브라우저 2개를 띄운 경우 Process Explorer로 살펴본 상황이다. 3개의 프로세스가 실행되고
두개의 프로세스는 자식의 형태로 호출됨을 알 수 있다.




하나의 IE를 2개의 프로세스에서 처리하는 독특한 상황인 것이다.

참고로 IWebBrowser2 인터페이스는 UIFrame을 담당하는 프로세스와 맞물려 있으므로
개별 프로세스의 pid 값을 비교검사해서 해당 IE 프레임의 IWebBrowser2를 구하지는 못한다.
(이전 IE7까지 잘 되었던 IShellWindows로 리스팅하고 

CreateProcess pid 비교를 통해 해당 IWebBrowser2를 구하는 코드는

 IE 8부터는 문제가 생긴다.)
모든 UI 프레임 관련 처리는 하나의 통합된 프로세스에서 처리되므로 굳이 비교해서 구하려면 
UI 프레임 프로세스의 pid 값을 해당 IE 프레임 윈도우의 pid 값과 비교해야 한다.
그런데 UI 프레임은 통합되어 하나의 프로세스로 관리되기 때문에
OS에 떠 있는 모든 IE의 UI 프레임 관련 프로세스는 동일하다.
(각각의 IE 프레임 윈도우의 핸들을 구해서 GetWindowThreadProcessId API를 실행해보면
모두 같은 pid 값(UI 프레임 프로세스)임을 알 수 있다.)
즉 UI 프레임을 처리하는 프로세스는 오로지 하나만 존재하므로 IE 프레임 윈도우들간 구분의 역할을 하지 못한다.

예를 들어 a.html과 별개의 다른 창에 존재하는 b.html의 UI 프레임 프로세스는 동일하다.
물론 각각의 IE의 탭, 툴바, ActiveX, BHO를 처리하는 프로세스는 다르다.
이를테면 UI 프레임을 담당하는 하나의 통합 프로세스에는
IWebBrowser2 인터페이스를 노출하는 객체 등 UI 관련 COM 오브젝트들이 몽땅 들어가 있고
각각의 개별 프로세스들에는 해당 IE와 관련된 탭, 툴바, ActiveX, BHO 오브젝트들이 있을 것이다.


이런 독특한 구조 때문인지 CreateProcess는 평상시대로의 활용을 하지 못한다.

아마 WinMain 함수 첫 부분에서 위 독특한 아키텍처에 맞도록 처리될 것이다.

테스트해 본 적이 있는데 

CreateProcess 실행시 리턴되는 프로세스 핸들 중 최초 실행한 경우의 프로세스만 유효하게 존재하고(UI Frame 처리 프로세스)

브라우저가 이미 떠 있는 상황에서 실행한 경우에는 얼마 안 있어 종료한다.

UI Frame 처리 프로세스가 이미 존재하는 경우 그 곳에 해당 IE의 UI Frame 오브젝트를 생성하고
해당 IE에 대응하는 탭, 툴바 등등 개별 프로세스를 새로이 런칭시키는 등등
이런 과정을 행하리라는 것을 짐작할 수 있다.


IE 7부터 지원되는  IELaunchURL 함수를 사용해야 제대로 해당 IE의 프로세스 아이디나 핸들을 구할 수 있다.

여기서 IELaunchURL으로 얻어지는 pid는 OS에 하나만 존재하는 UI Frame 프로세스의 아이디가 아니라

탭, 툴바 등등을 처리하는 개별 프로세스의 아이디이다. 

둘은 부모-자식 관계이므로 UI 프레임을 처리하는 프로세스를 얻으려면

스냅샷이나 이넘프로세시스 등 프로세스 리스팅 API를 통해 부모 프로세스 아이디를 구하면 된다.


참고로 IWebBrowser2 인터페이스만 구하고자 할 때에는 간편하게 CoCreateInstance를 사용하면 로딩과 동시에 구할 수 있다.  

CoCreateInstance(CLSID_InternetExplorer, NULL, CLSCTX_LOCAL_SERVER, IID_IWebBrowser2, (void**)&pWebBrowser);



// 부모 프로세스 아이디 구하기
DWORD __stdcall GetParentProcessId(DWORD pid)
{
HANDLE hSnapProcess;
BOOL bSuc;

#if defined (_UNICODE) || defined (UNICODE)
PROCESSENTRY32W ppe = {0};
#else
PROCESSENTRY32 ppe = {0};
#endif

ppe.dwSize = sizeof(ppe);
hSnapProcess = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
Process32First(hSnapProcess, &ppe);
//Process32Next(hSnapProcess, &ppe);

bSuc = FALSE;
do {
if (ppe.th32ProcessID == pid) {
bSuc = TRUE;
break;
}
} while (Process32Next(hSnapProcess, &ppe)); // 다음 프로세스 검사한다.

CloseHandle(hSnapProcess);

if (bSuc) return ppe.th32ParentProcessID;
return 0; 
}





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

typedef BOOL (WINAPI *IELAUNCHURL)(LPCWSTR, LPPROCESS_INFORMATION, LPIELAUNCHURLINFO);

// 동적으로 Dll을 로딩해서 실행한다.
HRESULT WINAPI IELaunchURL_Wrap(LPCWSTR pszURL,
    LPPROCESS_INFORMATION ppi,
    LPIELAUNCHURLINFO pli)
{
IELAUNCHURL fpIELaunchURL;
HMODULE hDll;
HRESULT hr;
hDll = LoadLibrary("ieframe.dll");
if (hDll)
{
fpIELaunchURL =  (IELAUNCHURL)GetProcAddress(hDll, "IELaunchURL");
hr = fpIELaunchURL(pszURL, ppi, pli);
//if (SUCCEEDED(hr))  // 외부 호출부에서 처리한다.
//{
// WaitForInputIdle(pi.hProcess, 2000);
// CloseHandle(pi.hProcess);
// CloseHandle(pi.hThread);
//}
FreeLibrary(hDll);
return hr;
}
return E_FAIL;
}


호출부에서는 아래와 같이 사용하면 된다.
char szText[256];
HRESULT hr;
wchar_t *wszUrl = L"http://www.google.co.kr/";
PROCESS_INFORMATION pi = {0};
IELAUNCHURLINFO li = {0};
li.cbSize = sizeof(IELAUNCHURLINFO);

hr = IELaunchURL_Wrap(wszUrl, &pi, &li);
if (SUCCEEDED(hr))
{
WaitForInputIdle(pi.hProcess, 2000);
//CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);

wsprintf(szText, "%d", pi.dwProcessId);
MessageBox(NULL, szText, "pid",MB_TOPMOST);

TerminateProcess(pi.hProcess, 0);  // TerminateProcess 잘 통하는가 확인
CloseHandle(pi.hProcess);
}


============MSDN===============

IELaunchURL function

1 out of 7 rated this helpful - Rate this topic

Opens a URL in an Windows Internet Explorer process with an appropriate integrity level, and returns information about the new process.

Syntax

HRESULT IELaunchURL(
  _In_      LPCWSTR pszUrl,
  _In_      LPPROCESS_INFORMATION pProcInfo,
  _In_opt_  LPIELAUNCHURL lpInfo
);

Parameters

pszUrl [in]

A pointer to a NULL-terminated string containing the URL to be opened. If this parameter is NULL, the user's home page is opened.

pProcInfo [in]

A pointer to a PROCESS_INFORMATION structure that receives information about the process.

lpInfo [in, optional]

A pointer to an IELAUNCHURLINFO structure; can be NULL.

Return value

Returns S_OK if the URL is successfully opened in a new Internet Explorer process, or an error value otherwise.

Examples

The following example shows a function that opens Internet Explorer processes with an appropriate integrity level.

HRESULT LaunchIE(LPCWSTR pszURL)
{
    PROCESS_INFORMATION procInfo;
    IELAUNCHURLINFO launchInfo;
    launchInfo.cbSize = sizeof(IELAUNCHURLINFO);
    launchInfo.dwCreationFlags = NULL;

    HRESULT hr = IELaunchURL(pszURL, &procInfo, &launchInfo);
    if (SUCCEEDED(hr))
    {
        WaitForInputIdle(procInfo.hProcess, 2000);
        CloseHandle(procInfo.hProcess);
        CloseHandle(procInfo.hThread);
    }
    return hr;
}

Requirements

Minimum supported client

Windows Vista [desktop apps only]

Minimum supported server

Windows Server 2008 [desktop apps only]

Product

Internet Explorer 7

Header

Iepmapi.h

Library

Iepmapi.lib

DLL

Ieframe.dll

 

 

Build date: 11/27/2012