mshtml 메모리 릭 최소화하기
오후 3:59 2003-12-09 bro@shinbiro.com
=======================================================

mshtml은 웹브라우저 컨트롤이나 CHtmlView를 사용할때 내부 HTML 다큐먼트
안의 엘리먼트 값을 보거나 수정하기 위해서 쓰는 HTML DOM Parser COM
라이브러리이다.
문제는 잘못사용하면 메모리릭이 많이 생긴다는 것이다.

컨트롤이나 CHtmlView를 사용시 Navigate시 메모리가 거의 7M정도는
올라간다는 것을 감안해야한다.
관련 라이브러리 dll들이 로드되면서 3M 정도가 일딴 올라가고
Navigate한 번 호출로 바로 또 4M정도가 추가로 올라간다.
문제는 Navigate를 여러번 할때마다 적어도 4K정도의 메모리릭이
발생한다 이는 웹브라우저가 사용하는 wininet 인터넷 라이브러리의
내부적 버그로 알려져 있다.

이 문서는 mshtml을 잘 사용하여 추가적인 메모리릭을 최소화하자는데 있다.


사용하기

// MSHTML 라이브러리 사용
#pragma warning(push)
#pragma warning(disable: 4099)
#pragma warning(disable: 4146)
#pragma warning(disable: 4192)
#import <mshtml.tlb>
#pragma warning(default: 4192)
#pragma warning(default: 4049)
#pragma warning(default: 4146)
#pragma warning(pop)

#pragma warning(disable: 4049)


다음은 웹브라우저에서 특정 요소값을 읽어오는 코드다.

        try
        {
        MSHTML::IHTMLDocument2Ptr doc = (MSHTML::IHTMLDocument2Ptr)m_Web.GetDocument();

        MSHTML::IHTMLElementCollectionPtr all = doc->all;
        MSHTML::IHTMLElementPtr select = all->item( _variant_t( _bstr_t(_T("idSelect"))), vtMissing );
        // 아래처럼 하지 말라. 이경우 Navigate후 200K 증가후 종료시 20K만 떨어지는 문제가 있었다.
        //MSHTML::IHTMLElementPtr select = doc->all->item( _variant_t( _bstr_t(_T("idSelect"))), vtMissing );

        // 아래 코드는 해당 엘리먼트의 이벤트를 받는 CWinSink클래스(msdn제공)를 썼을때며
        m_Sink.m_hEventWnd = GetSafeHwnd();
        m_Sink.Init( select );
        
        // 거꾸로 잘 Release를 명시적으로 호출한다. 이경우 4K의 기본 메모리릭만 발생하게 된다.        
        // 이벤트를 싱크해서 하는 경우 문제의 소지가 있는듯하다 JSCRIPT 쪽에서 뻑나는 경우가 있는것 같다.
        // 하기전에 꼭 테스트해보자.
        select->Release();
        all->Release();
        doc->Release();
        
        }
        catch( ... )
        //catch( _com_error & e )
        {
        }        


// 만일 이런 순으로 다이얼로그에서 호출했다면
        pdlg = new CTestDlg;

        pdlg->Create(CTestDlg::IDD, this );
        pdlg->ShowWindow(SW_SHOW);

// OnClose나 OnCencel, OnOK에서 DestroyWindow()를 호출해준다.
void CTestDlg::OnOK()
{
    DestroyWindow();
}
void CTestDlg::OnCancel()
{
    DestroyWindow();
}

void CTestDlg::OnClose()
{
        // TODO: Add your message handler code here and/or call default
        
        CDialog::OnClose();
        DestroyWindow();
}

// 윈도우가 파괴되기전에 이벤트 싱크했던게 있으면 빼주고. (Unadvise)
void CTestDlg::OnDestroy()
{
        m_Sink.Passivate();
        CDialog::OnDestroy();
}

// 마지막으로 윈도우를 new했다면 이 함수에서 delete this를 해야한다.
// 안그러면 Warning: calling DestroyWindow in CWnd::~CWnd; OnDestroy or PostNcDestroy in derived class will not be called.
// 이런 워닝을 받게된다.
void CTestDlg::PostNcDestroy()
{
        // TODO: Add your specialized code here and/or call the base class
        
        CDialog::PostNcDestroy();
        delete this;
}

------------------------------------------------------
몇가지 기본적으로 적재되는 웹브라우저 관련 메모리 줄이기 팁


        CTestDlg dlg;
        dlg.DoModal();
        
        CoFreeUnusedLibraries();

The thread 0x944 has exited with code 0 (0x0).
The thread 0x658 has exited with code 0 (0x0).
Loaded 'C:\WINNT\system32\MSHTML.DLL', no matching symbolic information found.
Loaded 'C:\WINNT\system32\msls31.dll', no matching symbolic information found.
Loaded 'C:\WINNT\system32\shfolder.dll', no matching symbolic information found.

위와 같이하면 10M까지 올라갔던것이 8M로 준다.

그런데 이벤트를 싱크하고 실제 이벤트를 발생시키게 되면 reference가 추가되어서
그런지 줄지 않는다..;

-----------------------------------------------------
스레드로 뽑아내볼 수도 있다.

위와 같이하면 10M까지 올라갔던것이 8M로 준다.

그런데 이 역시 이벤트를 싱크하고 실제 이벤트를 발생시키게 되면 reference가 추가되어서
그런지 줄지 않는다..;

UINT MyControllingFunction( LPVOID pParam )
{
        AfxOleInit();
        //AfxEnableControlContainer();

        // TODO: Add your message handler code here

        CTestDlg dlg;

        dlg.DoModal();

        AfxOleTerm(FALSE);
        return 0;
}

void CTestFixLeakMSHTMLDlg::OnButton4()
{
        // TODO: Add your control notification handler code here
        AfxBeginThread( MyControllingFunction, NULL );
}

매번 호출될때 마다.
Loaded 'C:\WINNT\system32\MSHTML.DLL', no matching symbolic information found.
Loaded 'C:\WINNT\system32\msls31.dll', no matching symbolic information found.
Loaded 'C:\WINNT\system32\shfolder.dll', no matching symbolic information found.
The thread 0x7CC has exited with code 0 (0x0).
The thread 0x76C has exited with code 0 (0x0).
The thread 0x5EC has exited with code 0 (0x0).

--------------------------------------------------------------------
알려진 또다른 릭
Q241750: BUG: CHtmlView Leaks Memory by Not Releasing BSTRs in Several Methods



다른 유용한 이야기
Q293215 "PRB: An Application's Working Set Is Trimmed When Its Top-Level Window Is Minimized"


---------------------------------------------------------------------
웹브라우저 컨트롤을 담고 있는 다이얼로그를 숨겼다가 다시 나타나게 해도
DocumentComplete 이벤트가 떨어진다.
만일 위의 이벤트에서 이벤트 싱크를 하는코드를 넣었다면
숨겼다가 나타낼때마다 이벤트 싱크를 여러번하게 되어 문제를 일으킬수 있다.


오후 6:05 2003-12-17 현재
이벤트 싱크를 해야 하는 경우 가장 추천하는 방법은
MSHTML::IHTMLInputElementPtr val; 따로 기억하고
void CToyDlg::OnDocumentCompleteExplorer(LPDISPATCH pDisp, VARIANT FAR* URL)
{
        // TODO: Add your control notification handler code here
        m_Sink.Passivate();
        Sink();
}
void CToyDlg::Skin()
{
        try
        {
        MSHTML::IHTMLDocument2Ptr doc = (MSHTML::IHTMLDocument2Ptr)m_Web.GetDocument();
        MSHTML::IHTMLElementCollectionPtr all = doc->all;
        MSHTML::IHTMLElementPtr select = all->item( _variant_t( _bstr_t(_T("idSelect"))), vtMissing );

        m_Sink.m_hEventWnd = GetSafeHwnd();
        m_Sink.Init( select );

        val = all->item( _variant_t( _bstr_t(_T("idValue"))), vtMissing );

        //그냥 안한다.                
        //select->Release();
        //all->Release();
        //doc->Release();
        }
        //__except(EXCEPTION_EXECUTE_HANDLER)
        //catch( ... )
        catch( _com_error & e )
        {
        }        
}
... 필요할때
        CString* psid = new CString;
        *psid = (LPCTSTR)val->value;
        m_pTarget->PostMessage( WUM_TOY_CHANGE, (WPARAM)psid, 0 );
얻도록 한다..

'KB > MFC/Win32' 카테고리의 다른 글

msn6 코드 스니펫  (0) 2004.03.19
MSN 코드 모음  (0) 2004.03.19
HTML 스크립트 함수 ActiveX에서 호출하기  (0) 2004.03.19
HTML 소스 얻기  (0) 2004.03.19
MFC DLL Debug/Release 구분해서 하기  (0) 2004.03.19

+ Recent posts