CHtmlView의 C++에서 생성한 JScript를 Inject하여 실행하기

CHtmlView(CDHtmlDialog, CHtmlDialog)등에서 HTML을 로드하여 실행하게 됩니다.
혹시 이런 생각을 해보신적 있는지요?

기존 HTML 소스에 동적으로 JScript를 추가하고, 그 함수를 호출받고 싶다...
단, 추가할 JScript 소스는 C++에서 명시적으로 정의한다.
==>
즉, 동일한 URL에 대해 기존의 일반 웹 브라우저와 조금 다른 웹(즉, 뭐.. 화면 상단에 강제적인 버튼 추과와 그 처리등등...)을 표현하고 싶다.
즉, C++에서 DocumentComplete Timing때, C++에서 정의한 JScript 함수를 넣는다. Body OnLoad()에 그 함수를 대체해서 넣는다. 즉, 기존 Html이
function OnLoad()
{
 alert('hello');
}
였다면,
function OnLoad()
{
 alert('hello'); // <- 기존 내용
 inject();
}
function inject()
{
 alert('injected');
}
와 같이 수정하면, inject()가 실행될 것입니다.
즉, C++의 DocumentComplete에서,
InjectScript(..., "function OnLoad(){alert('hello');inject();}function inject(){alert('injected');}", ...)
를 호출하는 것입니다.

이를 위한 소스 코드를 아래와 같이 공유합니다.
사용은 CHtmlView의 OnDocumentComplete나 DocumentComplete 때 아래 함수를 호출하면 됩니다.
(view plain을 누르시면 코드 확인이 쉽습니다.)
  1. HRESULT InjectJScript(IN CHtmlView* pcWnd, IN LPCTSTR lpszJScript)  
  2. {  
  3.     if ((NULL == pcWnd) || (NULL == lpszJScript))  
  4.     {  
  5.         return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);  
  6.     }  
  7.   
  8.     return InjectJScript(pcWnd->GetHtmlDocument(), lpszJScript);  
  9. }  
  10.   
  11. // HTML에 JScript를 이식시킨다.  
  12. HRESULT InjectJScript(IN LPDISPATCH pDispatch, IN LPCTSTR lpszJScript)  
  13. {  
  14.     HRESULT                     hr                  = S_OK;  
  15.     IHTMLDocument2*             pIHtmlDocument2     = NULL;  
  16.     BSTR                        bstrScript          = {0,};  
  17.     BSTR                        bstrElementType     = {0,};  
  18.     BSTR                        bstrInsertWhere     = {0,};  
  19.     IHTMLElement*               pIHtmlElement       = NULL;  
  20.     IHTMLElement2*              pIHtmlElement2      = NULL;  
  21.     IHTMLElement*               pIHtmlElementScript = NULL;  
  22.     IHTMLScriptElement*         pIHtmlScript        = NULL;  
  23.   
  24.     if ((NULL == pDispatch) || (NULL == lpszJScript))  
  25.     {  
  26.         hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);  
  27.         goto FINAL;  
  28.     }  
  29.   
  30.     bstrScript      = ::SysAllocString(T2COLE(lpszJScript));  
  31.     bstrElementType = ::SysAllocString(T2COLE(TEXT("script")));  
  32.     bstrInsertWhere = ::SysAllocString(T2COLE(TEXT("afterBegin")));  
  33.   
  34.     hr = pDispatch->QueryInterface(IID_IHTMLDocument2, (VOID**)&pIHtmlDocument2);  
  35.     if (FAILED(hr))  
  36.     {  
  37.         pIHtmlDocument2 = NULL;  
  38.         ASSERT(FALSE);  
  39.         goto FINAL;  
  40.     }  
  41.   
  42.     hr = pIHtmlDocument2->get_body(&pIHtmlElement);  
  43.     if (FAILED(hr))  
  44.     {  
  45.         pIHtmlElement = NULL;  
  46.         ASSERT(FALSE);  
  47.         goto FINAL;  
  48.     }  
  49.   
  50.     hr = pIHtmlElement->QueryInterface(IID_IHTMLElement2, (VOID**)&pIHtmlElement2);  
  51.     if (FAILED(hr))  
  52.     {  
  53.         pIHtmlElement2 = NULL;  
  54.         ASSERT(FALSE);  
  55.         goto FINAL;  
  56.     }  
  57.   
  58.     hr = pIHtmlDocument2->createElement(bstrElementType, &pIHtmlElementScript);  
  59.     if (FAILED(hr))  
  60.     {  
  61.         pIHtmlElementScript = NULL;  
  62.         ASSERT(FALSE);  
  63.         goto FINAL;  
  64.     }  
  65.   
  66.     hr = pIHtmlElementScript->QueryInterface(IID_IHTMLScriptElement, (VOID**)&pIHtmlScript);  
  67.     if (FAILED(hr))  
  68.     {  
  69.         pIHtmlScript = NULL;  
  70.         ASSERT(FALSE);  
  71.         goto FINAL;  
  72.     }  
  73.   
  74.     hr = pIHtmlScript->put_defer(VARIANT_TRUE);  
  75.     if (FAILED(hr))  
  76.     {  
  77.         ASSERT(FALSE);  
  78.         goto FINAL;  
  79.     }  
  80.   
  81.     hr = pIHtmlScript->put_text(bstrScript);  
  82.     if (FAILED(hr))  
  83.     {  
  84.         ASSERT(FALSE);  
  85.         goto FINAL;  
  86.     }  
  87.   
  88.     hr = pIHtmlElement2->insertAdjacentElement(bstrInsertWhere, pIHtmlElementScript, NULL);  
  89.     if (FAILED(hr))  
  90.     {  
  91.         ASSERT(FALSE);  
  92.         goto FINAL;  
  93.     }  
  94.   
  95.     // 여기까지 왔다면, 성공~  
  96.     hr = S_OK;  
  97.   
  98. FINAL:  
  99.   
  100.     if (NULL != pIHtmlScript)  
  101.     {  
  102.         pIHtmlScript->Release();  
  103.         pIHtmlScript = NULL;  
  104.     }  
  105.   
  106.     if (NULL != pIHtmlElementScript)  
  107.     {  
  108.         pIHtmlElementScript->Release();  
  109.         pIHtmlElementScript = NULL;  
  110.     }  
  111.   
  112.     if (NULL != pIHtmlElement2)  
  113.     {  
  114.         pIHtmlElement2->Release();  
  115.         pIHtmlElement2 = NULL;  
  116.     }  
  117.   
  118.     if (NULL != pIHtmlElement)  
  119.     {  
  120.         pIHtmlElement->Release();  
  121.         pIHtmlElement = NULL;  
  122.     }  
  123.   
  124.     if (NULL != pIHtmlDocument2)  
  125.     {  
  126.         pIHtmlDocument2->Release();  
  127.         pIHtmlDocument2 = NULL;  
  128.     }  
  129.   
  130.     ::SysFreeString(bstrScript);  
  131.     ::SysFreeString(bstrElementType);  
  132.     ::SysFreeString(bstrInsertWhere);  
  133.   
  134.     return hr;  
  135. }  

ps)
위 코드의 중심은 insertAdjacentElement인데, 좀더 연구를 하면, 기존의 함수를 그대로 두고, OnLoad시 기존의 함수는 그대로 실행되고, 그때 연속하여 Inject 함수를 실행할 수 있게금 할 수 도 있어 보입니다. 이건 노력 여부에 달린 문제네요... 물론, 단순하게 JScript 대신 Tag Element로 insertAdjacentElement하여 해결될 수 있습니다.
하지만, 일반 Tag Element를 insertAdjacentElement하는 예는 많은데, 순수하게 JScript만 되는 경우는 없어서 한번 만들어본 내용입니다.