Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Programming_Windows_95_Part_II.pdf
Скачиваний:
41
Добавлен:
05.06.2014
Размер:
3.02 Mб
Скачать

204

Важно запомнить, что имя, которое легко прочитать человеку (IUnknown), дано исключительно для удобства, но клиенты и серверы OLE для того, чтобы отличить один интерфейс от другого пользуются двоичными цифрами. К счастью, вам нечасто придется иметь дело с такими длинными последовательностями чисел, поскольку для всех идентификаторов интерфейсов имеются символьные константы. В символьных именах к имени интерфейса добавляется префикс "IID_", таким образом IID_IUnknown относится к бинарному интерфейсу для интерфейса IUnknown. Например, клиент, имеющий указатель на компонент интерфейса IMalloc, для получения указателя на интерфейс IMarshal сделал бы такой вызов:

LPMARSHAL pMarshal;

HRESULT hr = pMalloc -> QueryInterface(IID_IMarshal, &pMarshal);

Запомните, что этот вызов является первым запросом: "Поддерживаете ли вы интерфейс Imarshal ?". Ответ либо "Да" (HRESULT равен S_OK), либо "Нет" (HRESULT равен E_NOINTERFACE). Смысл здесь в том, что имеются тысячи интерфейсов, и нет компонента, который бы все их поддерживал.

Если нужный интерфейс поддерживается, возвращаемое значение типа HRESULT равно S_OK. При удачном запросе имеют место две вещи: возвращаемый указатель заносится в память по адресу, на который ссылается второй параметр (в данном примере pMarshal ), и значение счетчика ссылок компонента увеличивается на 1. Увеличение счетчика ссылок отражает тот факт, что создано новое соединение с компонентом. Для закрытия соединения требуется последующий вызов функции Release ():

pMarshal -> Release();

Если, с другой стороны, запрашиваемый интерфейс не поддерживается, то возвращаемым значением функции QueryInterface становится E_NOINTERFACE. В этом случае, указатель не возвращается, и счетчик ссылок не увеличивается. Однако, это будет означать, что функция QueryInterface перезаписывает возвращаемое значение указателя — в нашем случае pMarshal — значением NULL.

Одним из важных достоинств такой возможности запроса является то, что клиенты компонента OLE могут выполнить запрос о возможностях компонента во время выполнения программы. Наличие интерфейса означает существование соответствующих свойств, а его отсутствие означает, что таких свойств нет.

Благодаря этому достоинству относительно просто модернизировать свойства компонента OLE. Однако, новые свойства никогда не добавляются путем расширения функций интерфейса; после того, как интерфейс определен, число его свойств изменить нельзя! Вместо этого новые свойства добавляются путем добавления новых интерфейсов. Существующие интерфейсы остаются неизменными, продолжая поддерживать тех клиентов, которые ожидают их присутствия.

Является ли OLE спецификацией клиент/сервер?

Одним из вопросов, которые задают программисты, начинающие работать с OLE, это вопрос о том, является ли OLE спецификацией клиент/сервер. Вспоминая главу 17, можно сказать, что спецификацией клиент/сервер является DDE. OLE версии 1.0 использовал DDE в качестве транспортного механизма. В этой версии сообщения DDE передавали запросы от клиента OLE к серверу OLE, а сервер OLE отвечал новыми сообщениями DDE, которые ссылались на определенную DDE глобальную (GlobalAlloc) память.

Хотя термины "клиент" и "сервер" часто использовались в контексте OLE версии 1.0, эволюция OLE привела к использованию их только в общем смысле. Когда два компонента взаимодействуют, часто оба одновременно являются как клиентами (пользователями интерфейса), так и серверами (поставщиками интерфейса). В частности, в контексте составных документов, термин "клиент" был заменен на термин "приложение-контейнер". В контексте автоматизации OLE, вместо термина "клиент автоматизации" используется термин "контроллер автоматизации". И, хотя термин "сервер" все еще используется, контекст поставляемой услуги определяет уточняющее дополнение

— "сервер объекта" или "сервер автоматизации".

Термины "клиент" и "сервер" полезны при попытке понять конкретный интерфейс OLE. В частности, эти термины обеспечивают удобную базу для ответа на общий вопрос, который часто возникает при написании программ, использующих OLE: какие интерфейсы должны быть реализованы, а какие интерфейсы реализуются кем-либо еще?

Сервер закрытого компонента

Настало время рассмотреть программу IMALLOC, динамически подключаемую библиотеку, которая демонстрирует то, как реализовать интерфейс. Чтобы сосредоточиться на интерфейсе как таковом, в этом примере оставлено только несколько простых моментов. Во-первых, интерфейс управления памятью OLE IMalloc был выбран потому, что большинство программистов уже хорошо понимает услуги по выделению памяти. Во-вторых, библиотека IMALLOC очень мало реально работает с памятью, поскольку она передает заботы по управлению памятью закрытым функциям работы с "кучей" Win32 (HeapCreate, HeapAlloc и т. д.). И наконец, мы начинаем с

205

закрытого компонента (private component) — доступного только через соответствующий механизм защиты — поскольку, как станет ясно при изучении программы PUBMEM, создание открытого компонента (public component) приводит к дополнительной сложности, без которой сейчас вполне можно обойтись.

Хотя IMALLOC является примером простого компонента, это не означает его тривиальность. В частности, он показывает элементы, которые необходимы при определении интерфейса OLE. Определение интерфейса включает

всебя реализацию трех функций интерфейса IUnknown: QueryInterface, AddRef и Release. В программе используется специальный макрос, который создает переносимые объявления интерфейса для поддержки С и С++

воперационных системах различных платформ, включая Microsoft Windows 95, Microsoft Windows NT, Apple Macintosh и другие системы, в которых будет включена поддержка OLE.

Закрытые компоненты — это такие компоненты OLE, которые видимы только закрытыми средствами. Например, IMALLOC обеспечивает доступ к своему интерфейсу через закрытую, экспортируемую точку входа. Этот пример является динамически подключаемой библиотекой, но при другом использовании закрытых компонентов он мог бы быть целым приложением со множеством закрытых COM-компонентов. Ключевое преимущество здесь состоит в инкапсуляции, которая достигается благодаря четкому разграничению компонента-клиента и компонентасервера. Таким образом, компонент OLE определяется созданием недоступных открыто интерфейсов, поддерживающих спецификации OLE.

После внимательного изучения исходных файлов программы IMALLOC, вы могли бы сказать, что в конечном итоге IMALLOC не является компонентом OLE, поскольку он не делает ни одного вызова функций библиотек OLE. Удивительно, почему IMALLOC является компонентом OLE, если нет ни одного вызова функции OleInitialize (или CoInitialize)? Все просто. Интерфейс IMALLOC создавался в соответствии со спецификацией модели составного объекта, являющейся стандартом OLE. Поскольку услуги библиотек OLE не требуются, IMALLOC обходится без инициализации библиотек OLE, и без загрузки их в память. Но IMALLOC обеспечивает стандартный интерфейс OLE, что станет ясно, когда мы добавим этот интерфейс к образцу открытого компонента PUBMEM, представленного далее в этой главе. Для программы интерфейса IMALLOC почти не потребуются изменения для нормальной работы в открытом компоненте OLE.

Интерфейс IMALLOC выполнен на С++, что имеет смысл для реализации интерфейса, поскольку бинарная модель интерфейса OLE точно соответствует модели объекта С++. Хотя можно реализовать интерфейс и на С, для этого нужно делать кое-какие утомительные вещи, которые автоматически делает компилятор С++. Чтобы понять, как будет выглядеть программа OLE на С, посмотрите на программу CALLER, которая представлена далее в этой главе. Это программа-клиент, которая загружает и вызывает библиотеку IMALLOC.DLL. На рис. 20.2 представлены файлы программы IMALLOC.

IMALLOC.MAK

#-----------------------

# IMALLOC.MAK make file

#-----------------------

imalloc.dll : imalloc.obj

$(LINKER) $(DLLFLAGS) -OUT:imalloc.dll imalloc.obj $(GUILIBS) uuid.lib

imalloc.obj : imalloc.cpp

$(CC) $(CFLAGS) imalloc.cpp

IMALLOC.CPP

/*--------------------------------------------

 

 

IMALLOC.CPP --

Define an imalloc interface

 

(c) Paul Yao, 1996

--------------------------------------------

 

*/

#include <windows.h>

 

#include "imalloc.h"

 

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

 

 

// CreateAllocator --

Exported function to create allocator

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

EXPORT LPMALLOC CreateAllocator()

{

DAlloc *pAllocator = new DAlloc();

if(pAllocator != NULL && pAllocator->Initialize())

{

pAllocator->AddRef();

}

else

206

{

delete pAllocator;

}

return(LPMALLOC) pAllocator;

}

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

DAlloc::DAlloc()

{

RefCount = 0;

hHeap = NULL;

}

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

DAlloc::~DAlloc()

{

if(hHeap)

HeapDestroy(hHeap);

}

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

BOOL DAlloc::Initialize()

{

hHeap = HeapCreate(0, 4096, 65535);

return(BOOL)hHeap;

}

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

STDMETHODIMP

DAlloc::QueryInterface(REFIID riid, LPVOID FAR *ppvObject)

{

//Always initialize "out" parameters to NULL *ppvObject = NULL;

//Everyone supports IUnknown

if(riid == IID_IUnknown) *ppvObject =(LPUNKNOWN) this;

// We support IMalloc if(riid == IID_IMalloc)

*ppvObject =(LPMALLOC) this;

if(*ppvObject == NULL)

{

// Interface not supported return E_NOINTERFACE;

}

else

{

// Interface supported, so increment reference count ((LPUNKNOWN) *ppvObject)->AddRef();

return S_OK;

}

}

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

STDMETHODIMP_(ULONG)

DAlloc::AddRef(void)

{

return ++RefCount;

}

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

STDMETHODIMP_(ULONG)

DAlloc::Release(void)

207

{

if(0L != --RefCount) return RefCount;

delete this; return 0L;

}

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

STDMETHODIMP_(void *)

DAlloc::Alloc(ULONG cb)

{

return HeapAlloc(hHeap, HEAP_ZERO_MEMORY, cb);

}

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

STDMETHODIMP_(void *)

DAlloc::Realloc(void *pv, ULONG cb)

{

return HeapReAlloc(hHeap, HEAP_ZERO_MEMORY, pv, cb);

}

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

STDMETHODIMP_(void)

DAlloc::Free(void *pv)

{

HeapFree(hHeap, 0, pv);

}

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

STDMETHODIMP_(ULONG)

DAlloc::GetSize(void *pv)

{

return HeapSize(hHeap, 0, pv);

}

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

STDMETHODIMP_(int)

DAlloc::DidAlloc(void *pv)

{

PROCESS_HEAP_ENTRY phe;

ZeroMemory(&phe, sizeof(PROCESS_HEAP_ENTRY));

while(HeapWalk(hHeap, &phe))

{

if(phe.lpData == pv) return 1;

}

return 0;

}

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

STDMETHODIMP_(void)

DAlloc::HeapMinimize(void)

{

HeapCompact(hHeap, 0);

}

IMALLOC.H

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

// C Interface to private allocator //-------------------------------------------------------------------

#define EXPORT extern "C" __declspec(dllexport)

Соседние файлы в предмете Операционные системы