천객만래 [千客萬來] (It has an interminable succession of visitors)

[개발/VC++] ATL Window Class 종류



ATL 은 COM을 지원하기 위해 디자인 되었지만 윈도우를 모델링 하는 클래스 영역도 포함한다고

한다. 그리고 ActiveX 같은 윈도우를 가지는 객체도 만들수 있다.

아래는 ATL 에서의 주요 윈도우 클래스들이다.

 

 CWindow - 윈도우를 조작하기 위한 Win32 APIs의 작은 랩퍼 클래스이다.

                    윈도우 핸들과 HWND 를 CWindow 로 변환하는 오퍼레이터를 포함한다.

                    그러므로 윈도우 핸들을 필요로하는 어떤 함수에 CWindow 오브젝트를

                    넘길수 있다.

 CWindowImpl - 이미 존재하는 윈도우를 서브클래싱 하거나 이미 존재하는 클래스를

                    수퍼클래싱 하거나 , 윈도우 베이스의 새로운 윈도우를 만들때

                    사용한다.
 CContainedWindow - 다른 클래스의 메세지 맵을 위한 메세지 경로를 구현한 윈도우

                    클래스이다. 이 클래스는 하나의 클래스에 메세지 처리를 집중하는 것을 허락한다.

 CAxWindow - 컨트롤을 만들거나 존재 하는 컨트롤에 붙임으로써 ActiveX control

                    호스트 윈도우 구현을 지원한다. 
 CDialogImpl - 모달이나 모달리스 다이얼로스를 구현한다. IDOK 나 IDCANCEL 같은

                    기본 메세지 경로를 지원한다.
 CSimpleDialog - 단순 모달 다이얼로그를 주어진 리소스 ID로 구현한다. IDOK나 IDCANCEL과

                    같은 기본 메세지 맵을 기지고 있다.
CAxDialogImpl - CDialogImpl 과 같이 모달과 모달리스를 를 구현하는 베이스 클래스로 사용되

                    며 상속된 클래스에 기본 메세지 맵을 제공한다.
                    추가로 ActiveX 컨트롤을 지원한다. ATL 오브젝트 위저드에서 CAxDialogImpl에

                    상속된 클래스를 프로젝트에 넣은 것을 지원한다.

CWndClassInfo -  새로운 윈도우 클래스의 정보를 보관한다.

                    특별히 WNDCLASSEX를 캡슐화한다.

CWndTraits and CWinTraitsOR - ATL 윈도우 오브젝트의 스타일을 캡슐화한다





Posted by SB패밀리
[개발/VC++] ATL CImage 클래스를 이용한 화면 크기와 일치하는 캡쳐하기 함수입니다.

// ATL CImage Class를 이용한 Screen Capture Source
#include <atlimage.h>
 
void CaptureWindow()
{
int nx =0, ny = 0;
CImage cImg;
CWnd *pDesktopWnd = GetDesktopWindow(); // 바탕화면 윈도우 인스턴스
HDC hDC = NULL;
if(!pDesktopWnd)
return;
CWindowDC DeskTopDC(pDesktopWnd); // get HDC
nx = GetSystemMetrics(SM_CXSCREEN);
ny = GetSystemMetrics(SM_CYSCREEN);
if(!cImg.Create(nx, ny, 32))
return;
hDC = cImg.GetDC(); // CImage HDC 생성
BitBlt(hDC, 0,0, nx, ny, DeskTopDC.m_hDC, 0, 0, SRCCOPY); // 화면 복사
 
cImg.Save(_T("test.jpg"), Gdiplus::ImageFormatJPEG);
//cImg.Save(_T("test.jpg"));
cImg.ReleaseDC();
}

 
Posted by SB패밀리
쌈꼬쪼려 소백촌닭

VS 2008에서 WTL 8.0 Project Wizard 설치하는 방법 ( 비주얼 스튜디오 2008에 WTL 8.0 설치하는 방법)
How can I install WTL 8.0 Project Wizards in VS 2008?


비주얼 스튜디오 2008에 WTL 8.0 설치하는 방법을 알아보자.

아마 이 게시물을 보게 된 이유는
fatal error C1083: 포함 파일을 열 수 없습니다. 'atlapp.h': No such file or directory

와 같은 비주얼 스튜디오 C++ 컴파일 에러 때문에 검색해서 오게 되었을 가능성이 높다고 생각이든다.
atlapp.h는 ATL이 아니라 WTL이기 때문이다.
그래서 WTL 8.0을 설치해주어야 한다.

1 WTL 8.0을 찾아서 다운로드 받기

WTL 8.0 을 다운로드 받는 링크
http://www.microsoft.com/downloads/...E5BA5BA4-6E6B-462A-B24C-61115E846F0C] WTL 8.0

위의 링크에서 WTL 8.0을 다운로드 받는다.
WTL(Windows Template Library) 8.0을 다운로드 받아서 WTL80.exe 파일을 실행해서 적당한 위치 'C:\WTL80'이나 'C:\Program Files\WTL80'이에 압축을 풀어서 복사한다.

WTL 8.0 내부 폴더와 파일 구성은 위와 같다.

2 WTL 8.0 설치를 위한 수정 작업

VS2005까지는 WTL이 정식지원된다고 하지만 VS2008부터는 설치를 별도로 해야하는데
설치를 위해서 수정해 주어야 할 파일이 있다.



WTL80\AppWiz 폴더 안에 setup80.js 파일이 있다.
우리는 이 파일을 수정하게 될 것이다.
setup80.js 설치파일은 VS2005까지 지원을 한다. 그래서 편집을 통해서 VS2008을 지원할 수 있도록 할 것이다.



위 그림은 setup80.js 파일 내용이다. 72 라인과 80 라인의 내용이 원래 내용이다. 이것을 주석처리하고
73라인과 81라인으로 변경한다. 즉, 버전을 수정해 주는 것이다.
이렇게 해주지 않으면 설치가 되지 않는다.

3 WTL 8.0 Project Wizards를 설치

탐색기에서 setup80.js를 더블클릭해서 실행하도록 한다.
아래 그림과 같은 창이 나타나면 설치 성공이다.




4 WTL 8.0 경로 설정 

이제 Visual Studio 2008에서 header 파일 include를 설정해 주어야 한다.
비주얼 스튜디오 2008의 메뉴를 선택한다.
[메뉴]-[도구]-[옵션] 을 선택한다.



나타나는 옵션 설정 창에서 프로젝트 및 솔루션-> VC++ 디렉터리 설정을 클릭한다.
오른쪽에는 include된 목록이 나오는데 여기 '다음 파일의 디렉터리 표시'에서 '포함 파일'을 선택한다.



폴더 버튼을 클릭해서 새로운 include 폴더를 추가해야한다.
WTL80을 복사한 'C:\WTL80\include' 폴더를 선택하도록 한다.

이제 모든 작업이 완료되었다.

비주얼 스튜디오에서 프로젝트를 다시 컴파일 해 보도록 하자. 에러가 발생하지 않는다.






Posted by SB패밀리

Building Browser Helper Objects with Visual Studio 2005

Tony Schreiner, John Sudds
Microsoft Corporation

October 27, 2006

Summary: This article demonstrates how to use Microsoft Visual Studio 2005 to create a simple Browser Helper Object (BHO), a Component Object Model (COM) object that implements the IObjectWithSite interface and attaches itself to Internet Explorer. This article describes how to create an entry-level BHO step-by-step. At first, the BHO displays a message that reads "Hello World!" as Internet Explorer loads a document. Then, the BHO is extended to remove images from the loaded page. This article is written for developers who want to learn how to extend the functionality of the browser and to create Web developer tools for Internet Explorer. (8 printed pages)

Contents

Introduction
Overview
Setting up the Project
Implementing the Basics
Responding to Events
Manipulating the DOM
Summary
Related Topics

Introduction

This article relies on Microsoft Visual Studio 2005 and Active Template Library (ATL) to develop a BHO using C++. We decided to use ATL because it conveniently implements a basic boilerplate that we can extend for our needs. There are other ways to create a BHO, such as using Microsoft Foundation Classes (MFC) or the Win32 API and COM, but ATL is a lightweight library that automatically handles a lot of the details for us, including setting up the registry with the BHO class identifier (CLSID).

Another strength of ATL is its COM-aware smart pointer classes (such as CComPtr and CComBSTR) that manage the lifetime of COM objects. For example, CComPtr calls AddRef as a value is assigned, and calls Release as the object is destroyed or goes out of scope. Smart pointers simplify the code and help eliminate memory leaks. Their stability and reliability are especially useful when used within the scope of a single method.

The first part of this article walks you through the process of implementing a simple BHO and verifying that it is loaded by Internet Explorer. The next part demonstrates how to connect the BHO to browser events, and the final part shows a simple interaction with the DHTML Document Object Model (DOM) that changes the appearance of a Web page.

Overview

What exactly is a Browser Helper Object (BHO)? In a nutshell, a BHO is a lightweight DLL extension that adds custom functionality to Internet Explorer. Although it is less common and not the focus of this article, BHOs can also add functionality to the Windows Explorer shell.

BHOs typically do not provide any user interface (UI) of their own. Rather, they function in the background by responding to browser events and user input. For example, BHOs can block pop-ups, auto-fill forms, or add support for mouse gestures. It is a common misconception that BHOs are required by toolbar extensions; however, BHOs used in conjunction with toolbars can provide an even richer user experience.

Note  BHOs are convenient tools for end users and developers alike; however, because BHOs are granted considerable power over the browser and Web content, and because they often go undetected, users should take great care to obtain and install BHOs from reliable sources.

The lifetime of a BHO is the same as the lifetime of the browser instance that it interacts with. In Internet Explorer 6 and earlier, this means that a new BHO is created (and destroyed) for each new top-level window. On the other hand, Internet Explorer 7 creates and destroys a new BHO for each tab. BHOs are not loaded by other applications that host the WebBrowser control or by windows such as HTML dialog boxes.

The primary requirement of a BHO is to implement the IObjectWithSite interface. This interface exposes a method, SetSite, that facilitates the initial communication with Internet Explorer and notifies the BHO when it is about to be released. We create a simple browser extension by implementing this interface, and then adding the CLSID of the BHO into the registry.

Let's get started.

Setting up the Project

To create a BHO project with Microsoft Visual Studio 2005:

  1. On the File menu, click New Project....
    The New Project dialog box appears. This dialog box lists the application types that Visual Studio can create.
  2. Under the Visual C++ node, select "ATL" if it is not already selected, then select "ATL Project" from the Visual C++ project types. Name the project "HelloWorld" and use the default location. Click OK.
  3. In the ATL Project Wizard, ensure that the server type is "Dynamic-link library (DLL)" and click Finish.

At this point, Visual Studio has created boilerplate for a DLL. We now add the COM object that implements the BHO.

  1. In the Solution Explorer panel, right-click on the project and select Class... from the Add submenu.
  2. Select "ATL Simple Object" and click Add. The ATL Simple Object Wizard appears.
  3. In Names of the ATL Simple Object Wizard, type "HelloWorldBHO" as a Short Name.The remaining names are filled in automatically.
  4. In Options of the ATL Simple Object Wizard, select "Apartment" for Threading Model, "No" for Aggregation, "Dual" for Interface, and "IObjectWithSite" for Support. ATL Simple Object Wizard Options
  5. Click Finish.

The following files are created as part of this project.

  • HelloWorldBHO.h – this header file contains the class definition for the BHO.
  • HelloWorldBHO.cpp – this source file is the main file for the project and contains the COM object.
  • HelloWorld.cpp – this source file implements the exports that expose the COM object through the DLL.
  • HelloWorld.idl – this source file can be used to define custom COM interfaces. For this article, we will not change this file.
  • HelloWorld.rgs – this resource file contains the registry keys that are written and removed when the DLL is registered and unregistered.

Implementing the Basics

The ATL Project Wizard provides a default implementation of SetSite. Although the interface contract of IObjectWithSite implies that this method may be called again and again as necessary, Internet Explorer invokes this method exactly twice; once to establish a connection, and again as the browser is exiting. Specifically, the SetSite implementation in our BHO performs the following actions:

  • Stores a reference to the site. During initialization, the browser passes a IUnknown pointer to the top-level WebBrowser Control, and the BHO stores a reference to it in a private member variable.
  • Releases the site pointer currently being held. When Internet Explorer passes NULL, the BHO must release all interface references and disconnect from the browser.

As part of the processing of SetSite, the BHO should perform other initialization and uninitialization as required. For example, you can establish a connection point to the browser in order to receive browser events.

HelloWorldBHO.h

Double-click to open HelloWorldBHO.h from the Visual Studio Solution Explorer.

First, include shlguid.h. This file defines interface identifiers for IWebBrowser2 and the events that are used later in the project.

#include <shlguid.h>     // IID_IWebBrowser2, DIID_DWebBrowserEvents2, etc.

Next, in a public section of the CHelloWorldBHO class, declare SetSite.

STDMETHOD(SetSite)(IUnknown *pUnkSite);

The STDMETHOD macro is an ATL convention that marks the method as virtual and ensures that it has the right calling convention for the public COM interface. It helps to demarcate COM interfaces from other public methods that may exist on the class. The STDMETHODIMP macro is likewise used when implementing the member method.

Finally, in a private section of the class declaration, declare a member variable to store the browser site.

private:
    CComPtr<IWebBrowser2>  m_spWebBrowser;

HelloWorldBHO.cpp

Switch now to HelloWorldBHO.cpp and insert the following code for SetSite.

STDMETHODIMP CHelloWorldBHO::SetSite(IUnknown* pUnkSite)
{
    if (pUnkSite != NULL)
    {
        // Cache the pointer to IWebBrowser2.
        pUnkSite->QueryInterface(IID_IWebBrowser2, (void**)&m_spWebBrowser);
    }
    else
    {
        // Release cached pointers and other resources here.
        m_spWebBrowser.Release();
    }

    // Return the base class implementation
    return IObjectWithSiteImpl<CHelloWorldBHO>::SetSite(pUnkSite);
}

During initialization, the browser passes a reference to its top-level IWebBrowser2 interface, which we cache. During uninitialization, the browser passes NULL. To avoid memory leaks and circular reference counts, it's important to release all pointers and resources at that time. Finally, we call the base class implementation so that it can fulfill the rest of the interface contract.

HelloWorld.cpp

When a DLL is loaded, the system calls the DllMain function with a DLL_PROCESS_ATTACH notification. Because Internet Explorer makes extensive use of multi-threading, frequent DLL_THREAD_ATTACH and DLL_THREAD_DETACH notifications to DllMain can slow the overall performance of the extension and the browser process. Since this BHO does not require thread-level tracking, we can call DisableThreadLibraryCalls during the DLL_PROCESS_ATTACH notification to avoid the overhead of new thread notifications.

In HelloWorld.cpp, code the DllMain function as follows:

extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
    if (dwReason == DLL_PROCESS_ATTACH)
    {
        DisableThreadLibraryCalls(hInstance);
    }
    return _AtlModule.DllMain(dwReason, lpReserved); 
}

Register the BHO

All that remains is to add the CLSID of the BHO to the registry. This entry marks the DLL as a browser helper object and causes Internet Explorer to load the BHO at start-up. Visual Studio can register the CLSID when it builds the project.

Note  On Windows Vista, Visual Studio requires elevated privileges to interact with the registry. Make sure to start the development environment by right-clicking Microsoft Visual Studio 2005 in the Start menu and selecting Run as administrator.

The CLSID for this BHO is found in HelloWorld.idl, in a block of code similar to the following:

    importlib("stdole2.tlb");
    [
        uuid(D2F7E1E3-C9DC-4349-B72C-D5A708D6DD77),
        helpstring("HelloWorldBHO Class")
    ]

Note that this file contains three GUIDs; we need the CLSID for the class, not those of the library or interface ID.

To create a self-registering BHO:

  1. Open HelloWorld.rgs from the Solution Explorer in Visual Studio.
  2. Add the following code to the bottom of the file:
    HKLM {
      NoRemove SOFTWARE {
        NoRemove Microsoft {   
          NoRemove Windows {
            NoRemove CurrentVersion {
              NoRemove Explorer {
                NoRemove 'Browser Helper Objects' {
                  ForceRemove '{D2F7E1E3-C9DC-4349-B72C-D5A708D6DD77}' = s 'HelloWorldBHO' {
                    val 'NoExplorer' = d '1'
                  }
                }
              }
            }
          }
        }
      }
    }
    
  3. Replace the GUID that follows ForceRemove above with the CLSID of the BHO found in HelloWorld.idl.Do not replace the curly braces.
  4. Save the file, and rebuild the solution from the Build menu.Visual Studio registers the object automatically.

The NoRemove keyword indicates that the key should be not be deleted when the BHO is unregistered. Unless you specify this keyword, empty keys will be removed. The ForceRemove keyword indicates that the key and any values and sub-keys that it contains should be deleted. ForceRemove also causes the key to be recreated when the BHO is registered, if the key already exists.

Since this BHO is specifically designed for Internet Explorer, we specify the NoExplorer value to prevent Windows Explorer from loading it. Neither the value nor the type makes any difference—as long as the NoExplorer entry exists, Windows Explorer will not load the BHO.

If you haven't done so already, select Build Solution from the Build menu to build and register the BHO.

Take a Test Drive

For a quick test, set a breakpoint in SetSite and start the debugger by pressing F5. When the Executable for Debug Session dialog box appears, select the "Default Web Browser" and click OK. If Internet Explorer is not your default browser, you can browse for the executable.

Note   On Windows Vista, the Internet Explorer Protected Mode feature launches a separate process and exits, making it a little harder to debug. You can easily turn off Protected Mode for the current session in two ways: launch the browser from a administrative process (such as Visual Studio), or create a local HTML file and specify it as a command line parameter to Internet Explorer.

As the browser starts, it loads the DLL for the BHO. When the breakpoint is hit, note that the pUnkSite parameter is set. Press F5 again to continue loading the home page.

Close the browser to verify that SetSite is called again with NULL.

Responding to Events

Now that you've confirmed that Internet Explorer can load and run the BHO, let's take our example a little further by extending the BHO to react to browser events. In this section, we describe how to use ATL to implement an event handler for DocumentComplete that displays a message box after the page loads.

To be notified of events, the BHO establishes a connection point with the browser; to respond to these events, it implements IDispatch. According to the documentation for DocumentComplete, the event has two parameters: pDisp (a pointer to IDispatch) and pUrl. These parameters are passed to IDispatch::Invoke as part of the event; however, unpacking the event parameters by hand is a non-trivial and error-prone task. Fortunately, ATL provides a default implementation that helps to simplify the event-handling logic.

HelloWorldBHO.h

Start in HelloWorldBHO.h by including exdispid.h, which defines the dispatch IDs for browser events.

#include <exdispid.h> // DISPID_DOCUMENTCOMPLETE, etc.

Next, we indiciate that we want to handle events defined by the DWebBrowserEvents2 interface by adding a class definition for IDispEventImpl, which provides an easy and safe alternative to Invoke for handling events. IDispEventImpl works in conjunction with an event sink map to route events to the appropriate handler function.

class ATL_NO_VTABLE CHelloWorldBHO :
    . . . 
    public IDispEventImpl<1, CHelloWorldBHO, &DIID_DWebBrowserEvents2, &LIBID_SHDocVw, 1, 1>

Next, add ATL macros that route the event to a new OnDocumentComplete event handler method, which takes the same arguments, in the same order, as defined by the DocumentComplete event. Place the following code in a public section of the class.

BEGIN_SINK_MAP(CHelloWorldBHO)
    SINK_ENTRY_EX(1, DIID_DWebBrowserEvents2, DISPID_DOCUMENTCOMPLETE, OnDocumentComplete)
END_SINK_MAP()

    // DWebBrowserEvents2
    void STDMETHODCALLTYPE OnDocumentComplete(IDispatch *pDisp, VARIANT *pvarURL); 

The number supplied to the SINK_ENTRY_EX macro (1) refers to the first parameter of the IDispEventImpl class definition and is used to distinguish between events from different interfaces, if necessary. Also note that you cannot return a value from the event handler; that's OK because Internet Explorer ignores values returned from Invoke anyway.

Finally, add a private member variable to track whether the object has established a connection with the browser.

private:
    BOOL m_fAdvised; 

HelloWorldBHO.cpp

To connect the event handler to the browser through the event map, call DispEventAdvise during the processing of SetSite. Likewise, use DispEventUnadvise to break the connection.

Here is the new implementation of SetSite:

STDMETHODIMP CHelloWorldBHO::SetSite(IUnknown* pUnkSite)
{
    if (pUnkSite != NULL)
    {
        // Cache the pointer to IWebBrowser2.
        HRESULT hr = pUnkSite->QueryInterface(IID_IWebBrowser2, (void **)&m_spWebBrowser);
        if (SUCCEEDED(hr))
        {
            // Register to sink events from DWebBrowserEvents2.
            hr = DispEventAdvise(m_spWebBrowser);
            if (SUCCEEDED(hr))
            {
                m_fAdvised = TRUE;
            }
        }
    }
    else
    {
        // Unregister event sink.
        if (m_fAdvised)
        {
            DispEventUnadvise(m_spWebBrowser);
            m_fAdvised = FALSE;
        }

        // Release cached pointers and other resources here.
        m_spWebBrowser.Release();
    }

    // Call base class implementation.
    return IObjectWithSiteImpl<CHelloWorldBHO>::SetSite(pUnkSite);
}

Finally, add a simple OnDocumentComplete event handler.

void STDMETHODCALLTYPE CHelloWorldBHO::OnDocumentComplete(IDispatch *pDisp, VARIANT *pvarURL)
{
    // Retrieve the top-level window from the site.
    HWND hwnd;
    HRESULT hr = m_spWebBrowser->get_HWND((LONG_PTR*)&hwnd);
    if (SUCCEEDED(hr))
    {
        // Output a message box when page is loaded.
        MessageBox(hwnd, L"Hello World!", L"BHO", MB_OK);
    }
}

Notice that the message box uses the top-level window of the site as its parent window, rather than simply passing NULL in that parameter. In Internet Explorer 6, a NULL parent window does not block the application, meaning that the user can continue to interact with the browser while the message box is waiting for user input. In some situations, this can cause the browser to hang or crash. In the rare case that a BHO needs to display a UI, it should always ensure that the dialog box is application modal by specifying a handle to the parent window.

Another Test Drive

Start up Internet Explorer again by pressing F5. After the document has loaded, the BHO displays its message.

The "Hello World!" Message Box

Continue browsing to observe when and how often the message box appears. Notice that the BHO alert is shown not only when the page is loaded, but also when the page is reloaded by clicking the Back button; however, it does not appear when you click the Refresh button. The message box also appears for every new tab and every document loaded in a frame or iframe.

The event is fired after the page is downloaded and parsed, but before the window.onload event is triggered. In the case of multiple frames, the event is fired multiple times followed by the top-level frame at the end. In the code that follows, we detect the final event of a series by comparing the object passed in the pDisp parameter of the event to the top-level browser that was cached in SetSite.

Manipulating the DOM

The following JavaScript code demonstrates a basic manipulation of the DOM. It hides images on the Web page by setting the display attribute of the image's style object to "none."
function RemoveImages(doc)
{
    var images = doc.images;
    if (images != null)
    {
        for (var i = 0; i < images.length; i++) 
        {
            var img = images.item(i);
            img.style.display = "none";
        }
    }
}

In this final section, we show you how to implement this basic logic in C++.

HelloWorldBHO.h

First, open HelloWorldBHO.h and include mshtml.h. This header file defines the interfaces we need for working with the DOM.

#include <mshtml.h>         // DOM interfaces

Next, define the private member method to contain the C++ implementation of the JavaScript above.

private:
    void RemoveImages(IHTMLDocument2 *pDocument);

HelloWorldBHO.cpp

The OnDocumentComplete event handler now does two new things. First, it compares the cached WebBrowser pointer to the object for which the event is fired; if they are equal, the event is for the top-level window and the document is fully loaded. Second, it retrieves a pointer to the document object and passes it to RemoveImages.

void STDMETHODCALLTYPE CHelloWorldBHO::OnDocumentComplete(IDispatch *pDisp, VARIANT *pvarURL)
{
    HRESULT hr = S_OK;

    // Query for the IWebBrowser2 interface.
    CComQIPtr<IWebBrowser2> spTempWebBrowser = pDisp;

    // Is this event associated with the top-level browser?
    if (spTempWebBrowser && m_spWebBrowser &&
        m_spWebBrowser.IsEqualObject(spTempWebBrowser))
    {
        // Get the current document object from browser...
        CComPtr<IDispatch> spDispDoc;
        hr = m_spWebBrowser->get_Document(&spDispDoc);
        if (SUCCEEDED(hr))
        {
            // ...and query for an HTML document.
            CComQIPtr<IHTMLDocument2> spHTMLDoc = spDispDoc;
            if (spHTMLDoc != NULL)
            {
                // Finally, remove the images.
                RemoveImages(spHTMLDoc);
            }
        }
    }
}

The IDispatch pointer in pDisp contains the IWebBrowser2 interface of the window or frame in which the document has loaded. We store the value in a CComQIPtr class variable, which performs a QueryInterface automatically. Next, to determine if the page is completely loaded, we compare the interface pointer to the one we cached in SetSite for the top-level browser. As a result of this test, we only remove images from documents in the top-level browser frame; documents that do not load into the top-level frame do not pass this test. (For more information, see How To Determine When a Page Is Done Loading in WebBrowser Control and How to get the WebBrowser object model of an HTML frame.)

It takes two steps to retrieve the HTML document object. Because get_Document retrieves a pointer for the active document even if the browser has hosted a document object of another type (such as a Microsoft Word document), we must further query the active document for an IHTMLDocument2 interface to determine if it is indeed an HTML page. The IHTMLDocument2 interface provides access to the contents of the DHTML DOM.

After confirming that an HTML document is loaded, we pass the value to RemoveImages. Note that the argument is passed as a pointer to IHTMLDocument2, not as a CComPtr.

void CHelloWorldBHO::RemoveImages(IHTMLDocument2* pDocument)
{
    CComPtr<IHTMLElementCollection> spImages;

    // Get the collection of images from the DOM.
    HRESULT hr = pDocument->get_images(&spImages);
    if (hr == S_OK && spImages != NULL)
    {
        // Get the number of images in the collection.
        long cImages = 0;
        hr = spImages->get_length(&cImages);
        if (hr == S_OK && cImages > 0)
        {
            for (int i = 0; i < cImages; i++)
            {
                CComVariant svarItemIndex(i);
                CComVariant svarEmpty;
                CComPtr<IDispatch> spdispImage;

                // Get the image out of the collection by index.
                hr = spImages->item(svarItemIndex, svarEmpty, &spdispImage);
                if (hr == S_OK && spdispImage != NULL)
                {
                    // First, query for the generic HTML element interface...
                    CComQIPtr<IHTMLElement> spElement = spdispImage;

                    if (spElement)
                    {
                        // ...then ask for the style interface.
                        CComPtr<IHTMLStyle> spStyle;
                        hr = spElement->get_style(&spStyle);

                        // Set display="none" to hide the image.
                        if (hr == S_OK && spStyle != NULL)
                        {
                            static const CComBSTR sbstrNone(L"none");
                            spStyle->put_display(sbstrNone);
                        }
                    }
                }
            }
        }
    }
}

Interacting with the DOM in C++ is more verbose than JavaScript, but the code flow is essentially the same.

The preceding code iterates over each item in the images collection. In script, it is clear whether the collection element is being accessed by ordinal or by name; however, in C++ you must manually disambiguate these arguments by passing an empty variant. We again rely on an ATL helper class—this time CComVariant—to minimize the amount of code that we have to write.

Final Notes

To facilitate scripting, all objects in the DOM use IDispatch to expose properties and methods that are derived from multiple interfaces. In C++, however, you must explicitly query for the interface that supports the property or method you want to use. For example, an image object supports both the IHTMLElement and IHTMLImgElement interfaces. Therefore, to retrieve a style object for an image, you first have to query for an IHTMLElement interface, which exposes the get_style method.

Also note that COM rules do not guarantee a valid pointer on failure; therefore, you need to check the HRESULT after every COM call. Moreover, for many DOM methods it is not an error to return a NULL value, so you need to be careful to check both the return value and the pointer value. To make the check even safer, always initialize the pointer to NULL beforehand. Adopting a defensive, verbose, and error-tolerant coding style can help to prevent unpredictable bugs later.

Summary

There are various types of BHOs with a wide range of purposes; however, all BHOs share one common feature: a connection to the browser. Because of their ability to tightly integrate with Internet Explorer, BHOs are valued by countless developers who want to extend the functionality of the browser. This article demonstrated how to create a simple BHO that modifies the style attributes of IMG elements in a loaded document. We invite you to extend this entry-level example as you like. You can further explore the possibilities by visiting the following links.

Related Topics


출처 : http://msdn.microsoft.com/en-us/library/bb250489(VS.85).aspx

Posted by SB패밀리
COM/ATL/STL - BSTR과 VARIANT, string, CComBSTR

COM 관련 코드를 작성하려고 보면 여기서만 쓰이는 생소한 데이터 형들이 등장하는데, 그중 문자열관련해서 다음과 같은 것을
볼 수 있다.


BSTR
Pascal-Style(길이값 내장)과 C-Style(널종료문자)을 섞어 놓은 형식으로 기본 구조는 다음과 같다.
-    4Byte(길이정수) + 문자값(2Byte) + 종료문자( 0 2개 )
즉, 최초에 DWORD의 정수데이터가 붙고 그 뒤로 Unicode식의 2Byte Encoding 문자열이 붙는형식이다.
그러나 이 앞쪽의 정수 부분은 C++ 코드 작성시에는 없다고 생각해야 한다.
왜냐하면, typedef OLECHAR* BSTR; 로 선언 되어 있기 때문.
COM을 통해 데이터가 전송될 때 알아서 마샬링 되는 것 같다.
아무튼 실제로 BSTR은 WCHAR과 다름에도 불구하고 내부적으로 같은 형식으로 인식되기 때문에 주의를 해야한다.
함수에 인자로 넘길경우 컴파일 오류가 안지 않더라도 내부적으로 오류가 발생하기 쉽다.

게다가 BSTR 은 COM 라이브러리를 통해 마샬링 되어야 하기 때문에 메모리 관리를 다른 곳에서 해야한다.
즉, 사용할 때 메모리 할당과 해제를 명시적으로 API를 통해서 해야한다.
SysAllocString() : 메모리 할당시 사용
SysFreeString() : 메모리 해제시 사용
BSTR bstr = NULL;

bstr = SysAllocString ( L"Hi Bob!" );

if ( NULL == bstr )
  // out of memory error

// Use bstr here...

SysFreeString ( bstr );

이렇게 사용한다.


_bstr_t
BSTR데이터형을 사용하는게 귀찮은 사람들을 위한 BSTR Wrapper 클래스다.
BSTR대신 함수에 넘겨줄수는 없다 ? 가능하지만 상당히 제약적이다.
직접 내부 BSTR에 접근이 안돼기 때문에 함수에 BSTR대신 넘겨주기 위해서는 ATL에서 지원하는 CComBSTR클래스를
사용하면 된다.
다음과 같이 생성 및 문자열 encoding 변환을 할 수 있다.
// Constructing
_bstr_t bs1 = "char string"; & // construct from a LPCSTR
_bstr_t bs2 = L"wide char string"; // construct from a LPCWSTR
//내부적으로 2Byte Unicode 형식이지만, char / wchar_t 양쪽에서 생성 할 수 있다.

_bstr_t bs3 = bs1;  // copy from another _bstr_t
_variant_t v = "Bob";
_bstr_t bs4 = v;  // construct from a _variant_t that has a string

// Extracting data
LPCSTR psz1 = bs1;  // automatically converts to MBCS string
LPCSTR psz2 = (LPCSTR) bs1;  // cast OK, same as previous line
LPCWSTR pwsz1 = bs1;  // returns the internal Unicode string
LPCWSTR pwsz2 = (LPCWSTR) bs1; // cast OK, same as previous line 변환된다!!
BSTR  bstr = bs1.copy()// copies bs1, returns it as a BSTR

// ...
SysFreeString ( bstr ); // 수동적으로 메모리를 해제해줘야 한다.




_variant_t
COM에서 사용되는 VARIANT의 wrapper 클래스다.
당연히 BSTR과 _bstr_t의 관계처럼 좀더 사용하기 용이하다.
기본적으로 VARIANT는 문자열외에 다양한 데이터를 저장하기 위한 구조체지만, 문자열을 저장할 경우 BSTR형식으로
저장이 된다.
_bstr_t와는 다르게 _variant_t는 VARIANT를 상속받은 클래스로 모든 함수에 VARIANT 대신 넘겨주는 것이 가능하다.
내부의 VARIANT는 감춰져있다.
// Constructing
_variant_t v1 = "char string"// construct from a LPCSTR
_variant_t v2 = L"wide char string"; // construct from a LPCWSTR
_bstr_t bs1 = "Bob";
_variant_t v3 = bs1;  // copy from a _bstr_t object

// Extracting data
_bstr_t bs2 = v1;  // extract BSTR from the VARIANT
_bstr_t bs3 = (_bstr_t) v1; // cast OK, same as previous line
위와 같이 _variant_t와 _bstr_t 사이에 전환이 용이 하다.



basic_string::string / basic_string::wstring
STL의 문자열 클래스 basic_string에는 MBSC/Unicode용으로 각각 string/ wstring이 존재 한다.
string은 char / wstring은 wchar_t를 저장하는데, TCHAR은 존재하지않는다.
TCHAR과 STL을 함께 사용하려면 간단히 다음처럼 직접 만들어주면된다.
// Specializations
typedef basic_string<TCHAR> tstring; // string of TCHARs 새로 정의 해준다.

// Constructing 이렇게 각각 생성할 수 있다.
string str = "char string"// construct from a LPCSTR
wstring wstr = L"wide char string"; // construct from a LPCWSTR
tstring tstr = _T("TCHAR string"); // construct from a LPCTSTR

// Extracting data
// 값을 사용할 때에는 .c_str()을 통해 해당하는 원 데이터형으로 반환된다.
LPCSTR psz = str.c_str()// read-only pointer to str's buffer
LPCWSTR pwsz = wstr.c_str(); // read-only pointer to wstr's buffer
LPCTSTR ptsz = tstr.c_str(); // read-only pointer to tstr's buffer
_bstr_t에 바로 할당하기 위해서는
_bstr_t bs1 = wstr.c_str(); 와 같은 식으로 내부 데이터 값을 받아오면 된다.



CComBSTR
ATL의 BSTR wrapper 클래스로 _bstr_t보다 좀더 유용한 기능들이 있다.
우선 COM함수에 BSTR대신 넘겨줄 수 있고, BSTR 메모리 관리를 자동으로 해준다.
내부에 MBCS 변환 기능은 없다.
문자열 변환에는 ATL 변환 Macro를 사용하면 된다. <- 매우 편리하다
// Constructing
CComBSTR bs1 = "char string"// construct from a LPCSTR
CComBSTR bs2 = L"wide char string"; // construct from a LPCWSTR
CComBSTR bs3 = bs1;  // copy from another CComBSTR
CComBSTR bs4;

bs4.LoadString ( IDS_SOME_STR ); // load string from string table

// Extracting data
BSTR bstr1 = bs1;  // returns internal BSTR, but don't modify it!
BSTR bstr2 = (BSTR) bs1; // cast ok, same as previous line
BSTR bstr3 = bs1.Copy(); // copies bs1, returns it as a BSTR
BSTR bstr4;

// CComBSTR의 메모리 관리를 꺼버릴 수 있다.
bstr4 = bs1.Detach(); // bs1 no longer manages its BSTR, 메모리 관리는 수동으로 해야한다.

// ...
SysFreeString ( bstr3 );
SysFreeString ( bstr4 );


추가로, 연산자&는 내부의 BSTR*를 리턴하도록 오버로딩 되어있기 때문에 사용상 고려할점이 존재한다.
STL의 list같은 컬렉션에서 CComBSTR을 사용하기 위해서는 &연산자 오버로딩으로 일반 데이터타입과는 다르게,
CAdapt 를 사용해야 한다. 즉 다음과 같다.
std::list< CAdapt<CComBSTR> > bstr_list;



CComVariant
ATL의 VARIANT wrapper 클래스다. _variant_t와는 다르게 내부의 VARIANT가 감춰져 있지 않아서 직접 접근이 가능.
게다가 CComBSTR과 간단히 형 변환되지는 않기 때문에 값을 넣기 위해서는 검사를 해야한다.
CComVariant v4 = ... // Init v4 from somewhere
CComBSTR bs3;
// 검사하고 변환이 가능하면 넣는다.
if ( SUCCEEDED( v4.ChangeType ( VT_BSTR ) ))
bs3 = v4.bstrVal;



ATL Conversion Macros ? MBCS / Unicode / BSTR 간의 Encoding 변환
Macro 함수의 이름은 다음과 같은 구조로 돼어있다.
[원본 타입]2[새 타입] / [원본타입]2C[새 타입]
2는 그냥 변환 / 2C는 constant pointer를 말하는 C이다.

A : MBCS 문자열 char*
W : Unicode 문자열 wchar_t*
T : TCHAR 문자열 TCHAR*
OLE : OLECHAR 문자열 OLECHAR*
BSTR : BSTR

예를 들어, W2A() 매크로는 Unicode문자열을 MBCS문자열로 변환한다.
매크로를 사용하기 위해서는 atlconv.h 헤더를 포함해야 하는데, 해당 헤더파일만 포함하면 굳이 ATL프로젝트가 아니라도
사용이 가능하다.

사용시에는 우선 USES_CONVERSION 이라는 매크로를 사용전에 호출해주어 변환에 필요한 기본 변수들을 정의 한뒤,
실제 변환 매크로를 사용하면 된다.
// Functions taking various strings:
void Foo ( LPCWSTR wstr );
void Bar ( BSTR bstr );
// Functions returning strings:
void Baz ( BSTR* pbstr );

#include &lt;atlconv.h&gt;

main()
{
using std::string;
USES_CONVERSION;  // declare locals used by the ATL macros

// Example 1: Send an MBCS string to Foo()
LPCSTR psz1 = "Bob";
string str1 = "Bob";

Foo ( A2CW(psz1) );
Foo ( A2CW(str1.c_str()) );

// Example 2: Send a MBCS and Unicode string to Bar()
LPCSTR psz2 = "Bob";
LPCWSTR wsz = L"Bob";
BSTR bs1;
CComBSTR bs2;

bs1 = A2BSTR(psz2)// create a BSTR
bs2.Attach ( W2BSTR(wsz) ); // ditto, assign to a CComBSTR

Bar ( bs1 );
Bar ( bs2 );

SysFreeString ( bs1 )// free bs1 memory
// No need to free bs2 since CComBSTR will do it for us.

// Example 3: Convert the BSTR returned by Baz()
BSTR bs3 = NULL;
string str2;

Baz ( &bs3 ); // Baz() fills in bs3

str2 = W2CA(bs3); // convert to an MBCS string
SysFreeString ( bs3 ); // free bs3 memory
}
Posted by SB패밀리


1. stdafx.h 파일에 #include <atlbase.h> 위에 아래와 같이 include 하면 됩니다.

2. 필요에 따라 프로젝트 속성 - 링커 - 입력에 comsvcs.lib activeds.lib adsiid.lib wininet.lib 를 추가합니다.

 

/////////////////////////////////////////////////////////////////////////////
// EOCS_ADD
/////////////////////////////////////////////////////////////////////////////
// - MFC 지원
#include <afxwin.h>   // MFC 자동화 클래스입니다. - 코어 및 표준 컴포넌트
#include <afxext.h>   // MFC 자동화 클래스입니다. - 확장
#include <afxdisp.h>  // MFC 자동화 클래스입니다. - 자동화 확장
// - Active Server Pages Definitions
#include <asptlb.h>
// - COM 관련 확장
#include <afxconv.h> // for USES_CONVERSION,
#include <comdef.h>
#include <afxctl.h>
#include <afxdb.h>
// - MDAC
#import "c:\\Program Files\\Common Files\\System\\Ado\\msado15.dll" no_namespace rename("EOF", "EndOfFile") // 설치된 경로에 따라 변경
// - ADSI
#include "activeds.h"


출처 : http://blog.naver.com/eocsdev/150070499587

Posted by SB패밀리
TAG AFX, asp, atl, COM, MDAC, MFC, vc++


Implementing Web Browser Band Using ATL HTML Control


 

The Web Browser Bands were introduced in IE 4.0 , they provide a child window (can be both Horizontal and Vertical ) within the main explorer window. The Search, History and Favourites bars are examples of this bar.

These bands can be used to customise the explorer, to display information and for taking user input.

To create these bars we have to implement and register a band object. The band objects are COM objects which exist inside a container (i.e IE for Explorer Bars). Two main aspects for these band objects are: 

a) How they are registered
Bands must be registered as an OLE in-process server that supports apartment threading, the band objects must be registered for appropriate component category. The category determines the object type and it's container: The Vertical Explorer Bar requires registration in CATID_InfoBand and the Horizontal Explorer Bar requires registration in CATID_CommBand.

b) The interfaces they expose
The interfaces a band object must implement are IDeskBand, IObjectWithSite and IPersistStream. We will get into the specifics of interfaces later in the article. If the band object is to accept user input it has to implement IInputObject  and for a context menu it has to implement IContextMenu.

About the ATL HTML Control

The HTML control hosts the Intrenet Explorer WebBrowser control thus giving you access to script parsing, rendering and browsing fcilities of IE. By default the user interface of the control is provided by the WebBrowser Control, this covers the entire area of the control. The UI is rendered based on the HTML included in the resource of your project.

The control exposes two interfaces: one is for the clients of the control and the other with an UI suffix is used to extend the object model of the Web Browser(being hosted by our control). The script which can access the object model can also access this interface (as window.external) and can call its functions .Your C++ code can access the object model in this function . This communication process can be exploited to accomplish a variety of tasks. For further details on HTML control Dr Richard Grimes book (Professional ATL COM Programming) is the panacea.

About Registration:

For object registration as in-process server I have used the code provided by ATL. For Registring the band object for Category CATID_InfoBand (for Vertical Explorer Band) I have used interface ICatRegister exposed by the Component Categories manager object CLSID_StdComponentCategoriesMgr.

About the Interfaces:

IObjectWithSite This is very critical interface. The explorer calls the IObjectWithSite::SetSite Method passing its site's unknown pointer.The following things are achieved during this call.
If the pointer being passed is not NULL  then site is being set , using this pointer i obtain the IOleWindow Interface and get the parent window's handle and store it. I then create my HTML control as child of this window. I store the pointer to IInputObject site Interface for use later.Also I obtain a pointer to the IWebBrowser2 interface of the IE (parent) and store for use later.The IObjectWithSite::GetSite wraps call to sites QueryInterface.
IPersistStream Since there is no persistent data there is only minimal implementation of this interface.
IDeskBand This interface inherits from IDockingWindow and IOleWindow.The IOleWindow::GetWindow returns the child window handle and ContextSensitive help is not implemented .The IDockingWindow::ShowDW and CloseDW have been implemented but ResizeBorder is not required to be implemented.The IDeskBand::GetBandInfo is used by Explorer to specify the Explorer Bar's identifier and viewing mode.It also may request one or more pieces of information by filling the dwMask member of the DESKBANDINFO structure that is passed as the third parameter

Interaction With the Script:

I have implented a function named OnNag( ) of the interface ICHTMUI (the interface used by the webbrowser control as explained earlier).The script calls this finction passing the dispatch pointer to the HTML anchor element and the string representing the site to navigate to.I use the IWebBrowser2 interface pointer stored earlier to navigate to the site.Thus a click in the Band object causes navigation in the main IE Window.

The band.dll can be downloaded and registered .The menu item is added in View | Explorer Bars. On selecting the menu the Explorer bar is created .The explorer bar has links to some sites, to which one can navigate( in the main IE Window) by clicking on them

The project can be downloaded and by modifying the HTML resource (carefully) you add your own links and customise the GUI of the band.

References:

The article "Creating Custom Explorer Bars and Desk Bands" in SBN



출처 : http://www.codeguru.com/Cpp/I-N/ieprogram/article.php/c1235

Posted by SB패밀리




A Complete Scriptable ActiveX Web Control Tutorial using ATL

By Arif Zaman | 18 Dec 2007 | Unedited contribution
An article on Scriptable ActiveX Web Control using ATL
Is your email address OK? You are signed up for our newsletters but your email address is either unconfirmed, or has not been reconfirmed in a long time. Please click here to have a confirmation email sent so we can confirm your email address and start sending you newsletters again. Alternatively, you can update your subscriptions
Scriptable ActiveX Web Control

Introduction

ActiveX control is a term used to denote reusable software components that are based on Microsoft Component Object Model (COM) that allows the developer to create objects or controls that can be used to "activate" content on the World Wide Web.

I have read lots of stuffs over the internet but I have not find any complete guideline or example through the process I can build an ActiveX Control for web using ATL with VC++6.0. I wrote this article because I couldn't find a unified description of this process anywhere on the Web and also from my hurdles. I am going to present you a complete example with necessary stuffs. Hope this will help you to understand how we can build a Scriptable ActiveX control for web using ATL and also you will get the clear vision about the ActiveX security details. It's written with as much detail as possible, but with the assumption that the reader basically knows his stuff.

In this article, I have tried to present you an example of Scriptable ActiveX Web Control using ATL which will display a Label Control with the current Date and Time on your Internet Explorer. Also you will be guided step by step on creating the control using Microsoft VC++6.0 as safe for WEB.

Safety and security

The security concerns associated with ActiveX control is mainly due to the fact that ActiveX controls can do almost anything that a normal application can. Therefore, both malware and useful form ActiveX controls exists; thus the threat level of an ActiveX control can be associated with how trusted its author is.

However, there is one famous case of ActiveX controls safety concern: Internet Explorer. Internet Explorer has the ability to run ActiveX controls within a web page. This is both a blessing and a pain.

ActiveX controls like Adobe Reader, Adobe Flash Player, Apple QuickTime Player, Microsoft Windows Media Player, Real Networks RealPlayer, and Sun Java Virtual Machine are routinely used by Internet surfers every hour of a day all across the globe.

However, opening a web page that contains a harmful ActiveX control, and then allowing Internet Explorer to run that harmful control, would be same as running a malware on the computer. Fortunately, Internet Explorer 6 and Windows Vista have taken actions to reduce such risks.

Requirements for Scriptable ActiveX Controls

Microsoft has published a document that describes all aspects of component object safety with regard to scripting ActiveX controls.

Before registering your control as safe for scripting, you must ensure that your control does not take any actions that may be unsafe when used by a scripting client. Unsafe actions include exposing information normally kept private, such as passwords and other sensitive information, executing files, and making dangerous system calls.

Another type of object safety concerns the use of parameters and properties with the control. When used with a scripting client, a safe control must ensure that the control can continue to function with any possible combination of properties defined by the user.

Design a Scriptable ActiveX Controls

A scriptable ActiveX control is a control that is constructed so that it is appropriate for use by scripting clients. Scriptable controls are designed to be safe when used in a Web browser, such as Internet Explorer, or by scripts written for the Windows Scripting Host (WSH). Scriptable controls also support mechanisms to simplify passing parameters to the control via a scripting language.

By default, ActiveX controls are not considered safe for scriptable clients. If you have an ActiveX control that could be useful by scripting clients, taking a few simple steps can expand the usefulness of your control. If you're using ATL, these changes typically take just a few minutes.

To be scriptable, an ActiveX control must meet the following requirements:

  • Be safe when executed by an arbitrary scripting client.
  • Be registered as "Safe for Scripting".
  • Support the IPersistPropertyBag interface. This is technically not a requirement, but it simplifies the task of passing parameters to the control.
  • Creating a Scriptable ActiveX Control

    To create a Scriptable ActiveX control, use Microsoft Visual C++ 6.0 to perform the following steps:

    1. File->New->Projects.

    2. In the New Project dialog, as shown in Figure 1, select "ATL COM AppWizard".

    New Project dialog

    Figure 1. New Project Dialog

    3. Name the project ScriptableActiveX; for Location, enter the working folder for the project's source code, and then click the OK button.

    4. You will see a screen, as shown in Figure 2, that gives you several choices. The first choice is "Server Type". We are going to build a Server DLL, so make sure that the Server Type is set to "Dynamic Link Library".

    ATL COM AppWizard

    Figure 2. ATL COM AppWizard

    5. The other three checkboxes below do not concern us for this particular project, so we can ignore them. Press the finish button to have the Wizard generate to appropriate files for you.

    6. A "New Project Information" window, as shown in Figure 3, will appear to tell you what files are going to be created. Press the Ok button to accept this.

    New Project Information

    Figure 3. New Project Information

    Creating a new ATL object

    1. From the Insert menu, select "New ATL Object" menu item. The "ATL Object Wizard" window, as shown in Figure 4, will appear.

    ATL Object Wizard

    Figure 4. ATL Object Wizard

    2. Select Controls from the wizard's Category list box, and then select Full Control as the control type. Click Next to begin adding ScriptableLabel control to the project.

    3. The "ATL Object Wizard Properties" dialog, as shown in Figure 5, will appear so that you can define your control's properties. For full controls, such as ScriptableLabel, the dialog box offers four tabs.

    ATL Object Wizard Properties

    Figure 5. ATL Object Wizard Properties

    4. All component types present a Names property page that contains naming attributes for ATL components. You only need to fill in the Short Name property for ScriptableLabel and the other fields are filled in automatically.

    5. The second tab, as shown in Figure 6, for most component types is the Attributes property page, which is used to collect information about the component. All these values are the default options, except "Support Connection Points" properties for the component. Connection points are used to supply events to a control's container.

    ATL Object Wizard Properties (Attributes)

    Figure 6. ATL Object Wizard Properties (Attributes)

    6. The third tab, as shown in Figure 7, is the Miscellaneous tab. It contains attributes specific to ActiveX controls.

    ATL Object Wizard Properties (Miscellaneous)

    Figure 7. ATL Object Wizard Properties (Miscellaneous)

    7. The final tab, as shown in Figure 8, in the dialog box is Stock Properties. It contains a list of all stock properties that can be implemented by an ActiveX control.

    ATL Object Wizard Properties (Stock Properties)

    Figure 8. ATL Object Wizard Properties (Stock Properties)

    8. The ScriptableLabel control uses the following stock properties:

  • Background Color
  • Caption
  • Font
  • Foreground Color
  • After you select the stock properties listed here, click OK to close the dialog box. The ATL New Object Wizard then generates the necessary code and adds it to your project.

    Modify Control Attributes

    The default handler for OnCreate will superclass a default instance of the Windows CStatic class. Apply the following window styles changes to the OnCreate() member function to the ScriptableLabel control:

    Collapse | Copy Code
    
    m_ctlStatic.Create( m_hWnd,
                        rc, 
                        _T("Static"),
                        WS_CHILD|WM_PAINT);
    
    

    Retrieving Ambient Properties

    When the control is initially loaded, it collects the current foreground color, background color, and font from its container. A good time to collect ambient properties is when the control and its container negotiate the client site. Add the following source code to the ScriptableLabel.cpp source file. This function overrides the base class implementation of SetClientSite() and stores the ambient values of these three properties.
    Collapse | Copy Code
    
    STDMETHODIMP CScriptableLabel::SetClientSite(LPOLECLIENTSITE pSite)
    {
        HRESULT hr = CComControlBase::IOleObject_SetClientSite(pSite);
        if(!m_pFont && pSite)
        {
            hr = GetAmbientFontDisp(&m_pFont);
        }
        GetAmbientBackColor(m_clrBackColor);
        GetAmbientForeColor(m_clrForeColor);
        return hr;
    }
    
    

    Add the following member function declaration to the CScriptableLabel class:

    Collapse | Copy Code
    
    STDMETHOD(SetClientSite)(LPOLECLIENTSITE pSite);
    
    

    Drawing the Control

    The OnDraw() function is called by the ATL framework, passing in a structure that contains various bits of information needed to actually paint information on the screen. Our implementation isn�t much more complicated than it would be if we were simply displaying the current Date and Time. Add the following drawing code for the control to the ScriptableLabel.h header file.
    Collapse | Copy Code
    
    void DrawString( HDC hdc, RECT* rc, BSTR caption )
    {
    	USES_CONVERSION;		
    	TCHAR* pCaption = OLE2T(caption);
    
    	DrawText( hdc,
    			 pCaption,
    			 lstrlen( pCaption ),
    			 rc,
    			 DT_WORDBREAK );
    }
    
    HRESULT OnDraw(ATL_DRAWINFO& di)
    {
    	RECT& rc  = *(RECT*)di.prcBounds;
    	HDC   hdc = di.hdcDraw;
    
    	COLORREF clrFore, clrBack;
    	OleTranslateColor(m_clrForeColor, NULL, &clrFore);
    	OleTranslateColor(m_clrBackColor, NULL, &clrBack);
    	SetTextColor(hdc, m_clrForeColor);
    	HBRUSH hbrBtn = CreateSolidBrush(m_clrBackColor);
    
    	FillRect(hdc, &rc, hbrBtn);		
    	DrawString(hdc, &rc, m_bstrCaption);	
    
    	DeleteObject(hbrBtn);
    	return 0;
    }
    
    

    Implementing IPersistPropertyBag

    To simplify the use of ScriptableLabel in a scripting client, such as Internet Explorer, the control must support the IPersistPropertyBag interface. The ATL class library includes a class, IPersistPropertyBagImpl, that provides a default implementation of IPersistPropertyBag that is sufficient in most cases.

    1. Add the following line to your class derivation list:

    Collapse | Copy Code
    
    public IPersistPropertyBagImpl<CScriptableLabel>
    
    

    2. Add the following lines to your COM_MAP:

    Collapse | Copy Code
    
    COM_INTERFACE_ENTRY(IPersistPropertyBag)
    
    

    Marking the Control as Safe for Scriptable Clients

    You can easily add support for marking the Control as Safe for Scriptable Clients by implementing the IObjectSafety interfaces. This is done by deriving your control from the default ATL implementation, IObjectSafetyImpl.

    You can use two methods to mark a control as safe for scripting clients:

  • Make appropriate entries directly in the System Registry.
  • Implement the IObjectSafety interface.
  • Microsoft recommends that you use new controls to implement the IObjectSafety interface instead of making Registry entries. IObjectSafety enables the control to apply much finer-grained safety policies than possible using the Registry.

    ATL provides a default implementation of IObjectSafety that you easily can take advantage of by deriving your class from the IObjectSafetyImpl class.

    1. Add the following line to your class derivation list:

    Collapse | Copy Code
    
    public IObjectSafetyImpl<CScriptableLabel, 
                            INTERFACESAFE_FOR_UNTRUSTED_CALLER | 
                            INTERFACESAFE_FOR_UNTRUSTED_DATA>
    
    

    Two template arguments are passed to IObjectSafetyImpl:

  • The name of the class deriving from IObjectSafetyImpl.
  • The type of safety to be applied to the control.
  • Two values may be passed for the safety options:

    INTERFACESAFE_FOR_UNTRUSTED_CALLER specifies that your control can be used safely by a scripting client and does not violate any of Microsoft's security or safety guidelines. INTERFACESAFE_FOR_UNTRUSTED_DATA specifies that your control will work (or at least degrade gracefully) in the presence of any possible set of par

    2. Add the following lines to your COM_MAP:

    Collapse | Copy Code
    
    COM_INTERFACE_ENTRY(IObjectSafety)
    
    

    Compiling the Scriptable ActiveX Control

    Compile the ScriptableActiveX project. The compiler will register your new DLL in the registry so that other programs can make use of it. You can test the control in a variety of control containers, including Visual Basic and Visual C++. You will find lots of examples using Visual Basic and Visual C++. Better! we consider as web control.

    Because the ScriptableLabel control is scriptable, you can also use the control on an HTML page with Internet Explorer. Lets try it out.

    Building a Signed ActiveX Control

    To use your control on web pages, it is very convenient to distribute it as a .cab (cabinet file). This is a compressed archive - the classic use is to put the .cab file on a web server, and refer to it from web pages that host the control. Internet Explorer is smart enough to download and install the control if it is not already installed on the client machine, and to selectively update any files that are newer in the .cab file. Microsoft provides CABARC.EXE for creating and editing .cab files.

    The Contents of a CAB File

    A CAB file contains at least two files in the archive: an ActiveX component file (.EXE, .DLL or .OCX file), and an .INF file.

  • Component File (.EXE, .DLL or .OCX): The type of ActiveX component you create determines the file extension of the file included here. All ActiveX controls have a .OCX extension. Other ActiveX components either .EXEs or .DLLs. This file is your ActiveX component being installed.
  • .INF: This is the Information file that contains information about what other files your ActiveX component depends on (dependency files), where to get copies of those files, and how to install those files.
  • Creating a CAB File

    This section describes creating CAB files for distributing ATL and MFC controls over the Internet.

    To create a CAB file:

    1. Create an INF file.

    2. Run the CABARC utility.

    1. Creating an INF File

    The INF file is a text file that specifies the files (such as DLLs or other OCXs) that need to be present or downloaded for your control to run. An INF file allows you to bundle all the needed files in one compressed CAB file. By default, files with the same version numbers as existing files on the user's hard disk will not be downloaded.

    As an example, the following INF file will be used to create a CAB file for the ScriptableActiveX control.

    Collapse | Copy Code
    ; Sample INF file for SCRIPTABLEACTIVEX.DLL
    [version] 
    ; version signature (same for both NT and Win95) do not remove
    signature="$CHICAGO$"
    AdvancedINF=2.0  
    
    [Add.Code]
    ScriptableActiveX.dll=ScriptableActiveX.dll
    ScriptableActiveX.inf=ScriptableActiveX.inf
    
    [ScriptableActiveX.dll]
    file-win32-x86=thiscab
    clsid={B8748B60-E34D-42AA-9309-8012CA4964AC} 
    FileVersion=1,0,0,1 
    RegisterServer=yes
    
    [ScriptableActiveX.inf]
    file=thiscab
    ; end of INF file
    
    The clsid is the CLSID of the control to be installed.

    2. Running the CABARC Utility

    Once you have created an INF file, run the CABARC utility to create the CAB file. You should run CABARC in the directory that contains your source files. On the command line, put the source files in the order they appear in the INF and the INF file last. For example, to make a CAB file for the Polygon control from the INF above, use the following command:

    Collapse | Copy Code
    CABARC -s 6144 n ScriptableActiveX.cab ScriptableActiveX.dll ScriptableActiveX.inf
    
    The -s option reserves space in the cabinet for code signing. The n command specifies that you want to create a CAB file.

    Code Signing Tools

    Microsoft provides code signing tools as part of the "Microsoft .NET Framework software development kit (SDK) 1.1". To download the SDK, visit the following Microsoft Web site: .NET Framework SDK Version 1.1. After you install the SDK you will be able to find the code signing files in the following location on your PC: "\Program Files\Microsoft.NET\SDK\v1.1\Bin". From the many files found in the above BIN directory the following are of interest to us:

  • signcode.exe
  • makecert.exe
  • cert2spc.exe
  • chktrust.exe
  • Recently, with the introduction of "Studio 2005" Microsoft has released a new set of tools. The new set of tools is provided as part of the "Visual Studio 2005" and in the "Platform SDK". To download the "Platform SDK", visit the following Microsoft Web site: Platform SDK Full Download. After you install the SDK you will be able to find the code signing files in the following location on your PC: "\Program Files\Microsoft Platform SDK\Bin". From the many files found in the above BIN directory the following are of interest to us:

  • signtool.exe
  • makecert.exe
  • cert2spc.exe
  • All the above mentioned tools are actually small console programs that should be run from a command prompt. When run, each program expects command-line parameters in a special syntax. Now, we will demonstrate how to do it in this article.

    Signing a CAB File

    Security is becoming increasingly important for software and media developers. "Code Signing" enables end users of your software to verify that the code you provide comes from the rightful source and that it has not been maliciously altered or accidentally corrupted.

    Code signing technology is available for many years, but many software vendors refrained from using it because of the complexity involved in the process. However - since the release of "Windows XP Service Pack 2" - code signing can not be ignored any more.

    With XP SP2, when downloading an UN-SIGNED program with Internet Explorer and choosing to open it (or even if saved to a file and later opened via Windows Explorer), the browser will display the "Security Warning" dialog. BUT when downloading a SIGNED program the browser does not result in annoying error messages complaining that it is an unsigned or unsafe control.

    You have 2 options to sign your code:

    Option-1. Using Test Certificate

    Option-2. Using Public Certificate

    Option-1: Using Test Certificate

    Before you start with the process it is highly recommended that you create a "TEST certificate", and use it to sign your program as a test run. A program signed by this certificate must not be distributed on the net.

    Use the following steps to sign and validate the .cab file:

    1. Type the following at a command prompt to create a private key file, Mycert.pvk.

    Collapse | Copy Code
    makecert -sv "mycert.pvk" -n "CN=My Company" mycert.cer
    
    The file created in this step, Mycert.cer, is used to create an .spc file. Type the password in the dialog box.

    2. Create an .spc file from the certificate file with the correct password. To do so, type the following line at a command prompt:

    Collapse | Copy Code
    cert2spc mycert.cer mycert.spc
    
    Note that this step involves creation of a test private key. Alternatively, valid certificates can be created through Microsoft Certificate Server for Intranet use or purchased from external vendors for Internet use.

    3. Create a Personal Information Exchange file. This step is optional. You will need it only if you select to sign your code using SIGNTOOL.EXE as described above. The following command line may be used to create a "Personal Information Exchange" (PFX) file:

    Collapse | Copy Code
    pvk2pfx -pvk mycert.pvk -pi Pass1 -spc mycert.spc -pfx mycert.pfx -po Pass2 -f
    
    PVK2PFX.EXE expects 2 passwords:

    Pass1: - Provide the same password you used when creating the Private & Public keys.

    Pass2: - Provide a new password to protect the PFX file.

    Once signed you can distribute the ActiveX component to any user, BUT the user must install certificate chain. If you want users to install an ActiveX component without the Root and Intermediate certificates then buy a Public code-signing certificate.

    Option-2: Using Public Certificate

    To be able to sign your code you will need a certificate. Certificates are issued by special Internet organizations called CA (Certificate Authority). There are many such organizations, the most prominent ones are: verisign.com and tawthe.com.

    You can get a full list of CAs by going to: Microsoft Root Certificate Program Members

    However - acquiring a certificate is quite a tedious and time consuming process, (The CA has to identify you or your organization before it can issue a certificate) and you should expect to pay few hundred of dollars for the certificate.

    Finishing Touches

    Now, Use the key information to sign the .cab file:

    Collapse | Copy Code
    signcode -v mycert.pvk -spc mycert.spc ScriptableActiveX.cab -t [Timestamp server URL] 
    

    Or, the following commands may be used to sign your code using SIGNTOOL.EXE:

    Collapse | Copy Code
    signtool sign /f mycert.pfx /p Pass2 /v ScriptableActiveX.cab /t [Timestamp server URL]
    
    Pass2: Enter here the second password you used when creating the PFX file.

    Note Specify the timestamp server URL at this step. The timestamp server URL provides a place to enter or edit the location of a timestamp server. A timestamp server validates the date and time that the cabinet file was signed. Certificate files can expire after a certain period of time. Contact your certificate file provider (certificate authority) for the location of their timestamp server. The URL of a time stamping service provided for free by verisign.com http://timestamp.verisign.com/scripts/timstamp.dll

    Validate a CAB File

    Follow this procedure to validate a .cab file:

    Option-A: Using CHKTRUST

    a. Type the following at a command prompt to run SETREG.EXE on the client system with the TRUE value so that the test certificates are recognized:

    Collapse | Copy Code
    setreg -q 1 TRUE
    

    b. Run CHECKTRUST.EXE to ensure that the CAB file is signing correctly:

    Collapse | Copy Code
    chktrust ScriptableActiveX.cab
    
    Expected results:
    Collapse | Copy Code
    ScriptableActiveX.cab: Succeeded
    

    Option-B: Using SIGNTOOL

    Run SIGNTOOL.EXE to verify the signing:

    Collapse | Copy Code
    signtool verify /v /a ScriptableActiveX.cab
    
    Expected results:
    Collapse | Copy Code
    ScriptableActiveX.cab: Succeeded
    

    Option-C: Using Properties

    You can also use the following method to verify your digital signature:

  • Open Explorer.
  • Browse to the file you just signed.
  • Right Click the file name and select "Properties".
  • Explorer will present you with the properties dialog of the file. This properties dialog will include a new tab "Digital Signatures". You can now read the details of the signature in this tab.

    Embedding a Signed CAB File on a Web Page

    ATL and MFC controls are embedded in Web pages using the <OBJECT> tag. Within the <OBJECT> tag, you need to specify three attributes for the control:

    Collapse | Copy Code
    
    <OBJECT classid=B8748B60-E34D-42AA-9309-8012CA4964AC
    	id=ScriptableLabelControl
    	CODEBASE ="ScriptableActiveX.cab#version=1,0,0,1">
    </OBJECT>
    
    

    The ID parameter refers the name of the control. The CLASSID parameter refers the CLSID of the control. And the CODEBASE parameter refers the location from which to download the control. Note that CODEBASE can point at a number of different file types successfully.

    If you include the optional version number with a CAB file, the version number should refer to the control being downloaded. For example, since ScriptableActiveX.dll has a version number of 1, 0, 0, 1, the version for the CAB is also 1, 0, 0, 1.

    If you do not include the version number, older versions of the same control will not be replaced if they are found on the client machine.

    Testing the Scriptable ActiveX Web Control

    Adding controls by hand is not a simple process. The CLSID numbers are unwieldy to type, and it can be difficult to visualize the effects of the parameter settings on the control. To help with these difficulties, Microsoft created the ActiveX Control Pad, which can be obtained from their web site at Microsoft ActiveX Control Pad.

    To test the Scriptable ActiveX Web Control with JavaScript, you may use ActiveX Control Pad to create or you may find it easier to lay out your page in your preferred HTML editor as the following HTML code:

    Collapse | Copy Code
    
    <HTML>
    <HEAD>
    <TITLE>Scriptable ActiveX Web Control</TITLE>
    <SCRIPT LANGUAGE="JavaScript">
    	function NotifyActiveX(e)
    	{
    		if ( e.readyState != 4 ) return;
    		window.setInterval("document.all.ScriptableLabelControl.caption=(new Date()).toString()",1000);
    	}
    </SCRIPT>
    <STYLE TYPE="text/css">
    #ScriptableLabelControl {
    	width: 250px;
    	height: 27px;
    	padding: 10px;
    	border: thin solid #408080;
    	margin-left: 10em;
    }
    </STYLE>
    </HEAD>
    
    <BODY>
    <OBJECT 
      ID="ScriptableLabelControl"
      onReadyStateChange="NotifyActiveX(this)"
      CLASSID="CLSID:B8748B60-E34D-42AA-9309-8012CA4964AC"
      CODEBASE="ScriptableActiveX.cab">
    </OBJECT>
    </BODY>
    </HTML>
    
    

    Save this HTML code in your web server, and browse it!

    Internet Explorer will prompt the user before downloading and installing the ActiveX control. If Internet Explorer is attempting to install the Signed ActiveX control automatically it shows the prompt, as shown in Figure 9.

    Install ActiveX

    Figure 9. Install ActiveX

    When prompted with a certificate, please accept it. When downloading and installation process will be finished, the current date and time should appear, as shown in Figure 10.

    Scriptable ActiveX Control

    Figure 10. Scriptable ActiveX Control

    If you see the current date and time displayed properly in your browser, CONGRATULATIONS! ActiveX and scripting are working properly.

    Note:

  • If you see a date and time but it isn't the right time, your PC's clock is set wrong! Double-click the time in the system tray to correct it.
  • If you see a blank space, ActiveX is probably working properly, but not scripting.
  • If you see the message ActiveX is not supported, then your browser doesn't recognize ActiveX at all. Netscape, Opera, or other browsers usually do not support ActiveX.
  • When you think you've corrected any problem you are having with this, simply refresh the page [press F5] to try again.
  • Conclusion

    For a control to be scriptable, it must follow Microsoft's object safety guidelines and be marked as safe for scripting. In this article, you examined the IObjectSafety interface and learned how a control built using ATL can take advantage of IObjectSafety. An example of a safely scriptable ActiveX control helped you explore this topic.

    License

    This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

    출처: http://www.codeproject.com/KB/atl/ScriptableActiveX.aspx

    Posted by SB패밀리