- •Содержание
- •Управление памятью: хорошо, плохо и ужасно
- •Сегментированная память
- •Промежуточные решения
- •И, наконец, 32 бита
- •Выделение памяти
- •Библиотечные функции C
- •Фундаментальное выделение памяти в Windows 95
- •Перемещаемая память
- •Удаляемая память
- •Другие функции и флаги
- •Хорошо ли это?
- •Функции работы с "кучей"
- •Файловый ввод/вывод
- •Старый путь
- •Отличия Windows 95
- •Ввод/вывод с использованием файлов, проецируемых в память
- •Режимы многозадачности
- •Многозадачность в DOS
- •Невытесняющая многозадачность
- •Решения, использующие многопоточность
- •Многопоточная архитектура
- •Коллизии, возникающие при использовании потоков
- •Преимущества Windows
- •Новая программа! Усовершенствованная программа! Многопоточная!
- •Многопоточность в Windows 95
- •И снова случайные прямоугольники
- •Задание на конкурсе программистов
- •Решение с использованием многопоточности
- •О пользе использования функции Sleep
- •Синхронизация потоков
- •Критический раздел
- •Объект Mutex
- •Программа BIGJOB1
- •Объект Event
- •Локальная память потока
- •Печать, буферизация и функции печати
- •Контекст принтера
- •Формирование параметров для функции CreateDC
- •Измененная программа DEVCAPS
- •Вызов функции PrinterProperties
- •Проверка возможности работы с битовыми блоками (BitBlt)
- •Программа FORMFEED
- •Печать графики и текста
- •Каркас программы печати
- •Прерывание печати с помощью процедуры Abort
- •Реализация процедуры прерывания
- •Добавление диалогового окна печати
- •Добавление печати к программе POPPAD
- •Обработка кодов ошибок
- •Техника разбиения на полосы
- •Разбиение на полосы
- •Реализация разбиения страницы на полосы
- •Принтер и шрифты
- •Глава 16 Буфер обмена
- •Простое использование буфера обмена
- •Стандартные форматы данных буфера обмена
- •Передача текста в буфер обмена
- •Получение текста из буфера обмена
- •Открытие и закрытие буфера обмена
- •Использование буфера обмена с битовыми образами
- •Метафайл и картина метафайла
- •Более сложное использование буфера обмена
- •Использование нескольких элементов данных
- •Отложенное исполнение
- •Нестандартные форматы данных
- •Соответствующая программа просмотра буфера обмена
- •Цепочка программ просмотра буфера обмена
- •Функции и сообщения программы просмотра буфера обмена
- •Простая программа просмотра буфера обмена
- •Основные концепции
- •Приложение, раздел и элемент
- •Типы диалогов
- •Символьные строки и атомы
- •Программа сервер DDE
- •Программа DDEPOP1
- •Сообщение WM_DDE_INITIATE
- •Оконная процедура ServerProc
- •Функция PostDataMessage программы DDEPOP1
- •Сообщение WM_DDE_ADVISE
- •Обновление элементов данных
- •Сообщение WM_DDE_UNADVISE
- •Сообщение WM_DDE_TERMINATE
- •Программа-клиент DDE
- •Инициирование диалога DDE
- •Сообщение WM_DDE_DATA
- •Сообщение WM_DDE_TERMINATE
- •Управляющая библиотека DDE
- •Концептуальные различия
- •Реализация DDE с помощью DDEML
- •Элементы MDI
- •Windows 95 и MDI
- •Пример программы
- •Три меню
- •Инициализация программы
- •Создание дочерних окон
- •Дополнительная информация об обработке сообщений в главном окне
- •Дочерние окна документов
- •Освобождение захваченных ресурсов
- •Сила оконной процедуры
- •Основы библиотек
- •Библиотека: одно слово, множество значений
- •Пример простой DLL
- •Разделяемая память в DLL
- •Библиотека STRLIB
- •Точка входа/выхода библиотеки
- •Программа STRPROG
- •Работа программы STRPROG
- •Разделение данных между экземплярами программы STRPROG
- •Некоторые ограничения библиотек
- •Динамическое связывание без импорта
- •Библиотеки, содержащие только ресурсы
- •Глава 20 Что такое OLE?
- •Основы OLE
- •Связь с библиотеками OLE
- •Расшифровка кода результата
- •Интерфейсы модели составного объекта (COM-интерфейсы)
- •Услуги интерфейса IUnknown
- •Является ли OLE спецификацией клиент/сервер?
- •Сервер закрытого компонента
- •IMALLOC.DLL
- •Теперь о макросах
- •Услуги, предоставляемые интерфейсом IUnknown
- •Клиент закрытого компонента
- •Сервер открытого компонента
- •Назначение реестра
- •Способы генерации и использования идентификаторов CLSID
- •Компонент фабрика классов
- •Управление временем жизни сервера
- •Клиент открытого компонента
- •Заключение
233
OLE, клиенты вызывают вспомогательную функцию библиотеки OLE — функцию CoCreateInstance. Эта функция определяется следующим образом:
STDAPI CoCreateInstance( |
// Идентификатор класса |
|
|
REFCLSID rclsid, |
|
|
LPUNKNOWN pUnkOuter, |
// Указатель IUnknown |
|
DWORD dwClsContext, |
// Контекст сервера |
|
REFIID riid, |
// Идентификатор интерфейса |
|
LPVOID *ppv |
|
); |
// Возвращаемое значение |
|
•rclsid задает идентификатор класса компонента для его создания.
•pUnkOuter задает указатель интерфейса IUnknown для создания агрегированных объектов.
•dwClsContext идентифицирует контекст компонента сервера. Может быть равен CLSCTX_INPROC_SERVER для сервера динамически подключаемой библиотеки, CLSCTX_INPROC_HANDLER для обработчика динамически подключаемой библиотеки (функциипосредники для сервера вне процесса клиента) и CLSCTX_LOCAL_SERVER для локального сервера (различные процессы на одной машине). Для сетевого сервера никакого флага не задается.
•riid идентифицирует требуемый в качестве возвращаемого значения интерфейс.
•ppv указывает на положение значения возвращаемого интерфейса, в случае успешного создания компонента или на NULL, если компонента с требуемым интерфейсом создать невозможно.
Функция LockServer интерфейса IClassFactory как увеличивает, так и уменьшает на 1 значение счетчика захвата сервера. Она сочетает в одной функции сервера задачи, которые выполняются функциями-членами AddRef и Release интерфейса IUnknown. Исключение состоит в том, что сервер никогда не освобождает сам себя, как это делает объект компонента. Это приводит к важному результату в реализации сервера, а именно, в управлении временем жизни сервера.
Управление временем жизни сервера
Время жизни сервера является столь же важной проблемой при реализации сервера, как и время жизни компонента
— при реализации компонента. Это верно и для однокомпонентного сервера, как в библиотеке PUBMEM, и для многокомпонентного сервера. Механизмы управления временем жизни у обоих типов одинаковы. Мы рассматриваем время жизни сервера, основанного на динамически подключаемых библиотеках. Хотя факторы, влияющие на время жизни сервера, основанного на файлах с расширением .EXE те же самые, детали реализации разные. (За подробностями обращайтесь к книге "Inside OLE" (OLE изнутри), автора Brockschmidt.)
Сервер динамически подключаемой библиотеки загружается в память также, как и другие динамически подключаемые библиотеки Windows — с помощью вызова функции LoadLibrary. Факт, что в библиотеках OLE имеется собственная версия этой функции CoLoadLibrary, не меняет того, что элемент, не относящийся к динамически подключаемым библиотекам загружает их в память. (Функция CoLoadLibrary создавалась для упрощения установки OLE на платформы, не являющиеся платформами Windows.) Динамически подключаемую библиотеку выгружает из памяти последующий вызов функции CoFreeLibrary.
Время жизни сервера динамически подключаемой библиотеки является проблемой, поскольку клиент непосредственно не загружает библиотеку сервера в оперативную память — это делают библиотеки OLE. Поэтому вовремя выгрузить сервер тоже является задачей библиотек OLE. Но большая часть взаимодействий между клиентом и сервером игнорирует библиотеки OLE, которые поэтому, кроме как по запросу, не имеют возможности узнать о наступлении подходящего времени для удаления сервера из оперативной памяти.
Библиотеки OLE периодически вызывают точку входа сервера-функцию DllCanUnloadNow, с запросом о том, не пора ли выгружать сервер. Функция отвечает"Да" (S_OK) или "Нет" (S_FALSE):
HRESULT APIENTRY DllCanUnloadNow(void)
{
if(cObject > 0 || cLockCount > 0)
{
return S_FALSE;
}
else
{
return S_OK;
}
}
Ответ основан на одном из двух факторов: существует ли хоть один компонент и зафиксирован ли в памяти сервер клиентом. Последнее означает ситуацию, в которой ни одного компонента не существует, но — чтобы избежать
234
дополнительной перезагрузки в память динамически подключаемой библиотеки — клиент хочет, чтобы сервер не выгружался.
Счетчик захвата сервера управляется функцией-членом LockServer интерфейса IClassFactory. Эта функция делает несколько больше, чем просто увеличивает и уменьшает на 1 счетчик захвата, который представляет из себя нечто большее, чем просто глобальную переменную, которую поддерживает сервер. Далее представлена функция-член
LockServer интерфейса IClassFactory из библиотеки PUBMEM:
STDMETHODIMP DClassFactory::LockServer(BOOL fLock)
{
if(fLock)
{
++cLockCount;
}
else
{
--cLockCount;
}
return NOERROR;
}
Другим фактором, управляющим временем жизни сервера, является счетчик компонентов. В библиотеке PUBMEM этот счетчик увеличивается на 1 внутри функции-члена DClassFactory::CreateInstance при удачном создании запрашиваемого компонента OLE:
// Создание объекта выделения памяти
LPMALLOC pMalloc = CreateAllocator();
if(pMalloc == NULL)
{
return E_OUTOFMEMORY;
}
else
{
// Выбор интерфейса, требуемого процедурой вызова
HRESULT hr = pMalloc -> QueryInterface(riid, ppvObject);
// Уменьшение на 1 счетчика ссылок, увеличенного функцией CreateAllocator pMalloc -> Release();
// Увеличение на 1 счетчика объектов
If(SUCCEEDED(hr))
++cObject;
return hr;
}
Этот счетчик уменьшается на 1, когда компонент удаляет сам себя, что происходит, когда в функции Release члене интерфейса IMalloc счетчик ссылок становится равным нулю:
STDMETHODIMP_(ULONG) DAlloc::Release(void)
{
if(0L != RefCount) return RefCount;
--cObject; delete this; return 0L;
}
Между прочим, это все изменения, которые необходимо внести при преобразовании закрытого компонента в открытый.
Настало время рассмотреть создание клиента открытого компонента, что демонстрирует следующий пример программы.