> www hints
Разработка ActiveX компонетов

     В разделе описаны принципы разработки и использования ActiveX компонентов, детали их реализации, а также некоторые обобщенные примеры, которые (или идеи которых) можно использовать в различных компонентах. ActiveX компонент является очень мощным средством расширения функциональности тонких клиентов (Web-браузеров), применяемых в Intranet сетях. Применение ActiveX для WWW-сайтов сопряжено с проблемами доверия пользователей к вашей разработки и проблемами многоплатформенности. Скорее всего, данная технология в области WWW-сайтов в скором времени будет полностью замещена Java-апплетами, однако на платформе Win32 конкурентов у ActiveX нет.
Интеграция ActiveX в HTML-страницу
Обработка событий от ActiveX (IConnectionPoint)
Безопасные ActiveX компоненты
Отображение пиктограмм, загруженных с Web-сервера
Отправка на Web-сервер массива данных посредством формата multipart form
Интеграция ActiveX в HTML-страницу
    Интеграция ActiveX компонента в HTML-странцу основано на применении объекта object в терминах модели DHTML. Для успешной интеграции необходимо лишь правильное описание ActiveX компонента, после чего он станет отображаться на странице и сможет быть установлен на компьютере клиента, открывшего вашу страницу с этим компонентом. Далее приведен пример вставки ActiveX объекта:
<object style="border-style:inset;border-width:1;"
	classid="clsid:F2479A20-02CD-4ACA-B1E1-2CCC1181625E"
	codebase="http://localhost/httpxtree.ocx#version=1,0,0,1"
	name="httptree"
	align="left"
	language="JScript"
	width="300"
	height="530"
></object>
    Необходимо обязательно указать идентификатор класса ActiveX компонента (так называемый CLSID). Атрибут codebase указывает место расположения компонента на Web-сервере и версию компонента. Для того, чтобы компонент установился на компьютере пользователя, необходимо разместить компонент (файл <имя компонента>.ocx) в соответствующем каталоге Web-сервера. Имя (name) компонента необходимо для установки свойств компонента и обработки его событий посредсвтом некоторого сриптового языка, например, JScript.
Обработка событий от ActiveX (IConnectionPoint)
     Для обратного взаимодействия ActiveX компонента с контейнером, в котором компонент расположен, например, HTML-страница, используется стандартный OLE интерфейс - IConnectionPoint. Ваш компонент обязан реализовать данный интерфейс, а именно - описать события, генерируемые компонентом и вызывать их в соответствующих местах кода компонента. Представим ситуацию, что компонент вследствие нажатий на определенной области кнопки мыши, должен оповестить об этом HTML-страницу, которая в свою очередь может произвести операцию перехода по определенному URL (естественно, посредством операторов скриптового языка).
Где-то в коде ActiveX компонента:
void CHttpXTreeCtrl::OnDynMenuCommand(UINT nId)
{
	FireInvoke("http://localhost/redirecttopage.htm");
}
Обработчик события на HTML-странице:
<script for=httptree event="Invoke(action)">
	document.parentWindow.open(action, '_self');
</script>
     Для того, чтобы связать код обработчика с конкретным объектом используется имя данного объекта (в нашем случае httptree) и имя события (Invoke), при наступлении которого, запускается обработчик. Обратите внимание на объявление обработчика события (тэг event) - для передачи аргументов в обработчик, необходимо его объявить как функцию с параметрами.
Безопасные ActiveX компоненты
     Вполне естественно, что технология ActiveX содержит в себе опасность попадания на компьютер пользователя произвольного исполняемого кода, который может причинить существенный урон. Избежать данной опасности без участия пользователя практически невозможно, поэтому Microsoft встроила в Internet Explorer механизмы, которые предостерегают пользователя о возможных последствиях использования ActiveX компонентов. Пользователю рекомендуется устанавливать компоненты только из заслуживающих доверия источников. Одним из способов повысить доверие операционной системы к устанавливаемому ActiveX компоненту является регистрация его как "безопасного" (safety). Вам необходимо включить несколько дополнительных строчек кода в функцию регистрации компонента, которые будут указывать, что компонент полностью безопасен для системы пользователя:
...
LPCTSTR keys[] = {_T("CLSID\\{F2479A20-02CD-4ACA-B1E1-2CCC1181625E}\\
			Implemented Categories\\{7DD95801-9882-11CF-9FA9-00AA006C42C4}"),
		_T("CLSID\\{F2479A20-02CD-4ACA-B1E1-2CCC1181625E}\\
			Implemented Categories\\{7DD95802-9882-11CF-9FA9-00AA006C42C4}"),
		NULL};
	
LPCTSTR vals[] = {_T(""), _T(""), NULL};

STDAPI DllRegisterServer(void)
{
	...
	AfxOleRegisterHelper(keys, vals, 2, TRUE); 
}
Жирным шрифтом обозначен идентификатор компонента (CLSID), который вам необходимо подменить.
Отображение пиктограмм, загруженных с Web-сервера
     Предположим, перед вами встала задача отображения пиктограмм в ActiveX компоненте, образы (файлы изображений) которых хранятся на сервере - например, дерево отображающее структуру документов, расположенных в базе данных. Пиктограммы могут меняться в зависимости от настройки сервера либо в зависимости от типа/состояния документа. "Зашивать" в код ActiveX компонента заранее предопределенный набор пиктограмм может оказаться немасштабируемым решением.
Пусть на сервере реализован код, передающий поток байтов изображения клиенту:
PHP-скрипт (1-й способ):

<?php
   	header("Content-type: image/gif");	// необходимо указать MIMI-тип потока байт
	
	// activeX компонент требует определенной пиктограммы
	if(array_key_exists('image', $_GET)) { 
		readfile("c:\inetpub\wwwroot\httptree\pic".$_GET['image'].".gif");
	}
	else readfile("c:\inetpub\wwwroot\httptree\default.gif");
>

Oracle Web Application (2-й способ):

PROCEDURE usp_geticon (image IN NUMBER)
AS
	l_buf        RAW (32000);
	l_bufsize    NUMBER         := 32000;
	l_blob       BLOB;
	l_mimetype   VARCHAR2 (255);
	l_offset     NUMBER         := 1;
	l_length     NUMBER;
BEGIN

	SELECT icon, mimetype
	  INTO l_blob, l_mimetype
	  FROM mm.modelelementicon
	 WHERE modelelementiconid = image;

	OWA_UTIL.mime_header (l_mimetype);
	l_length := DBMS_LOB.getlength (l_blob);

	WHILE l_offset < l_length
	LOOP
		DBMS_LOB.READ (l_blob, l_bufsize, l_offset, l_buf);
		HTP.prn (UTL_RAW.cast_to_varchar2 (l_buf) );
		l_offset := l_offset + l_bufsize;
	END LOOP;
END usp_geticon;
     Вам необходимо запросить поток байт изображения с сервера, преобразовать этот поток в изображение и закэшировать его. Для этого вам понадобится библиотека MSXML, в которую встроены функции отправки на сервер POST и GET запросов (в терминах протокола HTTP):
...
#import "msxml4.dll" named_guids

HBITMAP getBitmap(_bstr_t sourcepath_, _bstr_t szIconId)
{
	MSXML2::IXMLHTTPRequestPtr request_;
	request_.CreateInstance(MSXML2::CLSID_XMLHTTP);

	if(request_ == NULL) throw "error";

	// отправляем GET запрос на Web-сервер
	HRESULT hr = request_->open("GET", sourcepath_ + "?image=" + szIconId, false);

	SUCCEEDED(hr) ? 0 : throw "Can't locate " + sourcepath_;

	hr = request_->send();
	SUCCEEDED(hr) ? 0 : throw "Can't send get request to " + sourcepath_;

	// обрабатываем поток байт присланный сервером в ответ
	HBITMAP hBmpResult = NULL;
	LPPICTURE lpIPicture = NULL; 

	IStreamPtr stream = request_->responseStream;

	if(stream == NULL) return NULL;

	// используем стандартную функцию Win32API, преобразующую 
	// поток байт (BMP/GIF/JPEG и т.п.) в объект IPicture
	//
	if(SUCCEEDED(::OleLoadPicture(stream.GetInterfacePtr(), 0, 
			FALSE, IID_IPicture, (LPVOID*)& lpIPicture ))) 
	{
		if(!lpIPicture) return NULL;

		HBITMAP hBmp = NULL; 
		lpIPicture->get_Handle((OLE_HANDLE*) &hBmp); 

		// копируем изображение для дальнейшего внутреннего использования в дереве
		//
		hBmpResult = (HBITMAP) CopyImage(hBmp, IMAGE_BITMAP, 0, 0, LR_COPYRETURNORG); 
	}
	return hBmpResult;
}
Таким образом, при вызове функции getBitmap вы передаете ей URL к скрипту (хранимой процедуре), который возвращает пиктограмму и собственно идентификатор пиктограммы. Для того, чтобы определить формат (например, BMP или GIF) в которого вам прислана пиктограмма, вы можете определить соответствующий атрибут заголовка запроса:
_bstr_t imageType = request_->getResponseHeader(_T("Content-type"));

if(wcsicmp(imageType, _T("image/bmp")) == 0)
{
	// это BMP файл, приступаем к его распарсиванию
}
else throw "Ничего кроме BMP файлов не поддерживается";
Отправка на Web-сервер массива данных посредством формата multipart form
     Для отправки на Web-сервер некоторого массива значений, с последующей его обработкой процессором PHP или модулем mod_plsql сервера Apache (поддержка Oracle Web Applications) не подходит типичный способ передачи, например, путем подставновки в адресную строку (URL). Спецификации HTTP 1.0/1.1 предусматривают способ передачи больших объемов данных, включая и массивы значений, посредством специального формата multipart/form-data. Данным способом можно передавать и несколько файлов одновременно (массив файлов). Для решения данной задачи не подходит использование MSXML (IXMLHTTPRequest), поскольку эта библиотека не предоставляет средств для формирования тела POST или PUT запросов. Таким образом, родилась идея инкапсулировать функции WinInet API и дополнительные алгоритмы формирования POST запроса в формате multipart/form-data в отчуждаемом классе CHTTPRequestPostMultipart. Далее приводится пример использование разработанного класса:
#include "HTTPRequestPostMultipart.h"

...
tFormFields fields;	// form fields description

fields.push_back("p_type");
fields.push_back("p_id");

// fill data arrays
tFormData data;
std::vector<_bstr_t> itemdata;

for(int i=0; i < 5; i++)
{
	itemdata.reset();

	itemdata.push_back("attribute");
	itemdata.push_back("1");

	// stores values in the matrix of data to be uploaded
	data.push_back(itemdata);

}
...
// use multipart uploading of form data
CHTTPRequestPostMultipart request;

try {
	// connect to the web-server
	if(!request.connect("http://localhost/test.php")) return false;

	// post matrix of values
	if(!request.postData(fields, data)) return false;
}
catch(CHTTPInternetException ex)
{
	... 
}
    
 Evgeny Savitsky © 2002-2004