// 깔끔하게 만들려고 노력했다.
// 에러처리 완벽하게 할려고 했다.

/*
 // <사용방법>
 LPWSTR lpszSource;      // WCHAR 포인터(유니코드 문자열) 변수 할당
 if(GetHtmlSource(pDoc, &lpszSource)){ // 성공여부 체크
  //ClipBoardTextCopyW(lpszSource); // 필요한 작업~~~~
  //.....
  //....
  //...
  free(lpszSource);     // ★ 작업이 끝나면 반드시 해제하자
 }

*/

int __stdcall GetHtmlSource(IHTMLDocument2 *pDoc, LPWSTR *ppszText)
{
 int iRunCount = 0;  // 함수 실행 횟수 제한키 위해
 int iSuccessCount = 0; // 성공적으로 프레임으로부터 소스 추출한 횟수

 *ppszText = (LPWSTR)calloc(1, sizeof(WCHAR));
 if(NULL == *ppszText){
  MessageBox(NULL, _T("초기 2바이트 메모리 할당에 실패하였습니다."), _T("GetHtmlSource 에러"), MB_ICONERROR|MB_TOPMOST);
  *ppszText = NULL;
  return 0;
 }
 
 iSuccessCount = GetHtmlSourceRecur(pDoc, ppszText, &iRunCount); // 실질적인 재귀함수 실행
 if(0 == iSuccessCount) free(*ppszText); // 성공카운트가 0이면 실패이므로 메모리 해제하자.
 
 return iSuccessCount;
}


// 프레임안에 프레임이 있는 구조가 흔하므로 재귀함수로 구성한다.
// 재귀함수의 무한반복(스택 오버플로우 에러 가능)은 위험하므로 호출횟수 제한함.
// 정상적인 웹페이지에서 프레임이 20개 넘는 경우는 없을 것이므로 20으로 제한
// 리턴값은 프레임에서 소스 추출 성공횟수
int __stdcall GetHtmlSourceRecur(IHTMLDocument2 *pDoc, LPWSTR *ppszText, int *piRunCount)
{
 // 호출횟수 검사
 *piRunCount = *piRunCount+1;
 if(*piRunCount > 20){
  MessageBox(NULL, _T("GetHtmlSourceRecur 재귀함수 호출이 20회를 초과하였습니다."), _T("GetHtmlSource 에러"), MB_ICONERROR|MB_TOPMOST);
  return 0;
 }

 BOOL bRes=FALSE;
 int iSuccessCount = 0;
 
 // 현재의 프레임의 URL 추출
 LPCWSTR lpszPreStr=L"<!-- ◀◀◀ 프레임 주소: "; // 주석표시와 앞에 붙일 스트링
 BSTR bstrUrl;
 if(S_OK == pDoc->get_URL(&bstrUrl)){
  *ppszText = (LPWSTR)realloc(*ppszText, (lstrlenW(*ppszText)+lstrlenW(bstrUrl)+lstrlenW(lpszPreStr)+5+1)*sizeof(WCHAR));
  if(*ppszText){
   lstrcatW(*ppszText, lpszPreStr);
   lstrcatW(*ppszText, bstrUrl);
   lstrcatW(*ppszText, L"-->\r\n"); // 주석 닫음과 개행(길이 5)
   bRes = TRUE;
  } else{
   MessageBox(NULL, _T("URL을 위한 메모리 할당이 실패하였습니다."), _T("GetHtmlSource 에러"), MB_ICONERROR|MB_TOPMOST);
   bRes = FALSE;
  }
  SysFreeString(bstrUrl);
 }
 
 if(!bRes){
  MessageBox(NULL, _T("URL을 얻는 데 실패하였습니다."), _T("GetHtmlSource 에러"), MB_ICONERROR|MB_TOPMOST);
  return 0;
 }


 // 현재의 프레임의 도큐먼트의 소스 추출... 구하는 원리는 <body>의 부모는 <html>이므로...
 IHTMLElement * pBodyElem=NULL;
 IHTMLElement * pParentElem=NULL;
 BSTR bstrSource;
 
 bRes=FALSE;
 if(S_OK==pDoc->get_body(&pBodyElem)){
  if(S_OK==pBodyElem->get_parentElement(&pParentElem)){
   if(S_OK == pParentElem->get_outerHTML(&bstrSource)){
    *ppszText = (LPWSTR)realloc(*ppszText, (lstrlenW(*ppszText)+lstrlenW(bstrSource)+8+1)*sizeof(WCHAR));
    if(*ppszText){
     lstrcatW(*ppszText, bstrSource);
     lstrcatW(*ppszText, L"\r\n\r\n\r\n\r\n"); // 개행 4번(길이 8)
     bRes = TRUE;
    } else{
     MessageBox(NULL, _T("소스를 위한 메모리 할당이 실패하였습니다."), _T("GetHtmlSource 에러"), MB_ICONERROR|MB_TOPMOST);
     bRes = FALSE;
    }
    SysFreeString(bstrSource);
   }
   pParentElem->Release();
  }
  pBodyElem->Release();
 }
 if(!bRes){
  MessageBox(NULL, _T("개별 도큐먼트 소스를 얻는 데 실패하였습니다."), _T("GetHtmlSource 에러"), MB_ICONERROR|MB_TOPMOST);
  return 0;
 }
 
 ++iSuccessCount;


 // 내부에 존재하는 프레임의 수를 검사하고 있으면 재귀호출
 long i, lFrameCount=0;
 IHTMLFramesCollection2 *pFramesCollection = NULL;
 IHTMLWindow2 * pWin=NULL;
 IHTMLDocument2 * pDocFrame = NULL;
 VARIANT varIndex; varIndex.vt=VT_I4;
 VARIANT varDispWin; varDispWin.vt=VT_DISPATCH;
 
 if(S_OK == pDoc->get_frames(&pFramesCollection)){
  if(S_OK == pFramesCollection->get_length(&lFrameCount)){
   for(i=0 ; i<lFrameCount ; i++){
    varIndex.lVal=i;
    if(S_OK == pFramesCollection->item(&varIndex, &varDispWin)){
     if(S_OK == varDispWin.pdispVal->QueryInterface(IID_IHTMLWindow2,(void**)&pWin)){
      if(S_OK == pWin->get_document(&pDocFrame)){ // 하위 doc이 구해지면 재귀시킨다.
       iSuccessCount += GetHtmlSourceRecur(pDocFrame, ppszText, piRunCount);
       pDocFrame->Release();
      }
      pWin->Release();
     }
     varDispWin.pdispVal->Release();
    }
   }
  }
  pFramesCollection->Release();
 }

 return iSuccessCount;
}


2009/09/30 03:32 2009/09/30 03:32

Trackback Address :: https://youngsam.net/trackback/932