- •Содержание
- •Управление памятью: хорошо, плохо и ужасно
- •Сегментированная память
- •Промежуточные решения
- •И, наконец, 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
- •Компонент фабрика классов
- •Управление временем жизни сервера
- •Клиент открытого компонента
- •Заключение
181
Рис. 19.3 Библиотека STRLIB
Точка входа/выхода библиотеки
Как можно заметить, в файле STRLIB.С мы некоторым образом задействовали функцию DllMain. Эта функция вызывается при первом запуске библиотеки и при завершении ее работы. Хотя функция DllMain включена еще и в EDRLIB.С, в действительности это не обязательно; функция, выполняющая такие действия, была бы включена туда компоновщиком по умолчанию.
Первым параметром функции DllMain является описатель экземпляра библиотеки. Если в библиотеке используются ресурсы, для которых требуется описатель экземпляра (например, DialogBox), необходимо сохранить hInstance в глобальной переменной. Последний параметр функции DllMain резервируется системой.
Параметр fdwReason может принимать одно из четырех значений, которое идентифицирует причину вызова функции DllMain системой Windows 95. Перед тем, как продолжить, запомните, что одна и та же программа может быть загружена несколько раз, и ее экземпляры могут вместе работать под Windows. Каждая загруженная программа рассматривается как отдельный процесс.
Значение DLL_PROCESS_ATTACH параметра fdwReason означает, что динамически подключаемая библиотека отображена в адресном пространстве процесса. Это сигнал библиотеке выполнить какие-то задачи по инициализации, которые требуются для обслуживания последующих запросов от процесса. Такая инициализация может включать в себя, например, выделение памяти. Во время выполнения программы функция DllMain вызывается с параметром DLL_PROCESS_ATTACH только однажды. Любой другой процесс, использующий ту же динамически подключаемую библиотеку, приводит к новому вызову функции DllMain с параметром DLL_PROCESS_ATTACH, но это происходит уже от имени нового процесса.
Если инициализация проходит удачно, возвращаемым значением функции DllMain должно быть ненулевое значение. Нулевое возвращаемое значение приведет к тому, что Windows не запустит программу.
Если параметр fdwReason имеет значение DLL_PROCESS_DETACH, то это означает, что динамически подключаемая библиотека больше процессу не нужна. Это дает возможность библиотеке освободить занимаемые ресурсы системы. В Windows 95 это не является абсолютно необходимым, но это хороший стиль программирования.
Аналогично, если функция DllMain вызывается с параметром DLL_THREAD_ATTACH, это означает, что связанный процесс создал новый поток. Когда поток завершается, Windows вызывает функцию DllMain с параметром DLL_THREAD_DETACH. Запомните, что существует вероятность того, что вызов функции DllMain с параметром DLL_THREAD_DETACH произойдет без предварительного вызова ее с параметром DLL_THREAD_ATTACH. Это возможно в том случае, если библиотека была загружена после создания потока.
Когда функция DllMain вызывается с параметром DLL_THREAD_DETACH, поток еще существует. Динамически подключаемая библиотека может даже посылать сообщения потоку в этот момент. Но при этом нельзя использовать функцию PostMessage, поскольку поток, вероятно, закончится еще до того, как такое сообщение будет обработано.
Кроме функции DllMain, в STRLIB имеется только три функции, которые будут экспортированы для использования другими программами. Все эти функции определяются как EXPORT. Это приводит к тому, что компоновщик заносит их в библиотеку импорта STRLIB.LIB.
Программа STRPROG
Программа STRPROG, представленная на рис. 19.4, совершенно проста и понятна. Две опции меню (Enter и Delete) вызывают появление окон диалога для ввода строки. Затем в программе STRPROG вызываются функции AddString и DeleteString. Если программе нужно обновить свою рабочую область, вызывается функция GetStrings, а для получения списка перенумерованных строк вызывается функция GetStrCallBack.
STRPROG.MAK
#-----------------------
# STRPROG.MAK make file
#-----------------------
strprog.exe : strprog.obj strprog.res strlib.lib
$(LINKER) $(GUIFLAGS) -OUT:strprog.exe strprog.obj strprog.res \ strlib.lib $(GUILIBS)
strprog.obj : strprog.c strprog.h strlib.h $(CC) $(CFLAGS) strprog.c
182
strprog.res : strprog.rc strprog.h
$(RC) $(RCVARS) strprog.rc
STRPROG.C
/*--------------------------------------------------------
STRPROG.C -- Program using STRLIB dynamic link library
(c) Charles Petzold, 1996
--------------------------------------------------------*/
#include <windows.h> #include <string.h> #include "strprog.h" #include "strlib.h"
#define MAXLEN 32
#define WM_DATACHANGE WM_USER
typedef struct
{
HDC hdc; int xText; int yText; int xStart; int yStart; int xIncr; int yIncr; int xMax; int yMax;
}
CBPARAM;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
char szAppName[] = "StrProg"; char szString[MAXLEN];
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{ |
|
HWND |
hwnd; |
MSG |
msg; |
WNDCLASSEX |
wndclass; |
wndclass.cbSize |
= sizeof(wndclass); |
wndclass.style |
= CS_HREDRAW | CS_VREDRAW; |
wndclass.lpfnWndProc |
= WndProc; |
wndclass.cbClsExtra |
= 0; |
wndclass.cbWndExtra |
= 0; |
wndclass.hInstance |
= hInstance; |
wndclass.hIcon |
= LoadIcon(NULL, IDI_APPLICATION); |
wndclass.hCursor |
= LoadCursor(NULL, IDC_ARROW); |
wndclass.hbrBackground |
=(HBRUSH) GetStockObject(WHITE_BRUSH); |
wndclass.lpszMenuName |
= szAppName; |
wndclass.lpszClassName |
= szAppName; |
wndclass.hIconSm |
= LoadIcon(NULL, IDI_APPLICATION); |
RegisterClassEx(&wndclass);
hwnd = CreateWindow(szAppName, "DLL Demonstration Program", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, iCmdShow);
183
UpdateWindow(hwnd);
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
BOOL CALLBACK DlgProc(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
switch(iMsg)
{
case WM_INITDIALOG :
SendDlgItemMessage(hDlg, IDD_STRING, EM_LIMITTEXT,
MAXLEN - 1, 0);
return TRUE;
case WM_COMMAND : switch(wParam)
{
case IDOK :
GetDlgItemText(hDlg, IDD_STRING, szString, MAXLEN);
EndDialog(hDlg, TRUE);
return TRUE;
case IDCANCEL : EndDialog(hDlg, FALSE); return TRUE;
}
}
return FALSE;
}
BOOL CALLBACK EnumCallBack(HWND hwnd, LPARAM lParam)
{
char szClassName[16];
GetClassName(hwnd, szClassName, sizeof(szClassName));
if(0 == strcmp(szClassName, szAppName)) SendMessage(hwnd, WM_DATACHANGE, 0, 0);
return TRUE;
}
BOOL CALLBACK GetStrCallBack(PSTR pString, CBPARAM *pcbp)
{
TextOut(pcbp->hdc, pcbp->xText, pcbp->yText, pString, strlen(pString));
if((pcbp->yText += pcbp->yIncr) > pcbp->yMax)
{
pcbp->yText = pcbp->yStart;
if((pcbp->xText += pcbp->xIncr) > pcbp->xMax) return FALSE;
}
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{ |
|
static HINSTANCE |
hInst; |
static int |
cxChar, cyChar, cxClient, cyClient; |
184
CBPARAM |
cbparam; |
HDC |
hdc; |
PAINTSTRUCT |
ps; |
TEXTMETRIC |
tm; |
switch(iMsg)
{
case WM_CREATE :
hInst =((LPCREATESTRUCT) lParam)->hInstance; hdc = GetDC(hwnd);
GetTextMetrics(hdc, &tm); cxChar =(int) tm.tmAveCharWidth;
cyChar =(int)(tm.tmHeight + tm.tmExternalLeading); ReleaseDC(hwnd, hdc);
return 0;
case WM_COMMAND : switch(wParam)
{
case IDM_ENTER :
if(DialogBox(hInst, "EnterDlg", hwnd, &DlgProc))
{
if(AddString(szString)) EnumWindows(&EnumCallBack, 0);
else
MessageBeep(0);
}
break;
case IDM_DELETE :
if(DialogBox(hInst, "DeleteDlg", hwnd, &DlgProc))
{
if(DeleteString(szString)) EnumWindows(&EnumCallBack, 0);
else
MessageBeep(0);
}
break;
}
return 0;
case WM_SIZE :
cxClient =(int) LOWORD(lParam); cyClient =(int) HIWORD(lParam); return 0;
case WM_DATACHANGE : InvalidateRect(hwnd, NULL, TRUE); return 0;
case WM_PAINT :
hdc = BeginPaint(hwnd, &ps);
cbparam.hdc |
= hdc; |
cbparam.xText |
= cbparam.xStart = cxChar; |
cbparam.yText |
= cbparam.yStart = cyChar; |
cbparam.xIncr |
= cxChar * MAXLEN; |
cbparam.yIncr |
= cyChar; |
cbparam.xMax |
= cbparam.xIncr *(1 + cxClient / cbparam.xIncr); |
cbparam.yMax |
= cyChar *(cyClient / cyChar - 1); |
GetStrings((PSTRCB) GetStrCallBack,(PVOID) &cbparam);
EndPaint(hwnd, &ps);