
- •Содержание
- •Управление памятью: хорошо, плохо и ужасно
- •Сегментированная память
- •Промежуточные решения
- •И, наконец, 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
- •Компонент фабрика классов
- •Управление временем жизни сервера
- •Клиент открытого компонента
- •Заключение

137
либо формат данных, либо NULL, что означает — данные всех форматов. Старшим словом параметра lParam этого сообщения является либо элемент ATOM, либо NULL, что означает — все элементы.
В программе DDEPOP1 сообщение WM_DDE_UNADVISE обрабатывается путем присвоения соответствующим полям fAdvise структуры POPADVISE значения FALSE и последующим позитивным или негативным подтверждением путем посылки сообщения WM_DDE_ACK.
Сообщение WM_DDE_TERMINATE
Когда клиент решает закончить диалог, он посылает серверу синхронное сообщение WM_DDE_TERMINATE. Сервер просто отвечает клиенту собственным сообщением WM_DDE_TERMINATE. Кроме этого, ServerProc после получения сообщения WM_DDE_TERMINATE удаляет дочернее окно, поскольку оно больше не нужно, и диалог, который это окно поддерживало, завершается.
ServerProc также обрабатывает сообщения WM_DDE_POKE и WM_DDE_EXECUTE, но в обоих случаях она просто отвечает негативным подтверждением получения.
Если программа DDEPOP1 закрывается из собственного системного меню, то она должна завершить все диалоги со своими клиентами. Поэтому, когда WndProc получает сообщение WM_CLOSE, она вызывает функцию
EnumChildWindows с функцией CloseEnumProc. Функция CloseEnumProc посылает всем дочерним окнам асинхронное сообщение WM_CLOSE.
ServerProc отвечает на сообщение WM_CLOSE путем отправки клиенту синхронного сообщения WM_DDE_TERMINATE и затем ждет от клиента ответного сообщения WM_DDE_TERMINATE.
Программа-клиент DDE
Теперь, после того, как мы рассмотрели программу-сервер DDE, которую можно использовать для работы с Microsoft Excel, рассмотрим программу-клиент DDE, сервером для которой является программа DDEPOP1. Эта программа, названная SHOWPOP1, представлена на рис. 17.2.
SHOWPOP1.MAK
#------------------------
# SHOWPOP1.MAK make file
#------------------------
showpop1.exe : showpop1.obj
$(LINKER) $(GUIFLAGS) -OUT:showpop1.exe showpop1.obj $(GUILIBS)
showpop1.obj : showpop1.c showpop.h $(CC) $(CFLAGS) showpop1.c
SHOWPOP1.C
/*----------------------------------------- |
|
SHOWPOP1.C -- |
DDE Client using DDEPOP1 |
|
(c) Charles Petzold, 1996 |
----------------------------------------- |
*/ |
#include <windows.h> #include <dde.h> #include <stdlib.h> #include <string.h> #include "showpop.h"
#define |
WM_USER_INITIATE(WM_USER + 1) |
|
#define |
DDE_TIMEOUT |
3000 |
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
char szAppName[] = "ShowPop1";
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
HWND hwnd;
MSG msg;

138
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(hInstance, szAppName); |
wndclass.hCursor |
= LoadCursor(NULL, IDC_ARROW); |
wndclass.hbrBackground |
=(HBRUSH) GetStockObject(WHITE_BRUSH); |
wndclass.lpszMenuName |
= NULL; |
wndclass.lpszClassName |
= szAppName; |
wndclass.hIconSm |
= LoadIcon(hInstance, szAppName); |
RegisterClassEx(&wndclass);
hwnd = CreateWindow(szAppName, "DDE Client - US Population", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
SendMessage(hwnd, WM_USER_INITIATE, 0, 0L);
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
static BOOL static char
static HWND static long ATOM
char DDEACK DDEDATA DDEADVISE DWORD
GLOBALHANDLE
HDC
MSG PAINTSTRUCT short
long TEXTMETRIC WORD
UINT
switch(iMsg)
{
case WM_CREATE :
hdc = GetDC(hwnd); GetTextMetrics(hdc, &tm); cxChar = tm.tmAveCharWidth;
cyChar = tm.tmHeight + tm.tmExternalLeading;

139
ReleaseDC(hwnd, hdc); return 0;
case WM_USER_INITIATE :
// Broadcast WM_DDE_INITIATE iMsg
aApp = GlobalAddAtom(szServerApp);
aTop = GlobalAddAtom(szTopic);
SendMessage(HWND_BROADCAST, WM_DDE_INITIATE,(WPARAM) hwnd,
MAKELONG(aApp, aTop));
// If no response, try loading DDEPOP first
if(hwndServer == NULL)
{
WinExec(szServerApp, SW_SHOWMINNOACTIVE);
SendMessage(HWND_BROADCAST, WM_DDE_INITIATE,(WPARAM) hwnd,
MAKELONG(aApp, aTop));
}
// Delete the atoms
GlobalDeleteAtom(aApp);
GlobalDeleteAtom(aTop);
fDoingInitiate = FALSE;
// If still no response, display message box
if(hwndServer == NULL)
{
MessageBox(hwnd, "Cannot connect with DDEPOP1.EXE!", szAppName, MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Post WM_DDE_ADVISE iMsgs
for(i = 0; i < NUM_STATES; i++)
{
hDdeAdvise = GlobalAlloc(GHND | GMEM_DDESHARE,
sizeof(DDEADVISE));
pDdeAdvise =(DDEADVISE *) GlobalLock(hDdeAdvise);
pDdeAdvise->fAckReq = TRUE;
pDdeAdvise->fDeferUpd = FALSE;
pDdeAdvise->cfFormat = CF_TEXT;
GlobalUnlock(hDdeAdvise);
aItem = GlobalAddAtom(pop[i].szAbb);
if(!PostMessage(hwndServer, WM_DDE_ADVISE,(WPARAM) hwnd, PackDDElParam(WM_DDE_ADVISE,
(UINT) hDdeAdvise, aItem)))
{
GlobalFree(hDdeAdvise);
GlobalDeleteAtom(aItem);
break;
}

140
DdeAck.fAck = FALSE;
dwTime = GetCurrentTime();
while(GetCurrentTime() - dwTime < DDE_TIMEOUT)
{
if(PeekMessage(&msg, hwnd, WM_DDE_ACK, WM_DDE_ACK, PM_REMOVE))
{
GlobalDeleteAtom(HIWORD(msg.lParam));
wStatus = LOWORD(msg.lParam); DdeAck = *((DDEACK *) &wStatus);
if(DdeAck.fAck == FALSE) GlobalFree(hDdeAdvise);
break;
}
}
if(DdeAck.fAck == FALSE) break;
while(PeekMessage(&msg, hwnd, WM_DDE_FIRST, WM_DDE_LAST, PM_REMOVE))
{
DispatchMessage(&msg);
}
}
if(i < NUM_STATES)
{
MessageBox(hwnd, "Failure on WM_DDE_ADVISE!", szAppName, MB_ICONEXCLAMATION | MB_OK);
}
return 0;
case WM_DDE_ACK :
// In response to WM_DDE_INITIATE, save server window
if(fDoingInitiate)
{
UnpackDDElParam(WM_DDE_ACK, lParam, &uiLow, &uiHi); FreeDDElParam(WM_DDE_ACK, lParam);
hwndServer =(HWND) wParam; GlobalDeleteAtom((ATOM) uiLow); GlobalDeleteAtom((ATOM) uiHi);
}
return 0;
case WM_DDE_DATA :
//wParam -- sending window handle
//lParam -- DDEDATA memory handle & item atom
UnpackDDElParam(WM_DDE_DATA, lParam, &uiLow, &uiHi); FreeDDElParam(WM_DDE_DATA, lParam);
hDdeData =(GLOBALHANDLE) uiLow;
pDdeData =(DDEDATA *) GlobalLock(hDdeData);
aItem =(ATOM) uiHi;
// Initialize DdeAck structure

141
DdeAck.bAppReturnCode |
= 0; |
DdeAck.reserved |
= 0; |
DdeAck.fBusy |
= FALSE; |
DdeAck.fAck |
= FALSE; |
// Check for matching format and data item
if(pDdeData->cfFormat == CF_TEXT)
{
GlobalGetAtomName(aItem, szItem, sizeof(szItem));
for(i = 0; i < NUM_STATES; i++) if(strcmp(szItem, pop[i].szAbb) == 0)
break;
if(i < NUM_STATES)
{
strcpy(szPopulation,(char *) pDdeData->Value); pop[i].lPop = atol(szPopulation); InvalidateRect(hwnd, NULL, FALSE);
DdeAck.fAck = TRUE;
}
}
// Acknowledge if necessary
if(pDdeData->fAckReq == TRUE)
{
wStatus = *((WORD *) &DdeAck);
if(!PostMessage((HWND) wParam, WM_DDE_ACK,(WPARAM) hwnd, PackDDElParam(WM_DDE_ACK,
wStatus, aItem)))
{
GlobalDeleteAtom(aItem);
GlobalUnlock(hDdeData);
GlobalFree(hDdeData);
return 0;
}
}
else
{
GlobalDeleteAtom(aItem);
}
// Clean up
if(pDdeData->fRelease == TRUE || DdeAck.fAck == FALSE)
{
GlobalUnlock(hDdeData);
GlobalFree(hDdeData);
}
else
{
GlobalUnlock(hDdeData);
}
return 0;
case WM_PAINT :
hdc = BeginPaint(hwnd, &ps);
for(i = 0; i < NUM_STATES; i++)

142
{
if(i <(NUM_STATES + 1) / 2)
{
x = cxChar;
y = i * cyChar;
}
else
{
x = 44 * cxChar;
y =(i -(NUM_STATES + 1) / 2) * cyChar;
}
TextOut(hdc, x, y, szBuffer, wsprintf(szBuffer, "%-20s",
(PSTR) pop[i].szState));
x += 36 * cxChar;
SetTextAlign(hdc, TA_RIGHT | TA_TOP);
TextOut(hdc, x, y, szBuffer,
wsprintf(szBuffer, "%10ld", pop[i].lPop));
SetTextAlign(hdc, TA_LEFT | TA_TOP);
}
EndPaint(hwnd, &ps); return 0;
case WM_DDE_TERMINATE :
// Respond with another WM_DDE_TERMINATE iMsg
PostMessage(hwndServer, WM_DDE_TERMINATE,(WPARAM) hwnd, 0L); hwndServer = NULL;
return 0;
case WM_CLOSE : if(hwndServer == NULL)
break;
// Post WM_DDE_UNADVISE iMsg
PostMessage(hwndServer, WM_DDE_UNADVISE,(WPARAM) hwnd, MAKELONG(CF_TEXT, NULL));
dwTime = GetCurrentTime();
while(GetCurrentTime() - dwTime < DDE_TIMEOUT)
{
if(PeekMessage(&msg, hwnd, WM_DDE_ACK, WM_DDE_ACK, PM_REMOVE))
break;
}
// Post WM_DDE_TERMINATE iMsg
PostMessage(hwndServer, WM_DDE_TERMINATE,(WPARAM) hwnd, 0L);
dwTime = GetCurrentTime();
while(GetCurrentTime() - dwTime < DDE_TIMEOUT)
{
if(PeekMessage(&msg, hwnd, WM_DDE_TERMINATE, WM_DDE_TERMINATE, PM_REMOVE))
break;

143
}
break; |
// for default processing |
case WM_DESTROY : PostQuitMessage(0); return 0;
}
return DefWindowProc(hwnd, iMsg, wParam, lParam);
}
SHOWPOP.H
/*-----------------------
SHOWPOP.H header file
-----------------------*/ |
|
|
struct |
|
|
{ |
|
|
char *szAbb; |
|
|
char *szState; |
|
|
long lPop; |
|
|
} |
|
|
pop[] = { |
|
|
"AL", "Alabama", |
0, "AK", "Alaska", |
0, |
"AZ", "Arizona", |
0, "AR", "Arkansas", |
0, |
"CA", "California", |
0, "CO", "Colorado", |
0, |
"CT", "Connecticut", |
0, "DE", "Delaware", |
0, |
"DC", "Dist. of Columbia", |
0, "FL", "Florida", |
0, |
"GA", "Georgia", |
0, "HI", "Hawaii", |
0, |
"ID", "Idaho", |
0, "IL", "Illinois", |
0, |
"IN", "Indiana", |
0, "IA", "Iowa", |
0, |
"KS", "Kansas", |
0, "KY", "Kentucky", |
0, |
"LA", "Louisiana", |
0, "ME", "Maine", |
0, |
"MD", "Maryland", |
0, "MA", "Massachusetts", |
0, |
"MI", "Michigan", |
0, "MN", "Minnesota", |
0, |
"MS", "Mississippi", |
0, "MO", "Missouri", |
0, |
"MT", "Montana", |
0, "NE", "Nebraska", |
0, |
"NV", "Nevada", |
0, "NH", "New Hampshire", |
0, |
"NJ", "New Jersey", |
0, "NM", "New Mexico", |
0, |
"NY", "New York", |
0, "NC", "North Carolina", |
0, |
"ND", "North Dakota", |
0, "OH", "Ohio", |
0, |
"OK", "Oklahoma", |
0, "OR", "Oregon", |
0, |
"PA", "Pennsylvania", |
0, "RI", "Rhode Island", |
0, |
"SC", "South Carolina", |
0, "SD", "South Dakota", |
0, |
"TN", "Tennessee", |
0, "TX", "Texas", |
0, |
"UT", "Utah", |
0, "VT", "Vermont", |
0, |
"VA", "Virginia", |
0, "WA", "Washington", |
0, |
"WV", "West Virginia", |
0, "WI", "Wisconsin", |
0, |
"WY", "Wyoming", |
0, "US", "United States Total", |
0 |
}; |
|
|
#define NUM_STATES(sizeof(pop) / sizeof(pop[0]))
Рис. 17.2 Программа SHOWPOP1
Эта программа отображает в своем окне названия штатов с обновленной численностью населения из программы DDEPOP1, используя возможности сообщения WM_DDE_ADVISE. Обратите внимание, что в программе SHOWPOP1 имеется точно такая же структура pop, как и в программе DDEPOP1, но в данной версии содержатся поля для двухбуквенных аббревиатур штатов, для названия штатов, и поле lPop (инициализируемое нулем), в которое будет заноситься обновленная численность населения, получаемая из программы DDEPOP1.
В программе SHOWPOP1 поддерживается только один диалог DDE, поэтому для этого диалога ей достаточно одного окна. Для него она использует оконную процедуру WndProc.