Рейтинговые книги
Читем онлайн Программирование на Visual C++. Архив рассылки - Алекс Jenter

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 45 46 47 48 49 50 51 52 53 ... 156

Запишем в заголовочном файле класса, содержащего webbrowser control:

// Web browser event sink

DECLARE_EVENTSINK_MAP()

virtual void OnDownloadComplete();

virtual void DocumentComplete(LPDISPATCH pDisp, VARIANT* URL);

А в .cpp файле добавим строки:

BEGIN_EVENTSINK_MAP(CChatChannelDialog, CDialog)

 ON_EVENT(CChatChannelDialog, AFX_IDW_PANE_FIRST, DISPID_NAVIGATECOMPLETE, OnDownloadComplete, VTS_NONE)

 ON_EVENT(CChatChannelDialog, AFX_IDW_PANE_FIRST, DISPID_DOCUMENTCOMPLETE, DocumentComplete, VTS_DISPATCH, VTS_PVARIANT)

END_EVENTSINK_MAP()

Для полной реализации механизма отлова событий этим путем, лучше всего обратиться к исходным текстам CHTMLView.

Модель объектов DHTML

Интерфейс DWebBrowserEvents2 при помощи события DISPID_NAVIGATECOMPLETE позволяет определить тот момент, когда HTML документ полностью сгенерирован внутри webbrowser control. После того, как это происходит, весь HTML документ доступен через функцию IWebBrowser2::get_Document. Также, как и webbrowser control, HTML документ поддерживает события, такие как click, mouseover. Для того, чтобы использовать объектную модель DHTML, нужно подключить заголовок mshtml.h.

CComQIPtr<IHTMLDocument2, &IID_IHTMLDocument2> pADocument;

IDispatch* pdispTmpVal;

m_pBrowserApp->get_Document(&pdispTmpVal);

pADocument = pdispTmpVal;

pdispTmpVal->Release();

Интерфейс IHTMLDocument2 предоставляет возможность получать и модифицировать содержимое документа. Вы можете использовать множество методов, таких как get_body, get_all, get_activeElement чтобы извлекать элементы или коллекции элементов внутри документа. Базовой основой для любого тэга внутри HTML-документа является интерфейс IHTMLElement. Меняя содержимое тэга при помощи свойств innerHTML и outerHTML мы реализуем принцип динамического содержания, который нами и преследовался. К любому элементу можно адресоваться при помощи идентификатора id через вызов IHTMLElementCollection::Item. Итак, c визуализацией ясно, а как же теперь обеспечить интерактивность? Как избавиться от ненужных клавишных комбинаций и меню? Как получить доступ из скриптов к внутренней модели объектов нашей программы?

Расширение объектной модели DHTML

Компания Microsoft предоставила возможность расширения объектной модели через механизм window.external. Приложение, использующее web-browser control может реализовывать собственную логику через переопределение объекта external. Естественно, чтобы иметь возможноть работать со своим приложением из скрипта, программа должна реализовывать dispatch-интерфейсы. При помощи ClassWizard, добавить поддержку автоматизации к своим объектам не составляет труда. Единственным замечанием здесь может служить лишь то, что объекты должны наследоваться от CCmdTarget. Чтобы передать указатель на свой объект самому объекту webbrowser, а заодно установить целую кучу дополнительных параметров, необходимо реализовать cлужебный интерфейс IDocHostUIHandler, который описан в mshtmhst.h. Этот интерфейс представляет собой некий call-back, или интерфейс обратной связи, к которому обращается webbrowser в следующих случаях:

• Необходимо показать контекстное меню. Как раз здесь можно заменить стандартное меню Internet-explorer на свое собственное. Либо вообще сделать так, чтобы меню не показывалось.

• Есть возможность подменить элементы пользовательского интерфейса браузера.

• Нужно обработать нажатие горячей клавиши.

• Нужно обработать URL, по которому совершается переход.

• Нужно обработать события drag-and-drop.

• Необходимо получить указатель на объект window.external.

После реализации этого call-back объекта, его можно "инсталлировать", используя метод интерфейса ICustomDoc SetUIHandler. Интерфейс IСustomDoc экспортируется обычно реализуется тем же объектом, что реализует IHTMLDocument2.

// код из OnNavigateComplete

CComQIPtr<ICustomDoc, &IID_ICustomDoc> m_pBrowserCustomDoc;

CComQIPtr<IHTMLDocument2, &IID_IHTMLDocument2> pADocument;

CDocHostUIHandler m_DocHostImpl;

m_DocHostImpl.AddRef();

m_DocHostImpl.m_pAppDisp = m_pApp->GetIDispatch(FALSE);

m_pBrowserCustomDoc = pADocument;

m_pBrowserCustomDoc->SetUIHandler((IDocHostUIHandler*)&m_DocHostImpl);

В данном коде фигурирует класс CDocHostUIHandler, который реализует все методы интерфейса IDocHostUIHandler (и конечно же AddRef, QueryInterface и Release от IUnknown). В базовом варианте, реализация этого объекта сводится лишь к созданию процедур-заглушек для каждого метода IDocHostUIHandler, возвращающих E_NOTIMPL. А если хочется, чтобы Internet Explorer не показывал своего конекстного меню, нужно возвращать из метода ShowContextMenu S_OK.

Если наш объект CDocHostUIHandler возвращает указатель в методе get_External, то этот указатель и используется как объект расширения и тогда где-нибудь внутри самой html странички можно будет написать такие строки:

<script language="JavaScript">

function ShowSettingsDialog() {

 if (window.external.ShowSettings() == true) {

  document.body.bgcolor = window.external.BackColor;

 }

}

</script>

<body>

<a href="javascript:ShowSettingsDialog()">Settings</a>

</body>

В приведенном примере, функция ShowSettings и свойство BackColor запрашиваются из недр нашего собственного приложения.

Где хранить свои HTML

В ресурсах! К счастью, Internet explorer умеет грузить из ресурсов, нужно только в качестве префикса URL написать res://<путь к модулю>/<название ресурса>. Я привожу реализацию этого метода, выдранную из исходного текста CHTMLView.

HINSTANCE hInstance = AfxGetResourceHandle();

CString strResourceURL;

BOOL bRetVal = TRUE;

LPTSTR lpszModule = new TCHAR[_MAX_PATH];

if (GetModuleFileName(hInstance, lpszModule, _MAX_PATH)) {

 // lpszResource - строкое название ресурса

 strResourceURL.Format(_T("res://%s/%s"), lpszModule, lpszResource);

 m_pBrowserApp->Navigate(strResourceURL, NULL, NULL, NULL, NULL);

} else bRetVal = FALSE;

delete [] lpszModule;

return bRetVal;

HTML ресурсы можно вынести в отдельный подкаталог, например html. Тогда в файле описания ресурсов (например, myapp.rc) необходимо добавить строки следующего вида:

IDR_MAIN HTML DISCARDABLE "html\main.html"

DEL.GIF HTML DISCARDABLE "html\del.gif"

LEFTARR.GIF HTML DISCARDABLE "html\leftarr.gif"

RIGHTARR.GIF HTML DISCARDABLE "html\rightarr.gif"

TITLE.GIF HTML DISCARDABLE "html\title.gif"

NEWMSG.GIF HTML DISCARDABLE "html\newmsg.gif"

Реализация доступа к ресурсам в IE достаточно умна, чтобы автоматически найти все необходимые объекты в ресурсах, на которые ссылается страничка, поэтому достаточно знать лишь ресурс-имя основной странички.

ВОПРОС-ОТВЕТ 

Q. Насколько корректно будут работать методы контроля утечек памяти (в частности объект CMemoryState) в многопоточных приложениях?

У меня сложилось впечатление, что объект CMemoryState не делает различия в каком потоке вызывались операторы new с момента обращения к memState.Checkpoint() до обращения к memState.DumpAllObjectsSince().

Видимо "моментальные снимки" распределённой памяти в данном случае не информативны, ведь несколько потоков работают в одном адресном пространстве?

Николай Турпитко 

A. Действительно, вне зависимости от потока, все распределения памяти попадают в один большой двусвязный список блоков памяти, который поддерживает отладочная версия CRT (если задан макрос _DEBUG). Что касается MFC-класса CMemoryState, он является просто тонкой обёрткой вокруг структуры _CrtMemState и функций для диагностики утечек памяти CRT. Поэтому он также не делает различий между потоками.

Хотя функции отладочной библиотеки очень полезны, они не отличаются гибкостью. Научить класс CMemoryState выдавать список блоков, выделенных текущим потоком, возможно, только используя недокументированные возможности CRT. Но кое-что в этом направлении можно сделать и легальными средствами. 

При распределении памяти в отладочной версии программы каждому блоку назначается тип. По умолчанию блок получает тип _NORMAL_BLOCK. Существуют и другие типы: _CRT_BLOCK (блок, распределяемый для внутренних нужд CRT), _CLIENT_BLOCK (блок, к которому применяется пользовательская функция построения дампа), _FREE_BLOCK (блок, который уже освобождён с помощью free; такие блоки остаются в памяти, чтобы отладочная библиотека могла отследить ошибки, связанные с записью в уже освобождённый блок памяти) и _IGNORE_BLOCK (блок, который игнорируется при построении списка распределённых объектов). В стандартную библиотеку входит версия оператора new с четырьмя параметрами, которой можно передать тип распределяемого блока.

Соответственно, мы можем сохранить идентификатор потока, который нас интересует, в глобальной переменной, а затем передавать оператору new тип  _NORMAL_BLOCK, если идентификатор текущего потока совпадает с сохранённым в переменной, и _IGNORE_BLOCK в противном случае. Чтобы облегчить эту задачу, можно написать небольшой модуль, который будет всем этим заниматься. Например: 

//------------------------------

// threadmem.h

void DumpOnlyThisThread(DWORD id);

extern DWORD __DumpThread;

#ifdef _DEBUG

#define THREAD_DEBUG_NEW

 new((__DumpThread == ::GetCurrentThreadId() ?

 _IGNORE_BLOCK : _NORMAL_BLOCK), THIS_FILE, __LINE__)

#else

#define THREAD_DEBUG_NEW new

1 ... 45 46 47 48 49 50 51 52 53 ... 156
На этой странице вы можете бесплатно читать книгу Программирование на Visual C++. Архив рассылки - Алекс Jenter бесплатно.
Похожие на Программирование на Visual C++. Архив рассылки - Алекс Jenter книги

Оставить комментарий