- •Содержание
- •Управление памятью: хорошо, плохо и ужасно
- •Сегментированная память
- •Промежуточные решения
- •И, наконец, 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
- •Компонент фабрика классов
- •Управление временем жизни сервера
- •Клиент открытого компонента
- •Заключение
221
MENUITEM "&Create", |
IDM_CREATE |
MENUITEM "&Destroy", |
IDM_DESTROY |
MENUITEM SEPARATOR |
|
MENUITEM "QueryInterface IID_IUnknown", IDM_IUNKNOWN |
|
MENUITEM "QueryInterface IID_IMalloc", |
IDM_IMALLOC |
MENUITEM "QueryInterface IID_IMarshal", IDM_IMARSHAL
}
POPUP "&Memory"
{
MENUITEM "&Allocate(IMalloc)", IDM_ALLOCATE_CUSTOM
MENUITEM "&Allocate(malloc)", IDM_ALLOCATE_DEFAULT
MENUITEM "&Free", IDM_FREE
}
}
Рис. 20.4 Программа CALLER
Программа CALLER непосредственно не использует библиотеки OLE и, следовательно, как и представленная ранее библиотека IMALLOC.DLL, программа CALLER не инициализирует библиотеки OLE. (Это изменится после модернизации программы CALLER в программу CALLPUB, представленную далее в этой главе; так же как и наследник библиотеки IMALLOC — библиотека PUBMEM, тоже вынуждена иметь дело с библиотеками OLE и, следовательно, иметь соответствующие функции инициализации и удаления.)
Весь доступ к услугам интерфейса IMalloc в программе CALLER происходит посредством выбора пунктов меню, что обеспечивает полный контроль над тем, что вызывается и тем, когда это вызывается. Первое, что необходимо сделать при запуске программы CALLER — это создать компонент для выделения памяти, что достигается посредством выбора опции Create из меню Allocator. Это приводит к вызову функции CreateAllocator библиотеки IMALLOC, возвращаемым значением которой является указатель на интерфейс, требуемый для доступа к интерфейсу процедуры выделения памяти библиотеки IMALLOC.
При выборе опции Destroy из меню Allocator, компонент для выделения памяти удаляется благодаря уменьшению значения счетчика ссылок при вызове функции Release:
pMalloc -> lpVtbl -> Release(pMalloc); pMalloc = NULL;
Устанавливать значение только что освобожденного указателя на интерфейс в NULL очень полезно. Это гарантирует, что его невозможно будет снова использовать без генерации исключения (что означает программную ошибку, от которой вы, будем надеяться, избавитесь до того, как начнете распространять вашу программу).
Программа CALLER вызывает функцию-член QueryInterface из процедуры выделения памяти для проверки поддерживаемых интерфейсов. С позиции клиента, важно всегда вызывать функцию Release, когда вызов функции QueryInterface возвращает указатель на интерфейс. В противном случае, вы рискуете оставить компонент в оперативной памяти со счетчиком ссылок большим нуля, хотя клиент считает, что он освободил ресурсы.
Теперь обратимся к тому, что представляет собой создание сервера открытого компонента.
Сервер открытого компонента
Библиотека PUBMEM.DLL демонстрирует создание сервера открытого компонента OLE. Отличие между открытым и закрытым компонентами заключается не в самом интерфейсе; фактически открытый интерфейс IMalloc, поставляемый библиотекой PUBMEM идентичен (за небольшим исключением) закрытому интерфейсу, поставляемому в представленном ранее образце библиотеки IMALLOC. Открытый компонент OLE — это компонент, созданный из класса открытого компонента OLE.
Класс открытого компонента OLE требует координации нескольких элементов. Класс компонента имеет уникальный идентификатор класса (CLSID) и элемент реестра (registry), который связывает идентификатор класса с инсталлированными файлами с расширением .EXE и .DLL. Сам компонент должен обеспечивать стандартный механизм для создания компонента по запросу, который называется фабрикой классов (class factory). Фабрика классов является закрытым компонентом OLE, созданным посредством вызова экспортируемой функции DllGetClassObject сервера DLL, которая обеспечивает услуги интерфейса IClassFactory. Библиотеки OLE периодически вызывают вторую точку входа — функцию DllCanUnloadNow, для проверки того, безопасно ли для состояния DLL удалить компонент. Будучи более сложной, чем закрытый компонент, фабрика классов обеспечивает базовый механизм для выдачи многих типов классов компонентов, каждый из которых предусматривает несколько разных интерфейсов. На рис. 20.5 представлены файлы с исходным текстом библиотеки PUBMEM.
PUBMEM.MAK
222
#----------------------
# PUBMEM.MAK make file
#----------------------
pubmem.dll : pubmem.obj classfac.obj compobj.obj
$(LINKER) /EXPORT:DllGetClassObject /EXPORT:DllCanUnloadNow \ $(DLLFLAGS) -OUT:pubmem.dll pubmem.obj \
classfac.obj compobj.obj $(GUILIBS) uuid.lib ole32.lib
pubmem.obj : pubmem.cpp pubmem.h $(CC) $(CFLAGS) pubmem.cpp
classfac.obj : classfac.cpp pubmem.h $(CC) $(CFLAGS) classfac.cpp
compobj.obj : compobj.cpp pubmem.h $(CC) $(CFLAGS) compobj.cpp
PUBMEM.CPP
/*------------------------------------------------- |
|
|
PUBMEM.CPP -- |
Define a public imalloc component |
|
|
(c) Paul Yao, 1996 |
|
------------------------------------------------- |
|
*/ |
#include <windows.h> |
|
|
#include "pubmem.h" |
|
|
extern int cObject; |
|
|
//------------------------------------------------------------------- |
|
|
// CreateAllocator -- |
Exported function to create allocator |
|
//------------------------------------------------------------------- |
|
|
EXPORT LPMALLOC CreateAllocator()
{
DAlloc *pAllocator = NULL;
pAllocator = new DAlloc();
if(pAllocator != NULL && pAllocator->Initialize())
{
pAllocator->AddRef();
}
else
{
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;
223
}
//-------------------------------------------------------------------
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)
{
if(0L != --RefCount) return RefCount;
--cObject; 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)
224
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);
}
CLASSFAC.CPP
/*--------------------------------------------- |
|
CLASSFAC.CPP -- |
OLE Class Factory component |
|
(c) Paul Yao, 1996 |
--------------------------------------------- |
*/ |
#include <windows.h> #include <initguid.h> #include "pubmem.h"
extern int cObject; extern int cLockCount;
//-------------------------------------------------------------------
DClassFactory::DClassFactory()
{
RefCount = 0;
}
//-------------------------------------------------------------------
DClassFactory::~DClassFactory()
{
}
//-------------------------------------------------------------------
STDMETHODIMP
DClassFactory::QueryInterface(REFIID riid, LPVOID FAR *ppvObj)
{
// Init recipient's pointer *ppvObj = NULL;
// If asking for IUnknown, we can provide
225
if(riid == IID_IUnknown) *ppvObj =(LPUNKNOWN) this;
//If asking for IClassFactory, we can provide if(riid == IID_IClassFactory)
*ppvObj =(LPCLASSFACTORY) this;
//Make sure reference count reflects access if(*ppvObj == NULL)
{
//Interface not supported
return E_NOINTERFACE;
}
else
{
// Interface supported to increment reference count ((LPUNKNOWN) *ppvObj)->AddRef();
return S_OK;
}
}
//-------------------------------------------------------------------
STDMETHODIMP_(ULONG)
DClassFactory::AddRef()
{
return ++RefCount;
}
//-------------------------------------------------------------------
STDMETHODIMP_(ULONG)
DClassFactory::Release()
{
if(0L != --RefCount)
return RefCount;
delete this; return 0L;
}
//-------------------------------------------------------------------
STDMETHODIMP
DClassFactory::CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid,
LPVOID FAR *ppvObject)
{
//Initialize return pointer *ppvObject = NULL;
//If trying to aggregate, fail if(pUnkOuter != NULL)
return CLASS_E_NOAGGREGATION;
//Create memory allocation object LPMALLOC pMalloc = CreateAllocator();
if(pMalloc == NULL)
{
return E_OUTOFMEMORY;
}
else
{
// Fetch interface requested by caller
HRESULT hr = pMalloc->QueryInterface(riid, ppvObject);
// Decrement reference count produced by CreateAllocator pMalloc->Release();
226
// Increment count of objects
if(SUCCEEDED(hr))
++cObject;
return hr;
}
}
//-------------------------------------------------------------------
STDMETHODIMP
DClassFactory::LockServer(BOOL fLock)
{
if(fLock)
{
++cLockCount;
}
else
{
--cLockCount;
}
return NOERROR;
}
COMPOBJ.CPP
/*---------------------------------------------- |
|
COMPOBJ.CPP |
-- Component Object registration |
|
(c) Paul Yao, 1996 |
---------------------------------------------- |
*/ |
#include <windows.h> |
|
#include "pubmem.h" |
|
int cObject |
= 0; |
int cLockCount = 0;
//-------------------------------------------------------------------
HRESULT APIENTRY
DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppvObj)
{
//Initialize "out" pointer to known value *ppvObj = NULL;
if(rclsid != CLSID_ALLOCATOR)
{
return CLASS_E_CLASSNOTAVAILABLE;
}
DClassFactory *pClassFactory = new DClassFactory(); if(pClassFactory == NULL)
{
return E_OUTOFMEMORY;
}
else
{
return pClassFactory->QueryInterface(riid, ppvObj);
}
}
//-------------------------------------------------------------------
HRESULT APIENTRY
DllCanUnloadNow(void)
{
if(cObject > 0 || cLockCount > 0)
{
return S_FALSE;
227
}
else
{
return S_OK;
}
}
PUBMEM.H
//-------------------------------------------------------------------
// C Interface to private allocator //-------------------------------------------------------------------
#define EXPORT extern "C" __declspec(dllexport)
EXPORT LPMALLOC CreateAllocator();
// {308D0430-1090-11cf-B92A-00AA006238F8} DEFINE_GUID(CLSID_ALLOCATOR,
0x308d0430, 0x1090, 0x11cf, 0xb9,
0x2a, 0x0, 0xaa, 0x0, 0x62, 0x38, 0xf8);
//-------------------------------------------------------------------
// Implementation of allocator interface //-------------------------------------------------------------------
#undef INTERFACE #define INTERFACE DAlloc
DECLARE_INTERFACE_(DAlloc, IMalloc)
{
// *** IUnknown methods ***
STDMETHOD (QueryInterface)(THIS_ REFIID riid, LPVOID FAR *ppv);
STDMETHOD_(ULONG, AddRef) (THIS);
STDMETHOD_(ULONG, Release)(THIS);
// *** IMalloc methods *** |
|
|
STDMETHOD_(void *, Alloc) |
(THIS_ ULONG cb); |
|
STDMETHOD_(void *, Realloc) |
(THIS_ void *pv, ULONG cb); |
|
STDMETHOD_(void, |
Free) |
(THIS_ void *pv); |
STDMETHOD_(ULONG, |
GetSize) |
(THIS_ void *pv); |
STDMETHOD_(int, |
DidAlloc) |
(THIS_ void *pv); |
STDMETHOD_(void, |
HeapMinimize)(THIS); |
#ifndef CINTERFACE public :
DAlloc();
~DAlloc();
BOOL Initialize(); private :
ULONG RefCount;
HANDLE hHeap; #endif
};
// Class Factory #undef INTERFACE
#define INTERFACE DClassFactory
DECLARE_INTERFACE_(DClassFactory, IClassFactory)
{
// *** IUnknown methods ***
STDMETHOD (QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppv);
STDMETHOD_(ULONG, AddRef) (THIS);
STDMETHOD_(ULONG, Release)(THIS);
// *** IClassFactory methods ***
STDMETHOD(CreateInstance)(THIS_ LPUNKNOWN pUnkOuter,