Отправляет email-рассылки с помощью сервиса Sendsay

Школа программирования

  Все выпуски  

Школа программирования Избавляемся от контекстного меню


Избавляемся от контекстного меню

   Доброе время суток:) Сегодня мы отключим меню в эксплорере. В этом нам поможет статья Избавляемся от контекстного меню http://www.rsdn.ru/article/inet/iemenu.xml:) Для того чтобы убрать меню, нужно реализовать интерфейс IDocHostUIHandler. А для того, чтобы его реализовать нужно получить доступ к документу, который загружен в браузер. Тут есть одна тонкость - после создания нашего ActiveX WebBrowser документа нет, т.е. если мы в этот момент попытаемся получить документ, то мы получим дырку от бублика:) Ну нет еще документа, нет его, нет, так надо загрузить. И после загрузки, мы, как реальные пацаны, можем легко получить документ. А как понять когда загружен документ. Понять легко - для этого есть событие OnDocumentComplete. А как нам получить это событие, зададим резонный вопрос? Сейчас расскажу. Есть такой замечательный класс в MFC, который называется CCmdTarget. Этот класс мы и будем использовать для подключения к событиям.
     Создадим класс, который будет наследоваться от CCmdTarget и назовем его CDocumentEventSinkNotify (рис 1 и 2)

9_1
рис 1

9_2
рис 2

Чтобы класс работал, необходимо в конструкторе вызвать EnableAutomation()

CDocumentEventSinkNotify::CDocumentEventSinkNotify()
{
    EnableAutomation();
}

Чтобы перехватить событие будем использовать макросы BEGIN_DISPATCH_MAP/END_DISPATCH_MAP и DECLARE_DISPATCH_MAP()
В DocumentEventSinkNotify.h

class CDocumentEventSinkNotify : public CCmdTarget
{
    DECLARE_DYNCREATE(CDocumentEventSinkNotify)

    CDocumentEventSinkNotify();           // protected constructor used by dynamic creation

// Attributes
public:

// Operations
public:

// Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CDocumentEventSinkNotify)
    //}}AFX_VIRTUAL

// Implementation
protected:
    virtual ~CDocumentEventSinkNotify();

    virtual void DocumentComplete(LPDISPATCH pDisp, VARIANT FAR* URL){};
    // Generated message map functions
    //{{AFX_MSG(CDocumentEventSinkNotify)
        // NOTE - the ClassWizard will add and remove member functions here.
    //}}AFX_MSG

    DECLARE_MESSAGE_MAP()
    DECLARE_DISPATCH_MAP()
};

В DocumentEventSinkNotify.cpp
BEGIN_DISPATCH_MAP(CDocumentEventSinkNotify, CCmdTarget)
    DISP_FUNCTION_ID(CDocumentEventSinkNotify, "DocumentComplete", 259, DocumentComplete,  VT_EMPTY, VTS_DISPATCH VTS_PVARIANT)
END_DISPATCH_MAP()
    
     Тут надо объяснить два момента:) Момент первый откуда я взял параметры строчки DISP_FUNCTION_ID. В принципе из документации:))) Но можно поступить чуть более простым способом, по крайней мере я так делаю:) Воспользуемся помощью Visual C++. Создадим пустой проект на основе диалога, положим туда браузер и и там уже попытаемся привязать событие OnDocumentComplete и посмотрим, что получиться.

9_3

9_4
9_5
Visual C++ сгенерил вот такой код

BEGIN_EVENTSINK_MAP(CTempDlg, CDialog)
    //{{AFX_EVENTSINK_MAP(CTempDlg)
    ON_EVENT(CTempDlg, IDC_EXPLORER2, 259 /* DocumentComplete */, OnDocumentCompleteExplorer2, VTS_DISPATCH VTS_PVARIANT)
    //}}AFX_EVENTSINK_MAP
END_EVENTSINK_MAP()

Вот отсюда можно узнать некоторые параметры:) Пожалуй, единственное, что не выдает этот код, так это
параметр VT_EMPTY. Это входной параметр void. А выходные параметры пишутся в карте через пробел - VTS_DISPATCH VTS_PVARIANT

     Предположим, мы создадим переменную класса 
CDocumentEventSinkNotify, останется решить такую задачу - как передать событие в другой объект. Я делаю таким образом: создаю чисто виртуальный класс CCoreDocumentEventSinkNotify, который будет работать для обратного вызова. Переопределяю CDocumentEventSinkNotify таким образом, что при сработки его события вызывался бы метод CCoreDocumentEventSinkNotify. Наследую DLG_SPLASH еще от CCoreDocumentEventSinkNotify и в конструкторе DLG_SPLASH инициализирую переопределенный m_document_notify_sink
Короче, в сухом остатке: срабатывание DocumentComplete у CDocumentEventSinkNotify приведет к вызову OnDocumentComplete DLG_SPLASH.

#include <atlbase.h>
#include "DocumentEventSinkNotify.h"
#include "afxctl.h"
#include "myHost.h"

class CCoreDocumentEventSinkNotify
{
public:
    virtual void OnDocumentComplete()=0;
};

class COverDocumentEventSinkNotify : public CDocumentEventSinkNotify
{
private:
    CCoreDocumentEventSinkNotify *m_core;
public:
    COverDocumentEventSinkNotify(CCoreDocumentEventSinkNotify *_core)
    {
        m_core = _core;
    }
   
    virtual void DocumentComplete(LPDISPATCH pDisp, VARIANT FAR* URL)
    {
        m_core->OnDocumentComplete();
    };
   
};


class DLG_SPLASH : public CDialog, public CCoreDocumentEventSinkNotify
{
private:
    CWnd m_wnd_browser;         // окно для эксплорера
    CComPtr<IWebBrowser2> m_browser;    // ссылка на контрол эксплорера
    void LoadProject();
    CString GetFileDir();

    void SetTopMost(int _left, int _top, int _nwidth, int _nheight);


    COverDocumentEventSinkNotify m_document_notify_sink;
   
    bool m_success_connect_to_document_events;
   
    void ConnectToEvents();
    void DisconnectFromEvents();
    DWORD m_dwCookie_document_notify;
    virtual void OnDocumentComplete();

    CMyHost m_pMyHost;
public:
    DLG_SPLASH(CWnd* pParent = NULL);   // standard constructor

    void SplashShow();
    void SplashHide();

   
// Dialog Data
    //{{AFX_DATA(DLG_SPLASH)
    enum { IDD = IDD_SPLASH };
        // NOTE: the ClassWizard will add data members here
    //}}AFX_DATA


// Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(DLG_SPLASH)
    public:
    virtual BOOL PreTranslateMessage(MSG* pMsg);
    protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
    //}}AFX_VIRTUAL

// Implementation
protected:

    // Generated message map functions
    //{{AFX_MSG(DLG_SPLASH)
    virtual BOOL OnInitDialog();
    afx_msg void OnClose();
    afx_msg void OnSize(UINT nType, int cx, int cy);
    afx_msg void OnDestroy();
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
};


DLG_SPLASH::DLG_SPLASH(CWnd* pParent /*=NULL*/)
    : CDialog(DLG_SPLASH::IDD, pParent),m_document_notify_sink(this)
{
    //{{AFX_DATA_INIT(DLG_SPLASH)
        // NOTE: the ClassWizard will add member initialization here
    //}}AFX_DATA_INIT
}


    Для того, чтобы события ловились необходимо связать два объекта m_browser и m_document_notify_sink

void DLG_SPLASH::ConnectToEvents()
{
    DisconnectFromEvents();
    //m_lpDisp = lpDisp;
    m_success_connect_to_document_events = AfxConnectionAdvise(m_browser, DIID_DWebBrowserEvents2, m_document_notify_sink.GetIDispatch(FALSE), FALSE, &m_dwCookie_document_notify )?true:false;
}

     При выходе нужно развязать:)

void DLG_SPLASH::DisconnectFromEvents()
{
    if (m_success_connect_to_document_events==true)
    {
        AfxConnectionUnadvise (m_browser, DIID_DWebBrowserEvents2,m_document_notify_sink.GetIDispatch(FALSE), FALSE, m_dwCookie_document_notify );
        //    m_lpDisp.Release();
    }
    m_success_connect_to_document_events=false;
   
}

     Когда произошло событие загрузки документа мы подставим нашу реализацию
IDocHostUIHandler - объект m_pMyHost. Этот объект, класса CMyHost, я взял из статьи:)) Собственно, отключение меню делается так:

    STDMETHOD( ShowContextMenu )( 
        /* [in] */ DWORD dwID,
        /* [in] */ POINT *ppt,
        /* [in] */ IUnknown *pcmdtReserved,
        /* [in] */ IDispatch *pdispReserved)
    {
        TRACE("ShowContextMenu: ID= %d, Point = (%d,%d)\n", dwID, ppt->x, ppt->y);
        return S_OK;
    }


void DLG_SPLASH::OnDocumentComplete()
{

    // Получаем интерфейс текущего документа
    HRESULT hr;
    LPDISPATCH pDispatch;
    hr = m_browser->get_Document(&pDispatch);       
    if( pDispatch == NULL ) return; // только если документ загружен

    IHTMLDocument2* pHtmlDoc;
    hr = pDispatch->QueryInterface( __uuidof( IHTMLDocument2 ), (void**)&pHtmlDoc );
    pDispatch->Release();
   
    ASSERT( SUCCEEDED( hr ) );
   
    // Получаем ICustomDoc
    ICustomDoc* pCustomDoc;
    hr = pHtmlDoc->QueryInterface( __uuidof( ICustomDoc ), (void**)&pCustomDoc );
    pHtmlDoc->Release();
   
    ASSERT( SUCCEEDED( hr ) );
   
        m_pMyHost.AddRef();

        hr = pCustomDoc->SetUIHandler( (IDocHostUIHandler*)&m_pMyHost);
    ASSERT( SUCCEEDED( hr ) );       
    pCustomDoc->Release();
   
}


     При закрытии приложения, мы отключаемся от событий.
void DLG_SPLASH::OnDestroy()
{
    CDialog::OnDestroy();
    //m_pMyHost.Release();
    DisconnectFromEvents();
   
}

Исходники можно скачать отсюда  (http://program-school.narod.ru/source/waterEyeKeeper3.zip)
Уф. На сегодня все:)) До скорого:)
С автором Вы можете связаться по этому адресу - programschool@inbox.ru

В избранное