2010. 12. 8. 16:59

CHtmlView의 C++에서 생성한 JScript를 모든 Frame에 대해 Inject하여 실행하기

2010/11/03 - [프로그래밍/Let's Share it] - CHtmlView의 C++에서 생성한 JScript를 Inject하여 실행하기
에서 version up된 내용을 다시 공유합니다.

이전 포스트에서 CHtmlView로 html에 jscript를 inject하는 방법을 알아보았는데, 문제는 frameset이 지원되지 않는다는 점입니다. 즉, a.html(=1.html + 2.html)로 되는 경우, 1.html/2.html에서는 jscript가 inject되지 않습니다. 따라서, frame을 enum하여 모든 frame에서 가능토록 하고, recursive 사용으로 인한 depth cut-off까지 구현된것을 공유합니다.

사용법은 간단한데, CHtmlView의 DocumentComplete와 NavigateComplete2 시기에,
InjectJScriptAllFrame(this, TEXT("function XXXX() {....}"), 10);
과 같이 사용하면 됩니다.

즉, MAX Frame Depth를 10으로 준 경우이며,
만약 10개가 넘는 경우, Navigate가 중단되며, 꼭 계층적으로 10개의 Frame까지 표시되는 것은 아니니,
주의하시기 바랍니다.

view plain하면 좀더 코드를 좀더 잘 확인할 수 있습니다.
HRESULT InjectJScriptAllFrame(IN CHtmlView* pcWnd, IN LPCTSTR lpszJScript, IN INT nMaxRecurseFrame)
{
	HRESULT			hr				= S_OK;
	BOOL			bStackOverflow	= FALSE;
	IHTMLDocument2*	pIHtmlDocument2	= NULL;

	if ((NULL == pcWnd) || (NULL == lpszJScript))
	{
		hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
		ASSERT(FALSE);
		goto FINAL;
	}

	if (NULL == pcWnd->GetHtmlDocument())
	{
		hr = HRESULT_FROM_WIN32(ERROR_NOT_READY);
		ASSERT(FALSE);
		goto FINAL;
	}

	hr = pcWnd->GetHtmlDocument()->QueryInterface(IID_IHTMLDocument2, (VOID**)&pIHtmlDocument2);
	if (SUCCEEDED(hr))
	{
		if (NULL == pIHtmlDocument2)
		{
			hr = HRESULT_FROM_WIN32(ERROR_INTERNAL_ERROR);
			ASSERT(FALSE);
			goto FINAL;
		}

		// HtmlDocument를 Frame 별로 recursive하게 JScript를 inject한다.
		// 만약, Frame이 MAX가 되었다면, hr은 HRESULT_FROM_WIN32(ERROR_STACK_OVERFLOW)가 리턴된다.
		hr = InjectJScriptByDocumentRecurse(pIHtmlDocument2, lpszJScript, nMaxRecurseFrame, 0, &bStackOverflow);

		if (HRESULT_FROM_WIN32(ERROR_STACK_OVERFLOW) == hr)
		{
			// Stack overflow인 경우에는 stop~!
			pcWnd->Stop();
		}
	}

FINAL:

	if (NULL != pIHtmlDocument2)
	{
		pIHtmlDocument2->Release();
		pIHtmlDocument2 = NULL;
	}

	return hr;
}

HRESULT InjectJScriptByDocumentRecurse(IN IHTMLDocument2* pIHtmlDocument2, IN LPCTSTR lpszJScript, IN INT nMaxRecurseFrame, IN INT nCurDepth, OUT LPBOOL pbStackOverflow)
{
	HRESULT						hr						= S_OK;
	HRESULT						hr2						= S_OK;
	LONG						nFrameCount				= 0;
	LONG						i						= 0;
	VARIANT						varIndex				= {0,};
	VARIANT						varDispWin				= {0,};
	BSTR						bstrScript				= {0,};
	BSTR						bstrElementType			= {0,};
	BSTR						bstrInsertWhere			= {0,};
	IHTMLElement*				pIHtmlElement			= NULL;
	IHTMLElement2*				pIHtmlElement2			= NULL;
	IHTMLElement*				pIHtmlElementScript		= NULL;
	IHTMLScriptElement*			pIHtmlScript			= NULL;
	IHTMLFramesCollection2*		pIHtmlFramesCollection	= NULL;
	IHTMLWindow2*				pIHtmlWindow			= NULL;
	IHTMLDocument2*				pIHtmlDocument2Frame	= NULL;

	if ((NULL == pIHtmlDocument2) || (NULL == lpszJScript) || (NULL == pbStackOverflow))
	{
		hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
		goto FINAL;
	}

	if (nCurDepth >= nMaxRecurseFrame)
	{
		hr = HRESULT_FROM_WIN32(ERROR_STACK_OVERFLOW);
		*pbStackOverflow = TRUE;
		goto FINAL;
	}

	if (TRUE == *pbStackOverflow)
	{
		hr = HRESULT_FROM_WIN32(ERROR_STACK_OVERFLOW);
		goto FINAL;
	}

	// insertAdjacentElement에 들어갈 Param을 결정한다.
	bstrScript		= ::SysAllocString(T2COLE(lpszJScript));
	bstrElementType = ::SysAllocString(T2COLE(TEXT("script")));
	bstrInsertWhere	= ::SysAllocString(T2COLE(TEXT("afterBegin")));

	hr = pIHtmlDocument2->get_body(&pIHtmlElement);
	if ((FAILED(hr)) || (NULL == pIHtmlElement))
	{
		if ((SUCCEEDED(hr)) && (NULL == pIHtmlElement))
		{
			hr = HRESULT_FROM_WIN32(ERROR_NOT_READY);
			goto FINAL;
		}

		pIHtmlElement = NULL;
		ASSERT(FALSE);
		goto FINAL;
	}

	hr = pIHtmlElement->QueryInterface(IID_IHTMLElement2, (VOID**)&pIHtmlElement2);
	if (FAILED(hr))
	{
		pIHtmlElement2 = NULL;
		ASSERT(FALSE);
		goto FINAL;
	}

	hr = pIHtmlDocument2->createElement(bstrElementType, &pIHtmlElementScript);
	if (FAILED(hr))
	{
		pIHtmlElementScript = NULL;
		ASSERT(FALSE);
		goto FINAL;
	}

	hr = pIHtmlElementScript->QueryInterface(IID_IHTMLScriptElement, (VOID**)&pIHtmlScript);
	if (FAILED(hr))
	{
		pIHtmlScript = NULL;
		ASSERT(FALSE);
		goto FINAL;
	}

	hr = pIHtmlScript->put_defer(VARIANT_TRUE);
	if (FAILED(hr))
	{
		ASSERT(FALSE);
		goto FINAL;
	}

	hr = pIHtmlScript->put_text(bstrScript);
	if (FAILED(hr))
	{
		ASSERT(FALSE);
		goto FINAL;
	}

	hr = pIHtmlElement2->insertAdjacentElement(bstrInsertWhere, pIHtmlElementScript, NULL);
	if (FAILED(hr))
	{
		ASSERT(FALSE);
		goto FINAL;
	}

	// 여기까지 왔다면, 일단 성공~
	hr = S_OK;

	hr2 = pIHtmlDocument2->get_frames(&pIHtmlFramesCollection);
	if (FAILED(hr2))
	{
		// Frame이 없는 깔끔한 경우이다~
		goto FINAL;
	}

	hr2 = pIHtmlFramesCollection->get_length(&nFrameCount);
	if (FAILED(hr2))
	{
		goto FINAL;
	}

	for (i=0; iRelease();
			varDispWin.pdispVal = NULL;
		}

		hr2 = pIHtmlFramesCollection->item(&varIndex, &varDispWin);
		if (FAILED(hr2))
		{
			continue;
		}

		if (NULL == varDispWin.pdispVal)
		{
			ASSERT(FALSE);
			continue;
		}

		if (NULL != pIHtmlWindow)
		{
			pIHtmlWindow->Release();
			pIHtmlWindow = NULL;
		}

		hr2 = varDispWin.pdispVal->QueryInterface(IID_IHTMLWindow2, (VOID**)&pIHtmlWindow);
		if (FAILED(hr2))
		{
			continue;
		}

		if (NULL == pIHtmlWindow)
		{
			ASSERT(FALSE);
			continue;
		}

		if (NULL != pIHtmlDocument2Frame)
		{
			pIHtmlDocument2Frame->Release();
			pIHtmlDocument2Frame = NULL;
		}

		hr2 = pIHtmlWindow->get_document(&pIHtmlDocument2Frame);
		if (FAILED(hr2))
		{
			continue;
		}

		if (NULL == pIHtmlDocument2Frame)
		{
			ASSERT(FALSE);
			continue;
		}

		// Recursive 실행~!!!
		hr2 = InjectJScriptByDocumentRecurse(pIHtmlDocument2Frame, lpszJScript, nMaxRecurseFrame, nCurDepth+1, pbStackOverflow);

		// StackOverflow
		if (HRESULT_FROM_WIN32(ERROR_STACK_OVERFLOW) == hr2)
		{
			(*pbStackOverflow) = TRUE;
			hr = HRESULT_FROM_WIN32(ERROR_STACK_OVERFLOW);
			break;
		}
	}

FINAL:

	if (NULL != pIHtmlDocument2Frame)
	{
		pIHtmlDocument2Frame->Release();
		pIHtmlDocument2Frame = NULL;
	}

	if (NULL != pIHtmlWindow)
	{
		pIHtmlWindow->Release();
		pIHtmlWindow = NULL;
	}

	if (NULL != varDispWin.pdispVal)
	{
		varDispWin.pdispVal->Release();
		varDispWin.pdispVal = NULL;
	}

	if (NULL != pIHtmlScript)
	{
		pIHtmlScript->Release();
		pIHtmlScript = NULL;
	}

	if (NULL != pIHtmlElementScript)
	{
		pIHtmlElementScript->Release();
		pIHtmlElementScript = NULL;
	}

	if (NULL != pIHtmlElement2)
	{
		pIHtmlElement2->Release();
		pIHtmlElement2 = NULL;
	}

	if (NULL != pIHtmlElement)
	{
		pIHtmlElement->Release();
		pIHtmlElement = NULL;
	}

	::SysFreeString(bstrScript);
	::SysFreeString(bstrElementType);
	::SysFreeString(bstrInsertWhere);

	return hr;
}