IE 확징기능 만들기 , 툴바

IE 확징기능 만들기 , 툴바


데스크탑 프로그래밍에서 인터넷 프로그래밍으로(1)
인터넷 익스플로러의 활용(1) - 브라우저 익스텐션 만들기

예제 프로그램 다운로드

학교 연구실이나 괜찮은 회사의 사무실에서나 접속할 수 있었던 인터넷이 이제는 가정집에서도 전화 접속 네트웍을 통해 쉽게 접속할 수 있는 시대가 마침내 도래했다. 물론 아직까지는 접속 속도가 만족할 만한 수준이라고 하기는 어렵겠지만 하나로 통신, 한국 통신, 두루넷 등등의 다양한 인터넷 서비스 사업자들이 속속 나타나고 있기 때문에 고속 인터넷을 체험할 수 있는 시대도 멀지 않은 것 같다. 따라서 기존의 데스크탑 프로그램의 모양도 이에 발맞춰 변신을 시도해야할 때가 되었다. 즉, 인터넷 접속이 가능하다는 전제하에 프로그램을 작성해도 무방한 차원을 벗어나 인터넷 연결을 통해 그 프로그램의 부가가치가 올라갈 수 있다면 반드시 구현해주어야 한다는 것이다. 인터넷하면 일단 웹 브라우저를 생각하기 쉽고 인터넷 프로그래밍하면 웹 서버 기반의 프로그래밍을 생각하기 쉬운데 이번 연재는 클라이언트 기반의 인터넷 프로그래밍이 주제라는 점을 명심하기 바란다. 웹 서버 프로그래밍에 대해 관심이 있다면 프로그램 세계 9월호 특집을 참고하기 바란다.

이번 달부터 시작해서 5회에 걸쳐 마이크로소프트 윈도우 운영체제상의 인터넷 익스플로러 와 WinInet(WinInet이란 IE와 같이 설치되는 마이크로소프트의 인터넷 라이브러리이다. HTTP, FTP, Gopher 등의 인터넷 프로토콜을 API 레벨에서 사용할 수 있도록 해주는 라이브러리이다. 이와 비슷한 것으로 w3c에서 오픈 소스로 내놓고 있는 libwww(http://www.w3c.org/Library)라는 것도 있는데 이것은 윈도우 이외에도 유닉스, 리눅스 등에서도 사용할 수 있다.) 라이브러리를 바탕으로 하는 인터넷 프로그래밍에 대해 살펴보고자 한다. 즉, 이번 연재를 직접 실행해보고 싶다면 자신의 PC에 인터넷 익스플로러 4.01이나 5.x가 설치되어 있어야 한다. 인터넷 익스플로러를 설치하면 WinInet 라이브러리도 같이 설치된다. 인터넷 익스플로러 5.0은 다음 URL에서 다운로드받아 설치할 수 있다.

http://www.microsoft.com/windows/ie/download/ie5all.htm

예제 프로그램들은 비주얼 C++ 6.0과 비주얼 베이직 6.0을 이용해 만들어 보도록 하겠다. 본 연재를 잘 이해하려면 COM과 API 프로그래밍에 대한 지식이 있어야 한다.

1.IE의 구조와 특징

인터넷 시장에 뒤늦게 뛰어든 마이크로소프트는 넷스케이프의 웹 브라우저인 네비게이터 (Navigator)에 대항하기 위해 인터넷 익스플로러(이하 익스플로러라 부르겠다)를 만들어 냈다. 처음엔 별다른 인기를 끌지 못하고 네비게이터에 뒤쳐졌지만 현재는 네비게이터를 능가하는 인기를 끌고 있다. 필자 역시 익스플로러를 사용한다. 이유는 여러 가지가 있겠지만 익스플로러는 컴포넌트 구조로 되어있기 때문에 프로그래머 입장에서 볼 때 재사용성이 좋기 때문이고 필자가 윈도우 프로그래밍을 하는 관계로 마이크로소프트 제품을 많이 사용하는 것 때문이기도 할 것이다. 웹 페이지의 기능을 놓고 보았을 때도 익스플로러상의 웹 페이지에서는 액티브X 컨트롤을 사용할 수 있는 반면에 네비게이터에서는 플러그인을 사용해야만 한다. 액티브X 컨트롤은 일반 응용프로그램에서도 사용할 수 있지만 플러그인은 네비게이터밖에 사용할 곳이 없다. 하지만 익스플로러가 네비게이터를 앞지른 가장 큰 이유는 무엇보다도 마이크로소프트가 가장 많이 사용되는 윈도우 운영체제를 만든 회사라는 점이 클 것이다. 마이크로소프트가 윈도우 운영체제에 무료로 익스플로러를 기본으로 내장했기 때문에 네비게이터가 뒤쳐지기 시작했고 결국은 따라갈 수 없게 되었다고 보는 것이 정답일 것이다. 사실 윈도우 운영체제는 인터넷 익스플로러와 윈도우 탐색기가 운영체제 셀(Shell)의 기본을 이루고 있다. 익스플로러가 무료로 윈도우 운영체제에 기본으로 내장되기 시작하면서 네비게이터도 무료로 배포되기 시작했지만 넷스케이프는 자금력에서 마이크로소프트를 따라갈 수 없었고 거기다가 네비게이터는 일단 다운로드받던지 CD등을 통해 설치를 해주어야 하는 번거로움이 있었던 반면에 익스플로러는 운영체제만 설치하면 되니까 사용자 입장에서 볼 때는 익스플로러를 먼저 접할 기회가 많게 되었다. 또 마이크로소프트는 자신들의 제품을 사용하려고 하면 익스플로러가 설치되어 있어야만 동작하도록 만들어 놓은 경우가 많았다. 비주얼 스튜디오가 대표적인데 익스플로러가 설치되어 있지 않으면 비주얼 스튜디 오의 설치는 불가능하다. 오 마이 갓 !

각설하고 프로그래머 입장에서 보았을 때 익스플로러의 장점은 컴포넌트 기반으로 되어 있다는 것이다. 이것의 장점은 다음 회에서 가장 잘 느낄 수 있을 것이다. 향후 소프트웨어 프로그래밍은 컴포넌트 기반으로 갈 수 밖에 없다. 운영체제 자체의 시스템 호출 기능도 모두 컴포넌트화될 것이다. 이런 면에서 볼 때 앞으로 응용프로그램을 만들 때는 응용프로그램 자체를 컴포넌트화하는 것에 신경을 써야할 것이다. 향후 연재에서 기본이 되는 것은 익스플로러이기 때문에 먼저 익스플로러에 대해 알아보도록 하겠다.

익스플로러의 구조

필자는 익스플로러 5.0을 사용하고 있다. 익스플로러 5.0 실행 파일(iexplore.exe) 자체는 기능에 비해 크기가 작은 편(78272 바이트)이다. 왜 이렇게 크기가 작을까 ? 다음 그림을 보면 알 수 있듯이 익스플로러 자체가 COM을 기반으로 하는 컴포넌트 구조로 되어 있기 때문이다. COM에서 이야기하는 액티브X 컨트롤과 코드 컴포넌트로 구성되어 있다.


< 그림 1. 익스플로러의 구조 >

위의 그림에서 IEXPLORE.EXE 아래에 있는 SHDOCVW.DLL이 가장 핵심적인 부분이다. 사실 IEXPLORE.EXE는 SHDOCVW.DLL에서 제공해주는 웹 브라우저 컨트롤을 포함하는 컨테이너 역할을 한다. SHDOCVW.DLL에 존재하는 웹 브라우저 컨트롤은 그 자체로 익스플로러의 거의 모든 기능을 제공해준다. 다음 회에서는 바로 이 컨트롤을 이용해 커스텀 웹 브라우저를 만들어 볼 것이다. 이에 대한 기사는 이미 많이 소개되었기 때문에 아는 사람들이 많을 것이다. SHDOCVW.DLL은 주로 웹 페이지의 표시와 네비게이션의 관리와 같은 사용자와의 인터페이스 부분을 담당한다. SHDOCVW.DLL은 이런 일들을 하기 위해 하단에 존재하는 MSHTML.DLL을 사용하는데 이 것은 HTML 파일을 파싱하고 액티브X 컨트롤, 자바 애플릿, 스크립트 등을 처리하는 역할을 담당한다. 사실 XML 파일과 관련된 여러 가지 일을 처리하기 위한 MSXML.DLL이란 파일도 있는데 이것은 위의 그림에서 생략하였다.

익스플로러 자체도 컴포넌트

이와 같이 익스플로러는 컴포넌트를 이용하는 계층적인 구조로 만들어져 있을 뿐만 아니라 익스플로러 자신도 컴포넌트로 동작 가능하다. 익스플로러는 오토메이션 서버로 동작하기 때문에 실행 파일임에도 불구하고 나름대로의 메소드와 프로퍼티를 갖고 있다. 이를 이용하면 익스플로러를 다른 프로그램내에서 간단한 스크립트를 이용해 제어하는 것이 가능하다. 이에 대해서는 여러 번 소개한 적이 있는데 간단히 예를 보이도록 하겠다. 비주얼 베이직을 사용하겠다. 버튼을 하나 갖는 폼을 만들고 그 버튼을 누르면 익스플로러를 띄우는데 메뉴 바와 주소 표시줄을 안 보이게 하면서 처음 화면으로 마이크로소프트의 홈 페이지를 띄워보도록 하겠다.

  • 비주얼 베이직 6.0을 실행한다. 프로젝트 타입으로 "표준 실행 파일"(Standard EXE)를 선택 한다. 프로젝트 메뉴의 참조(Reference) 명령을 실행한다. 거기서 Microsoft Internet Controls를 선택한다.

    < 그림 2. Microsoft Internet Controls의 선택 >
  • 전역 변수로 다음을 선언한다.
    Dim ie As InternetExplorer
    

    즉, ie라는 변수를 인터넷 익스플로러 타입의 변수로 선언하는 것이다. InternetExplorer라는 타입으로 선언했는데 이는 원래 비주얼 베이직의 기본 타입은 아니다. 그렇기 때문에 앞서 참조 명령으로 Microsoft Internet Controls를 선택한 것이 바로 이 타입을 미리 비주얼 베이직에게 알려주기 위함이었다. 이것의 장점은 몇 가지가 있다. 가장 중요한 것은 타입 검사를 통해 엉뚱한 메소드나 프로퍼티를 호출하는 등의 문법 에러를 방지할 수 있다는 것이다. 참조 명령은 이런 목적으로 사용된다는 것도 하나 덤으로 알아두기 바란다. 물론 비주얼 베이직을 좀 써본 사람들이야 다 아는 이야기겠지만 말이다.

  • 다음으로 버튼을 하나 폼 위에 올린다. 그 버튼의 처리 함수를 다음과 같이 채운다.
    Private Sub Command1_Click()
    
        Set ie = CreateObject("InternetExplorer.Application")
        ie.AddressBar = False
        ie.MenuBar = False
        ie.Visible = True
        ie.Navigate "http://www.microsoft.com/"
    
    End Sub
    

    위의 코드를 잠깐 설명하자면 CreateObject는 VB에서 컴포넌트를 동적으로 생성하는데 사용된다. 어떤 컴포넌트를 생성할 것인지를 첫 번째 인자로 지정하는데 해당 컴포넌트의 ProgID를 지정하면 된다. 익스플로러 컴포넌트의 ProgID는 InternetExplorer.Application이다. 비주얼 베이직 초보자를 위해 하나만 짚고 넘어가자면 컴포넌트 타입의 변수에 값을 대입할 때는 항상 앞에 Set을 써주어야 한다. 만든 컴포넌트를 제거하고자 할 경우에는 Nothing을 대입해주면 된다. 물론 맨 앞에는 Set을 써주어야 한다. Set을 사용하는 것을 빼먹으면 실행 중에 에러가 발생한다. 다시 코드 설명으로 넘어가서 그 다음부터는 익스플로러의 프로퍼티와 메소드를 이용하여 원하는 일을 수행하는 것이다. 주소 표시줄을 안 보이게 만들고(ie.AddressBar = False) 메뉴바를 안 보이게 만들고(ie.MenuBar = False) 익스플로러를 보이게 만든다(ie.Visible = True). CreateObject로 만든 컴포넌트는 기본적으로 안 보이는 상태에 놓인다. Visible 프로퍼티를 True로 지정해주어야 보이게 된다. 마지막으로 마이크로소프트 웹 사이트에 연결하기 위해 Navigate라는 메소드를 이용한다. 첫 번째 인자로 연결하고자 하는 웹 사이트의 URL을 지정해준다. 간단히 살펴본 것처럼 익스플로러 자체도 일종의 컴포넌트이기 때문에 바깥에서 제어가 가능하다. 사실 익스플로러 뿐만 아니라 마이크로소프트 워드, 파워포인트, 엑셀 등의 제품도 이렇게 메소드와 프로퍼티를 갖고 있기 때문에 바깥에서 제어가 가능하다. 여러분들도 이런 기능을 갖는 응용프로그램을 쉽게 만들 수 있다. MFC에서라면 AppWizard의 ActiveX 관련 화면에서 Automation 박스를 선택해주면 된다. 그리고 나서 도큐먼트 클래스에 ActiveX 컨트롤에서 하듯이 프로퍼티와 메소드를 정의해주면 된다.

    익스플로러는 만능 문서 뷰어

    익스플로러는 액티브X 도큐먼트 컨테이너이기도 하기 때문에 액티브X 도큐먼트 서버로 만든 문서들을 볼 수 있도록 해준다. 예를 들어 워드나 파워포인트, 엑셀처럼 액티브X 도큐먼트 서버를 지원하는 프로그램들이 만들어낸 문서는 익스플로러 내에서 열어볼 수 있다. 예를 들어 파워포인트가 설치된 시스템에서 익스플로러로 어떤 웹 페이지를 보고 있다고 하자. 그 페이지의 내용은 다음과 같다.

    <HTML>
    <TITLE>액티브X 도큐먼트 테스트</TITLE>
    <BODY>
    <H3>파워포인트가 설치된 시스템이라면 아래의 눌러보세요를 클릭하세요.</H3>
    <A HREF="overview.ppt">눌러보세요~</A>
    <H3>아래는 아주 엉뚱한 확장자를 갖는 파일입니다. 아래의 눌러주세요도 클릭해보세요.
    <A HREF="test.zzz">눌러주세요~</A>
    </BODY>
    

    위의 웹 페이지에 써 있는 것처럼 이 시스템에 파워포인트가 설치되어 있다면 첫 번째 "눌러보세요" 링크를 클릭하면 웹 브라우저가 뜨면서 overview.ppt 파일을 보여주는데 익스플로러 자체가 파워포인트 뷰어로 변신한다. 다음 그림이 바로 그 예이다.


    < 그림 3. 액티브X 도큐먼트 컨테이너 - 익스플로러 >

    하지만 하단의 "눌러주세요"를 클릭해보면 전혀 다른 현상이 나타난다. 이 링크는 연결된 프로그램이 없는 ZZZ라는 확장자를 갖는다. (사실 이 파일은 아무 파일이나 확장자를 ZZZ을 갖도록 이름을 변경해주어 만든 것이다) 이런 파일을 왼쪽 마우스 버튼으로 클릭해보면 다음과 같이 다운로드할 것인지 아니면 실행할 것인지 묻는 다이얼로그가 뜬다.


    < 그림 4. 파일 실행 혹은 저장을 묻는 다이얼로그 >

    이와 같이 익스플로러는 단순한 웹 브라우저로서의 위치를 벗어나 통합 문서 뷰어로의 역할도 수행하고 있으며 윈도우 탐색기의 근간으로도 동작한다. 그 사실은 익스플로러의 주소 표시줄에 디렉토리 경로명을 적어보면 안다. 예를 들어 C:\라고 입력하고 Enter키를 쳐보기 바란다. 하단의 브라우저 창이 탐색기 창처럼 변하는 것을 볼 수 있을 것이다.액티브X 도큐먼트 서버로 동작하는 프로그램을 만드는 방법에 대해 보다 더 자세히 알고 싶다면 필자가 98년 7월호에 연재한 "액티브X 도큐먼트"라는 기사를 참고하기 바란다.

    2. 브라우저 익스텐션(Browser Extension)이란 ?

    앞에서 이미 익스플로러는 다재다능한 기능을 갖는다는 사실을 살펴보았다. 이번에는 익스플로러를 개발자들이 확장하여 사용할 수 있는 다른 방법들에 대해 알아보자.

    익스플로러는 개발자들이 자신을 확장하여 사용할 수 있는 많은 방법들을 제공해준다. 그 중의 하나가 바로 브라우저 익스텐션(Browser Extension)이라는 것이다. 이는 익스플로러 4.0부터 생긴 특징이며 이를 이용하면 브라우저의 좌측이나 하단에 익스플로러 바 (Explorer Bar)라는 것을 추가하거나 컨텍스트 메뉴에 새로운 항목들을 추가할 수도 있다. 익스플로러 5.0이 발표되면서 이 기능 이외에도 도구 메뉴에 메뉴를 추가하거나 도구 창에 툴밴드나 버튼을 추가하는 것이 가능해졌다. 다음 그림은 하단에 익스플로러 바를 추가한 예를 보여준다.


    < 그림 5. 익스플로러 바의 실행 예 >

    브라우저 익스텐션의 활용 예

    브라우저 익스텐션은 말 그대로 기존의 브라우저를 그대로 사용하면서 그것의 일부 기능을 확장하는 것이다. 익스플로러 바로 만들면 익스플로러와 같은 윈도우 공간에 놓이기 된다. 공간적으로만 같이 놓이는 것이 아니라 현재 브라우저에 보이는 웹 페이지의 내용을 접근할 수도 있다. 그렇기 때문에 이를 잘 활용하면 현재 웹 페이지에서 그림 링크만 찾아내어 따로 저장해준다든가 혹은 이 웹 페이지에서 링크하고 있는 다른 웹 페이지들이 몇 개나 있으며 그 중에 끊어진 링크는 몇 개나 되는지 등을 체크하는 유틸리티 등을 쉽게 만들 수 있다. 후자의 유틸리티는 웹 페이지의 신뢰성 등을 따지기 위해 많이 활용되고 있다.

    브라우저 익스텐션도 윈도우이기 때문에 그 위에서 무엇을 하건 그건 프로그래머의 자유이다. 그 위에 버튼을 두어 그걸 누르면 응용프로그램을 띄워도 되고 다른 윈도우를 생성해도 된다. 심지어 브라우저 익스텐션 위에 웹 브라우저 컨트롤을 올릴 수도 있다.

    브라우저 익스텐션의 종류

    브라우저 익스텐션에는 3가지 종류가 존재한다. 이 것들은 모두 COM 객체이며 총칭하여 밴드 객체(band object)라고 한다. 본 연재에서는 이 세 가지의 밴드 객체 중에서 익스플로러 바만을 살펴볼 것이다.

  • 익스플로러 바
  • 툴 밴드
  • 데스크 밴드

    익스플로러 바는 익스플로러 위에 올라가는 자식 윈도우에 해당한다. 익스플로러 바는 브라우저 창의 왼편에 놓일 수도 있고 하단에 놓일 수도 있다. 사실 익스플로러의 도구 창에서 목록보기나 채널이나 즐겨찾기 버튼 등을 누르면 왼편에 나타나는 창이 바로 익스플로러 바로 구현된 것이다. 현재 익스플로러에 설치된 익스플로러 바가 무엇이 있는지 알고 싶다면 보기 메뉴의 탐색 창 메뉴를 펼쳐보기 바란다. 그러면 설치된 익스플로러 바의 목록이 보일 것이다. 익스플로러 바는 인터넷 익스플로러의 보기 메뉴의 탐색 창에만 나타나는 것이 아니라 윈도우 탐색기의 보기 메뉴의 탐색 창에도 나타난다.

    툴밴드(Tool band)는 익스플로러 5.0부터 생겨났으며 익스플로러의 툴바를 포함하는 리바 (Rebar) 컨트롤 위에 올라간다.

    데스크 밴드(Desk band)는 인터넷 익스플로러와는 무관한 객체이며 데스크탑과 태스크바위에 위치한다. 데스크 밴드 객체를 실행하려면 태스크바를 오른쪽 클릭하고 도구 모음에서 원하는 데스크 밴드 객체를 선택하면 된다. 비주얼 스튜디오에서 예제 프로그램으로 제공해주는 BandObjs라는 프로그램에 있는 데스크 밴드의 실행 예는 그림 6과 같다.


    < 그림 6. 데스크 밴드의 실행 예 >

    밴드 객체 구현하기

    밴드 객체도 COM 객체라고 하였다. 모든 COM 객체는 인터페이스로 구성된다. 밴드 객체는 다음과 같은 인터페이스로 구성된다.

  • IDeskBand
  • IObjectWithSite
  • IPersistStream
  • IInputObject (옵션)
  • IContextMenu(옵션)

    위의 인터페이스 이외에도 IUnknown, IClassFactory 인터페이스를 기본적으로 지원되어야 한다. 다섯 개의 인터페이스 중에서 끝의 두 개의 인터페이스는 필요에 따라 구현해도 되고 하지 않아도 된다. 앞서 밴드 객체에는 3가지 종류가 있음을 살펴보았다. 밴드 객체의 구현 방법은 거의 동일하다. 앞의 다섯 가지 인터페이스를 구현해주면 된다. 다만 차이가 나는 부분은 레지스트리에 등록될 때 등록되는 카테고리가 다르다는 것이다. 툴밴드는 등록되는 카테고리가 없다. 수직 익스플로러 바는 CATID_InfoBand라는 카테고리에 등록이 되어야 하고 수평 익스플로러 바는 CATID_CommBand라는 카테고리에 등록이 되어야 한다. 데스크 밴드는 CATID_DeksBand 카테고리에 등록이 되어야 한다.


    참고 : 레지스트리 카테고리

    모든 COM 객체는 레지스트리에 등록되어야 한다. 이전에는 레지스트리에 그냥 등록되기만 하였는데 이를 분류하여 종류별로 나눠 등록하는 체제가 되면서 등장한 것이 바로 레지스트리 카테고리다. 이는 이 COM 객체가 어떤 놈인지를 나타내는 역할을 한다. 여기서도 해당 컴포넌트들이 무슨 역할을 하는 것인지를 알기 쉽도록 레지스트리의 미리 정해진 카테고리에 나눠 등록해주는 것이다. 밴드 객체에서 사용되는 카테고리 이외의 다른 카테고리의 예를 들자면 어떤 액티브X 컨트롤이 스크립트와 초기화에 안전한지 나타내는 카테고리도 있다.


    아마 이야기가 잘 진행되다가 갑자기 COM 객체와 인터페이스가 어쩌구 저쩌구 해서 당황한 독자 여러분들이 태반이라 생각된다. 사실 지금부터 하는 이야기는 어려운 이야기이다. 밴드 객체를 구현하려면 COM 인터페이스를 구현해야 하고 처음부터 다 구현한다면 이는 굉장히 어려운 작업이다. 왜냐하면 COM에 대한 충분히 이해하고 있어야 하며 그만큼 프로그래밍 실력도 있어야 하기 때문이다. 그렇기 때문에 본 연재의 예제 프로그램에서는 비주얼 스튜디오에서 제공되는 예제를 가져다가 우리 입맛에 맞게 슬쩍 조금 고쳐서 사용할 것이다. 일단 이렇게 해서 COM과 친해지는 것이 나중에 진짜로 COM 공부를 할 때 더 잘 이해가 될 것이다.


    참고 : COM 인터페이스

    COM은 마이크로소프트에서 만든 컴포넌트를 만들고 사용하기 위한 규칙이라고 할 수도 있다. 이를 어렵게 표현하면 객체 모델(Object Model)이라고 한다. COM과 쌍벽을 이루는 객체 모델로는 CORBA가 있다. COM의 컴포넌트는 인터페이스로 구성된다. 인터페이스는 관련있는 함수들의 집합이라고 생각하면 된다. 모든 COM 컴포넌트는 기본적으로 IUnknown과 IClassFactory 인터페이스를 가져야 한다. 그 다음으로는 필요에 따라 다른 인터페이스들을 구현해주어야 하는데 밴드 객체의 경우에는 위에서 이야기한 것과 같은 인터페이스들을 구현해주어야 한다. 이런 인터페이스들을 어떻게 구현하는가는 사실 굉장히 까다로운 이야기이다. 웬만한 내공으로 도전한다면 내상(?)을 입고 말 정도이다. 이에 관해 자세히 나온 책으로는 Inside OLE 같은 책이 있는데 너무 어렵고 내용이 많아서 베개로 쓰기 십상이다. COM 인터페이스의 구현 방법에 대해서는 나중에 따로 연재를 하도록 하겠다.

    일단은 COM 인터페이스를 어떻게 구현하는 것인지 이해하려고 하지 않는 것이 좋다. 너무 복잡하기 때문이다. COM 인터페이스를 구현하는 방법만 그것만으로도 몇 회 분의 연재가 필 요하다. 그렇기 때문에 본 예제 프로그램에서도 밴드 객체를 만드는 방법 자체에 대해서만 언급하도록 하겠다. COM에 대해 조금 더 맛을 보고 싶다면 프로그램 세계 98년 1,2월호에 연재된 필자의 "COM 프로그래밍 개론"과 "COM 객체 만들기"를 보기 바란다. 이 글도 COM에 대해 아주 자세히 다루지는 않지만 여러분의 이해를 돕는데 도움이 될 것이다.


    3. 브라우저 익스텐션 만들어보기

    본 예제 프로그램은 마이크로소프트에서 제공해주는 브라우저 익스텐션 관련 예제 프로그램인 BandObjs를 바탕으로 작성되었음을 미리 밝혀둔다.

    예제 프로그램 기능 설명

    본 기사에서 만들어볼 예제 브라우저 익스텐션의 이름은 GrabPicture로 하겠다. 이를 수직 익스플로러바로 구현하겠다. 현재 웹 브라우저가 보여주는 웹 페이지에서 정보를 추출하는 역할을 하는데 다음과 같은 일을 수행한다.

  • 현재 보이고 있는 페이지의 URL을 보인다.
  • 그림 태그 관련 정보만 빼내어 저장한다.
  • 링크 태그 관련 정보만 빼내어 저장한다.
  • 위의 그림/링크 태그 정보를 리스트 박스에 보여준다.

    아직 그림 자체를 저장하지는 못하고 그림의 위치와 이름만 저장한다. 그림을 저장할 수 있으려면 그림을 HTTP 프로토콜로 읽어올 수 있어야 하는데 이는 아직 우리의 내공(?)으로 할 수 있는 일이 아니다. 열심히 수련하면 연재 3회에서 이 일까지 수행할 수 있게 될 것이니 어떻게 하는지 알고 싶어도 꾹 참기 바란다. 정 못 참겠는 사람은 WinInet을 공부해보면 그 기능을 구현할 수 있을 것이다. 아무튼 여기서는 그림 자체를 저장하지는 못한다. 두 번째 기능도 마찬가지다. 이 웹 페이지에서 링크하는 다른 웹 페이지들로 무엇이 있는지 검사하여 그 링크들의 목록을 보여주기만 한다. 그 웹 페이지를 읽어오지는 못한다. 이 역시 3회에서 WinInet을 이용해 웹 페이지를 읽어오는 기능을 구현하게 되면 자연스럽게 확장가능하다. 다음 그림은 이 예제 프로그램의 실행 화면이다.


    < 그림 7. 예제 프로그램의 실행 화면 >

    첫 번째 버튼을 누르면 현재 웹 페이지의 URL이 무엇인지 보여준다. 두 번째 버튼을 누르면 현재 웹 페이지에서 A 태그 정보를 빼내서 보여준다. 세 번째 버튼을 누르면 IMG 태그 정보만 빼내서 보여준다.참고로 bandobjs는 비주얼 C++ 6.0에 딸려오는 예제 프로그램인데 MSDN CD에서 찾아볼 수 있다. 아니면 MSDN 웹 사이트에서 찾아볼 수 있으며 URL은 참고 문헌과 웹 사이트 2에 있다. 이 예제 프로그램은 C++과 API를 이용해서 구현되어 있다. C++를 사용하긴 했지만 MFC로 만들어진 프로그램이 아니라는 점에 다시 한번 주목하기 바란다. MFC나 ATL로 만들어진 COM 객체가 아니기 때문에 소스를 잘 분석해보면 COM 객체를 어떻게 만들 수 있는지 가장 로우 레벨에서 이해할 수 있는 좋은 예제이기도 하다.

    예제 구현 시작 - GUID의 변경

    BandObjs 예제에서 우리에게 필요한 것은 수직 익스플로러 바뿐이다. 다른 부분의 코드(수평 익스플로러 바나 데스크 밴드와 관련된 코드)는 필요 없다. 이를 일단 제거하겠다. 제거한 소스가 바로 frameband.zip이다. 다른 용도로 수직 익스플로러 바를 구현할 필요성이 있는 독자 분이 계시다면 이 소스에서 작업을 시작하기 바란다.

    작업은 frameband.zip에서부터 시작하였다. 제일 먼저 해야할 일은 밴드 객체에 다른 GUID를 지정해주는 일이다. GUID를 모르는 독자분을 위해 잠깐 GUID에 대해 설명하도록 하겠다. 모든 COM 객체는 자신을 구별하기 위한 유일한 GUID(128비트의 숫자)를 갖는다. 이는 밴드 객체들도 마찬가지이다. 만일 독자 여러분들이 필자가 제공하는 예제 프로그램을 조금 수정하여 그대로 사용할 경우 문제가 발생한다. 모두 동일한 GUID를 그대로 쓸 것이기 때문에 나중에 같은 GUID를 갖는 밴드 객체들이 우후죽순처럼 생겨나 충돌이 발생할 수 있기 때문이다. 따라서 진짜로 밴드 객체를 개발하여 배포할 생각을 갖고 있다면 다른 GUID를 만들어 사용해야 한다. GUID를 하나 만들려면 Guidgen.exe라는 유틸리티를 이용해야 한다. 이는 비주얼 스튜디오 디렉토리 밑의 Common\Tools 디렉토리에 존재한다. 이를 실행시킨 다음 New Guid 버튼을 누르고 Copy를 누르면 하나의 GUID를 생성하고 해당 정보를 클립보드에 넣는다. 예제 프로그램에서 GUID 정보는 guid.h 헤더 파일에 존재한다. guid.h를 편집기로 열고 클립보드에 넣어두었던 새로운 GUID를 붙여넣는다. 그리고 나서 기존의 GUID를 새로운 GUID로 변경해주면 된다.

    예제 프로그램의 구조

    앞서 이야기한 것처럼 이 프로그램은 C++로 작성되어 있지만 MFC나 ATL과 같은 C++ 클래스 라이브러리를 사용하지는 않는다. API를 이용해 나름대로 클래스를 구성하여 사용한다. 따라서 API 프로그래밍에 대한 지식도 어느 정도 있어야 예제 프로그램을 이해하는데 어려움이 적을 것이다.

    BandObjs.cpp - 밴드 객체는 DLL이다. DLL의 메인 루틴은 DllMain이 이 파일에 정의되어 있다. COM 관련 객체들은 반드시 갖고 있어야 하는 함수들(예를 들면 자신을 레지스트리에 등록해주는 DllRegisterServer)이 있는데 그 역시 이 파일에 정의되어 있다. 거의 그대로 변경없이 사용한다.

    ClsFact.cpp, ClsFact.h - 밴드 객체의 클래스 팩토리 인터페이스가 정의된 곳이다. 이 역시 그대로 사용한다.

    ExplrBar.cpp, ExplrBar.h - 바로 익스플로러바 객체가 정의된 CExplorerBar 클래스가 정의되고 구현된 파일들이다. 예제 프로그램의 소스에서 가장 중요하다. 설명 역시 이 파일을 대상으로 한다.

    Guid.h - 이 밴드 객체의 GUID가 정의된 파일이다.

    CExplorerBar 클래스의 설명

    앞서 밴드 객체도 결국은 윈도우라고 하였다. 그렇기 때문에 그림 7에서보는 것처럼 버튼 등의 자식 윈도우를 얼마든지 추가할 수 있다. 익스플로러가 밴드 객체를 실행할 때 제일 먼저 호출하는 함수는 SetSite 함수이다. 이 함수에서 나중에 HTML 소스를 얻는데 사용할 IID_IWebBrowser2 인터페이스의 포인터를 얻어서 m_pIWeb이란 멤버 변수에 저장해둔다. 그리고 익스플로러 바의 영역 크기에 맞게 윈도우를 하나 만든다. 자 이제 관련된 소스를 보자.

    STDMETHODIMP CExplorerBar::SetSite(IUnknown* punkSite)
    {
        .....
        m_hwndParent = NULL;
        if(SUCCEEDED(punkSite->QueryInterface(IID_IOleWindow, (LPVOID*)&pOleWindow)))
        {
            // 익스플로러바의 부모 윈도우의 핸들을 알아낸다. -> m_hwndParent에 저장
            pOleWindow->GetWindow(&m_hwndParent);
            pOleWindow->Release();
        }
    
        // IID_IWebBrowser2 인터페이스를 얻기 위해 먼저 IID_IServiceProvider 인터페이스를
        // 얻는다.
        IServiceProvider *spSrvProv;
        if (punkSite->QueryInterface(IID_IServiceProvider, (void**)&spSrvProv)!= S_OK)
        {
            return E_FAIL;
        }
        // IID_IServiceProvider 인터페이스로부터 IID_IWebBrowser2 인터페이스를 얻는다.
        if (spSrvProv->QueryService(SID_SWebBrowserApp, IID_IWebBrowser2,
                                 (LPVOID*)&m_pIWeb) != S_OK)
        {
            spSrvProv->Release();
            return E_FAIL;
        }
        spSrvProv->Release();
        ....
        // 익스플로러 바에게 할당된 영역에 띄울 윈도우를 등록하고 생성한다.
        if(!RegisterAndCreateWindow())
            return E_FAIL;
        ....
    

    RegisterAndCreateWindow 멤버 함수에 가보면 API 프로그래밍으로 윈도우를 만들 때 볼 수 있는 전형적인 코드로 구성되어 있다. 익스플로러 바에게 할당된 영역의 크기를 알아내 윈도우를 등록하고 하나 생성한다. 생성할 윈도우의 영역은 m_hwndParent라는 윈도우 핸들이 가리키는 영역이 되는데 이 값은 앞서 SetSite에서 IID_IOleWindow 인터페이스의 GetWindow 메소드를 이용해 얻어두었다. 이 함수의 소스는 다음과 같다.

    BOOL CExplorerBar::RegisterAndCreateWindow(void)
    {
        ....
        // 윈도우 클래스를 등록한다.
        WNDCLASS wc;
        if(!GetClassInfo(g_hInst, EB_CLASS_NAME, &wc))
        {
            ZeroMemory(&wc, sizeof(wc));
            wc.style = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
            wc.lpfnWndProc = (WNDPROC)WndProc;
            wc.cbClsExtra = 0;
            ...
            wc.lpszClassName  = EB_CLASS_NAME;
          
            if(!RegisterClass(&wc))
            {
                return FALSE;
            }
        }
    
        // 익스플로러에게 할당된 영역의 크기를 알아낸다. 이는 m_hwndParent가
        // 가리키는 윈도우의 영역이 된다. 이 값은 앞서 SetSite에서 얻는다.
        RECT  rc;
        GetClientRect(m_hwndParent, &rc);
    
        // 윈도우를 생성한다.
        CreateWindowEx(0, EB_CLASS_NAME, NULL, 
                        WS_CHILD | WS_CLIPSIBLINGS | WS_BORDER,
                        rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
                        m_hwndParent, NULL, g_hInst, (LPVOID)this);
        ......
    

    위의 코드에서 주의해서 볼 부분은 CreateWindowEx API로 윈도우를 생성할 때 마지막 인자로 현재 CExplorerBar 클래스의 객체를 가리키는 this 포인터를 준 점이다. 왜 이렇게 했는지는 바로 뒤에서 다시 설명하겠다.

    이렇게 생성한 윈도우도 윈도우이므로 자신에게 발생하는 메시지를 처리하는 윈도우 프로시저를 갖는다. 이 윈도우의 윈도우 프로시저 이름은 WndProc이다. 소스는 다음과 같이 발생하는 메시지별로 처리 함수를 불러주는 간단한 구조로 되어있다. 참고로 WndProc 함수는 CExplorerBar 클래스의 정적 멤버 함수(static member function)로 선언되어 있기 때문에 직접 CExplorerBar 클래스의 데이터 멤버들을 접근할 수 없다. 그래서 아래의 소스에서 편법을 써서 CExplorerBar 클래스의 데이터 멤버를 접근한다. 사실 제일 간단하고 무식한 방법은 CExplorerBar 타입의 포인터 변수를 전역 변수로 선언해두고 이를 WndProc 함수내에서 이 전역 변수를 사용하면 된다. 하지만 이는 길거리 프로그래밍이라고 할 수밖에 없다. 만일 이 윈도우가 여러 개 생성되어 동시에 동작한다면 전역 변수의 값이 깨져 버릴 것이다. 더 좋은 방법은 바로 이제부터 살펴볼 방법이다.

    앞서 CreateWindowEx로 윈도우를 생성할 때 CExplorerBar 클래스의 객체에 대한 포인터를 마지막 인자로 지정했었는데 그 값이 WM_NCCREATE 메시지의 lParam 데이터로 넘어오게 되어 있다. WM_NCCREATE 메시지는 윈도우가 처음 생성될 때 발생하는 메시지로 WM_CREATE 보다 먼저 발생한다. 즉 거의 제일 먼저 발생하는 메시지라고 할 수 있다. WM_NCCREATE 메시지가 올 때의 코드 처리를 보면 lParam 인자로 넘어온 데이터를 윈도우 엑스트라 바이트의 사용자 정의 영역에 저장해두고 있다. 그리고나서 매번 윈도우 메시지가 올 때마다 이 값을 꺼내서 pThis 변수에 저장해두고 윈도우 프로시저내에서 사용한다.

    CExplorerBar  *pThis = (CExplorerBar*)GetWindowLong(hWnd, GWL_USERDATA);
    

    윈도우 엑스트라 바이트라는 것에 대해 이야기했는데 이는 생성된 윈도우마다 할당되는 고유의 데이터 영역이라고 생각하면 된다. 이 영역에는 윈도우 프로시저의 메모리 주소, 윈도우의 스타일 등의 데이터가 저장되는데 사용자 정의 용도로 쓰도록 4바이트의 영역이 할당되어 있다. 여기서는 바로 이 영역에 CExplorerBar 객체의 포인터를 저장해둔 것이다. 만일 4바이트 이상의 영역이 필요하다면 앞서 윈도우 클래스를 등록할 때 wc.cbWndExtra에 원하는 영역의 크기를 지정해주면 된다. 엑스트라 바이트내의 데이터를 읽는데는 GetWindowLong API를 사용하고 쓰는데는 SetWindowLong API를 사용한다. 엑스트라 바이트내의 잡은 데이터는 윈도우마다 별도로 할당되어 사용되기 때문에 앞서 전역 변수에서처럼 동시에 같은 윈도우가 여러 개 동작해도 깨지거나 할 위험이 없다.

    LRESULT CALLBACK   CExplorerBar::WndProc(HWND hWnd,  UINT  uMessage,  WPARAM  wParam, 
    LPARAM lParam)
    {
        // 이 윈도우를 생성한 CExplorerBar 객체에 대한 포인터를 얻는다. 
        CExplorerBar  *pThis = (CExplorerBar*)GetWindowLong(hWnd, GWL_USERDATA);
    
        switch (uMessage)
        {
            case WM_NCCREATE:
            {
                // CreateWindowEx로 윈도우 생성시 마지막 인자로 주었던 CExplorerBar 객체
                // 에 대한 포인터를 받아다가 윈도우 엑스트라 바이트 데이터로 저장해둔다.
                LPCREATESTRUCT lpcs = (LPCREATESTRUCT)lParam;
                pThis = (CExplorerBar*)(lpcs->lpCreateParams);
                SetWindowLong(hWnd, GWL_USERDATA, (LONG)pThis);
    
                //set the window handle
                pThis->m_hWnd = hWnd;
            }
            break;
            case WM_CREATE : // 이 윈도우 위에 버튼을 생성한다.
                return pThis->OnCreate();
            case WM_DESTROY :
                return pThis->OnDestroy();
            case WM_PAINT:
                return pThis->OnPaint();
            case WM_COMMAND: // 눌린 버튼을 처리한다.
                return pThis->OnCommand(wParam, lParam);
            case WM_SETFOCUS:
                return pThis->OnSetFocus();
            case WM_KILLFOCUS:
                return pThis->OnKillFocus();
        }
        return DefWindowProc(hWnd, uMessage, wParam, lParam);
    }
    

    위의 윈도우 프로시저에서 눈여겨볼 메시지는 두 개 정도이다. WM_CREATE와 WM_COMMAND인데 먼저 WM_CREATE에서는 이 윈도우 위에 버튼을 3개 만들고 하단의 남는 영역에 리스트 박스를 만드는 일을 한다. 3개의 버튼의 기능에 대해서는 이미 ""예제 프로그램 기능 설명"에서 언급한 바 있다. WM_COMMAND 메시지에서는 이렇게 만들어진 버튼이 눌렸을 때 해당 기능을 수행한다. 하단의 리스트 박스로는 찾아진 링크 태그나 이미지 태그의 내용을 출력한다. WM_CREATE 메시지가 발생하면 CExplorerBar의 OnCreate 멤버 함수가 호출된다. 소스는 다음과 같다.

    LRESULT CExplorerBar::OnCreate()
    {
        // 세 개의 버튼을 붙인다.
        m_hButton1 = CreateWindow("BUTTON", "URL 보기", 
                         WS_CHILD | BS_PUSHBUTTON | WS_VISIBLE, 
                         BUTTON_START, 5, BUTTON_WIDTH, BUTTON_HEIGHT, 
                         m_hWnd, (HMENU)100, g_hInst, NULL);
        m_hButton2 = CreateWindow("BUTTON", "IMG 태그 빼내기", 
                         WS_CHILD | BS_PUSHBUTTON | WS_VISIBLE, 
                         BUTTON_START,5+(BUTTON_HEIGHT+15),BUTTON_WIDTH, 
                         BUTTON_HEIGHT, m_hWnd, (HMENU)101, g_hInst, NULL);
        m_hButton3 = CreateWindow("BUTTON", "A 태그 빼내기", 
                         WS_CHILD | BS_PUSHBUTTON | WS_VISIBLE, 
                        BUTTON_START, 5+ (BUTTON_HEIGHT+15)*2,  BUTTON_WIDTH,
                        BUTTON_HEIGHT, m_hWnd, (HMENU)102, g_hInst, NULL);
    
        // 하단에 리스트 박스를 하나 붙인다.
        RECT rc;
        GetClientRect(m_hWnd, &rc);
        m_hListBox = CreateWindow("ListBox", "결과 보기", 
                       WS_CHILD | LBS_STANDARD | WS_VISIBLE, 
                       5, 5+(BUTTON_HEIGHT+15)*2+BUTTON_HEIGHT+5, 
                       rc.right-10,rc.bottom-(5+(BUTTON_HEIGHT+15)*2+BUTTON_HEIGHT+10),
                       m_hWnd, (HMENU)103, g_hInst, NULL);
    
        if (!m_hButton1 || !m_hButton2 || !m_hButton3 || !m_hListBox)
        {
            return -1;
        }
        return 0;
    }
    

    세 개의 버튼을 만드는데 CreateWindow API를 사용하고 있다(이런 걸 일일이 코딩하다보면 문득 비주얼 베이직이 얼마나 편한 개발 도구인지 새삼 깨닫게 될 것이다). 버튼 각각의 ID가 100, 101, 102로 주어진다는 점을 주의 깊게 보기 바란다. 버튼을 눌린 것을 WM_COMMAND 메시지에서 처리할 때 어느 버튼이 눌렸는지를 이 ID를 갖고 구별한다. 버튼 생성이 하나라도 실패하면 -1을 리턴한다. BUTTON_로 시작하는 값들은 ExplrBar.cpp의 선두에 상수로 정의된 상수들이다.

    다음은 WM_COMMAND 메시지가 발생하면 이를 처리하는 OnCommand 함수의 소스이다.

    LRESULT CExplorerBar::OnCommand(WPARAM wParam, LPARAM lParam)
    {
        if (wParam == 100)  // URL 보기 버튼이 눌렸을 때
        {
            BSTR url;
            char urlName[128];
    
            if (m_pIWeb->get_LocationURL(&url) == S_OK)
            {
                // url은 유니코드 타입이기 때문에 이를 ANSI 문자열로 변경한다. 
                ::WideCharToMultiByte(CP_ACP, 0, url, -1, urlName, 128, NULL, NULL);
                SysFreeString(url);
            }
            // 찾아낸 URL을 메시지 박스로 보여준다.
            MessageBox(m_hWnd, urlName, "URL", MB_OK);
        }
        else if (wParam == 101 || wParam == 102)
        {
            // m_lpContent 변수로 HTML의 내용을 읽어온다.
            if (GetHTML())
            {
                // 리스트 박스의 내용을 모두 비운다.
                SendMessage(m_hListBox, LB_RESETCONTENT, 0, 0);
                if (wParam == 102) // A 태그 빼내기
                {
                    LPSTR lpStr = m_lpContent;
                                      
                    while((lpStr = StrStrI(lpStr, "<A")) != NULL)
                    {
                        // lpStr부터 >가 나올 때까지가 링크되는 URL이다.
                        LPSTR lpStrEnd = StrStrI(lpStr, ">");
                        if (lpStrEnd)
                        {
                            // lpStr+2서부터 lpStrEnd-1까지가 링크 정보이다.
                            *lpStrEnd = NULL;
                            SendMessage(m_hListBox, LB_INSERTSTRING, -1,
                                        (LPARAM)(LPSTR)(lpStr+2));
                            lpStr = lpStrEnd + 4;
                        }
                        else
                            break;
                    }
                }   
                else if (wParam == 101) // IMG 태그 빼내기
                {
                    LPSTR lpStr = m_lpContent;
                                      
                    while((lpStr = StrStrI(lpStr, "<IMG")) != NULL)
                    {
                        // lpStr부터 </A>가 나올 때까지가 링크되는 URL이다.
                        LPSTR lpStrEnd = StrStrI(lpStr, ">");
    
                        if (lpStrEnd)
                        {
                            // lpStr+4서부터 lpStrEnd-1까지가 링크 정보이다.
                            *lpStrEnd = NULL;
                            SendMessage(m_hListBox, LB_INSERTSTRING, -1,
                                         (LPARAM)(LPSTR)(lpStr+4));
                            lpStr = lpStrEnd + 4;
                        }
                        else
                            break;
                    }
                }
            }
        }
        return 0;
    }
    

    위의 코드는 자세히 보면 별로 어려운 코드는 아니다. 먼저 현재 보이는 웹 페이지의 URL을 알아내기 위해서 IID_IWebBrowser2 인터페이스의 get_LocationURL 메소드를 이용해 현재 페이지의 URL을 얻어낸다. 그런데 이 때 넘어오는 값은 유니코드이기 때문에 이를 ANSI 문자열로 바꾸어 주어야 한다. 이 때 WideCharToMultiByte API를 호출한다. 만일 이 프로그램을 유니코드로 컴파일하면 이렇게 변환해주는 과정을 생략해도 된다. 하지만 유니코드로 컴파일한 프로그램은 윈도우 NT나 2000에서만 동작한다. 현재 페이지의 HTML 소스를 가져오는 함수가 바로 GetHTML이란 함수이다. 이 함수는 현재 페이지의 HTML 소스에서 태그의 내용만 CExplorerBar의 m_lpContent라는 변수에 저장해둔다. 원하는 태그를 찾기 위해서는 m_lpContent와

    GetHTML의 소스는 다음과 같다. 이 역시 앞서 얻어두었던 COM 인터페이스의 포인터를 통해 태그 정보를 얻어내는 역할을 한다.

    BOOL CExplorerBar::GetHTML()
    {
        IHTMLDocument2 *pHTML;
        IDispatch *pDisp;
    
        // IDispatch 인터페이스를 통해 IHTMLDocument2 인터페이스에 대한 포인터를 얻는다.
        if (m_pIWeb->get_Document((IDispatch **)&pDisp)!= S_OK)
        {
            return FALSE;
        }
        if (pDisp->QueryInterface(IID_IHTMLDocument2, (LPVOID*)&pHTML) != S_OK)
        {
            pDisp->Release();
            return FALSE;
        }
    
        // HTML의 body 태그 값을 읽어온다.
        IHTMLElement  *pBody;
        pHTML->get_body(&pBody);
    
        BSTR htmlText;
        // body 태그와 그 안에 포함된 텍스트 정보를 얻어온다.
        pBody->get_outerHTML(&htmlText);
    
        // body 태그와 포함 정보의 크기를 계산한다.
        int nLen = ::WideCharToMultiByte(CP_ACP, 0, htmlText, SysStringLen(htmlText), 
                                NULL, NULL, NULL, NULL);
        // 먼저 할당되었던 것이 있으면 제거한다.
        if (m_lpContent)
        {
            delete [] m_lpContent;
            m_lpContent = NULL;
        }
        // 필요한 만큼 메모리를 할당받는다.
        m_lpContent = new char [nLen+1];
        if (m_lpContent == NULL)
        {
            pBody->Release();
            pHTML->Release();
            pDisp->Release();
            return FALSE;
        }
        // Unicode로 들어가 있는 텍스트 정보를 ANSI 문자열로 변환한다.
        ::WideCharToMultiByte(CP_ACP, 0, htmlText, SysStringLen(htmlText), 
                          m_lpContent, nLen, NULL, NULL);
        SysFreeString(htmlText);
        m_lpContent[nLen] = 0;
    
        pBody->Release();
        pHTML->Release();
        pDisp->Release();
    
        return TRUE;
    }
    

    IHTMLDocument2 인터페이스를 이용해 HTML 소스를 얻는데 이것에도 한계는 있다. 바로 BODY 태그와 그 안의 정보만 접근할 수 있다는 것이다. <HTML>과 </HTML> 사이의 모든 정보를 다 접근할 수 있는 것은 아니다. 예를 들어 <TITLE>과 <TITLE> 태그 등의 <BODY> 태그 바깥에 놓인 정보들은 접근하지 못한다. 위의 코드에서도 <BODY> 태그와 그 안의 정보를 얻는데 유니코드로 얻어졌다는 점을 주의하기 바란다. 이를 ANSI 문자열로 변환하여 사용해야 한다. WideCharToMultiByte를 두 번 호출하여 한 번은 필요한 메모리의 크기를 알아내고 두 번째 호출 때 실제로 변환한다는 점은 기억해둘 필요가 있다.

    이 것으로 밴드 객체 예제 구현에 대한 설명을 마치겠다. 이해하기 쉽게 하려고 예제를 되도록이면 간단하게 만들려고 했다. 독자 여러분들의 확장 여하에 따라 쓸만한 용도로 사용 할 수 있을 것이다.

    이 번 기사에서는 익스플로러의 다양한 기능과 확장 방법에 대해 알아보았다. 다음 회에는 익스플로러에서 제공되는 웹 브라우저 컨트롤을 이용해서 커스텀 웹 브라우저를 만들어 보도록 하겠다. 사실 웹 브라우저 컨트롤을 이용해 커스텀 웹 브라우저를 만드는 방법은 이미 여러 번 소개된 바 있다. 하지만 지금까지 소개되었던 기사들과는 다른 관점에서 사용법을 언급하도록 하겠다.

    참고 문헌

    1. 마이크로소프트 웍샵의 브라우저 익스텐션 설명
    2. 비주얼 스튜디오의 브라우저 익스텐션 예제 프로그램
    3. IE 5.0을 다운로드받을 수 있는 곳
  • 댓글
    • No Nickname
      No Comment
    • 권한이 없습니다.
      {{m_row.m_nick}}
      -
    제목 작성자 날짜
    공대여자
    공대여자
    mins01
    공대여자
    공대여자
    공대여자
    공대여자
    공대여자
    mins