Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Ch-14.doc
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
141.31 Кб
Скачать

Задание на конкурсе программистов

3 октября 1986 года фирма Microsoft провела однодневный брифинг для технических редакторов и авторов компьютерных журналов, чтобы обсудить имевшиеся на тот момент языковые продукты, включая свою первую интерактивную среду разработки QUICKBASIC 2.0. Тогда Windows 1.0 было меньше года, и никто не знал, когда мы могли бы получить что-либо похожее на эту среду (на это потребовалось всего несколько лет). Что сделало это событие уникальным, так это подготовленный департаментом по связям с общественностью фирмы Microsoft конкурс программистов под девизом "Storm the Gates" (штурмует ворота). Билл Гейтс использовал QUICKBASIC 2.0, а люди, представлявшие компьютерную прессу, могли использовать любой языковой продукт, какой им больше понравится.

Конкретная задача, использовавшаяся в конкурсе, была выбрана среди нескольких других (разработанных в соответствии с принципом — около получаса на написание программы), предложенных представителями прессы. Она выглядела примерно так:

Создать псевдомногозадачную среду, состоящую из четырех окон. В первом окне должна выводиться последовательность возрастающих на единицу чисел, во втором — возрастающая последовательность простых чисел, в третьем — последовательность чисел Фибоначчи. (Последовательность чисел Фибоначчи начинается с 0 и 1, а каждое следующее число является суммой двух предыдущих, т. е. 0, 1, 1, 2, 3, 5, 8 и т. д. ) Эти три окна либо прокручиваются, либо очищаются при полном заполнении окна числами. Четвертое окно должно отображать круги случайного радиуса. Программа завершается при нажатии клавиши <Escape>.

Конечно, в октябре 1986 года такая программа, выполнявшаяся в MS DOS, не могла быть более чем имитацией многозадачности, и никто из участвовавших в конкурсе не осмелился — большинство из них не имело еще достаточных знаний для этого — написать программу для Windows. Кроме того, для написания такой программы с нуля под Windows потребовалось бы значительно больше времени чем полчаса.

Большинство участников конкурса написали программу, которая делила экран на четыре области. Программа содержала цикл, в котором последовательно обновлялись данные в каждом окне, а затем проверялось, не нажата ли клавиша <Escape>. Как это обычно и происходит в DOS, программа загружала процессор на все 100%.

Если бы программа была написана для Windows 1.0, то результат мог бы быть похожим на программу MULTI1, приведенную на рис. 14.2. Мы говорим "похожим", поскольку приведенная ниже программа преобразована для работы в 32-разрядной среде. Но структура и большая часть кода, кроме определения переменных и параметров функций, могли бы быть такими же.

MULTI1.MAK

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

# MULTI1.MAK make file

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

multi1.exe : multi1.obj

$(LINKER) $(GUIFLAGS) -OUT:multi1.exe multi1.obj $(GUILIBS)

multi1.obj : multi1.c

$(CC) $(CFLAGS) multi1.c

MULTI1.C

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

MULTI1.C -- Multitasking Demo

(c) Charles Petzold, 1996

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

#include <windows.h>

#include <stdlib.h>

#include <string.h>

#include <math.h>

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

int cyChar ;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,

PSTR szCmdLine, int iCmdShow)

{

static char szAppName[] = "Multi1" ;

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 = NULL ;

wndclass.lpszClassName = szAppName ;

wndclass.hIconSm = LoadIcon (NULL, IDI_APPLICATION) ;

RegisterClassEx (&wndclass) ;

hwnd = CreateWindow (szAppName, "Multitasking Demo",

WS_OVERLAPPEDWINDOW,

CW_USEDEFAULT, CW_USEDEFAULT,

CW_USEDEFAULT, CW_USEDEFAULT,

NULL, NULL, hInstance, NULL) ;

ShowWindow (hwnd, iCmdShow) ;

UpdateWindow (hwnd) ;

while (GetMessage (&msg, NULL, 0, 0))

{

TranslateMessage (&msg) ;

DispatchMessage (&msg) ;

}

return msg.wParam ;

}

int CheckBottom (HWND hwnd, int cyClient, int iLine)

{

if (iLine * cyChar + cyChar > cyClient)

{

InvalidateRect (hwnd, NULL, TRUE) ;

UpdateWindow (hwnd) ;

iLine = 0 ;

}

return iLine ;

}

// Window 1: Display increasing sequence of numbers

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

LRESULT APIENTRY WndProc1 (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)

{

static int iNum, iLine ;

static short cyClient ;

char szBuffer[16] ;

HDC hdc ;

switch (iMsg)

{

case WM_SIZE :

cyClient = HIWORD (lParam) ;

return 0 ;

case WM_TIMER :

if (iNum < 0)

iNum = 0 ;

iLine = CheckBottom (hwnd, cyClient, iLine) ;

wsprintf (szBuffer, "%d", iNum++) ;

hdc = GetDC (hwnd) ;

TextOut (hdc, 0, iLine * cyChar, szBuffer, strlen (szBuffer)) ;

ReleaseDC (hwnd, hdc) ;

iLine++ ;

return 0 ;

}

return DefWindowProc (hwnd, iMsg, wParam, lParam) ;

}

// Window 2: Display increasing sequence of prime numbers

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

LRESULT APIENTRY WndProc2 (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)

{

static int iNum = 1, iLine ;

static short cyClient ;

char szBuffer[16] ;

int i, iSqrt ;

HDC hdc ;

switch (iMsg)

{

case WM_SIZE :

cyClient = HIWORD (lParam) ;

return 0 ;

case WM_TIMER :

do {

if (++iNum < 0)

iNum = 0 ;

iSqrt = (int) sqrt (iNum) ;

for (i = 2 ; i <= iSqrt ; i++)

if (iNum % i == 0)

break ;

}

while (i <= iSqrt) ;

iLine = CheckBottom (hwnd, cyClient, iLine) ;

wsprintf (szBuffer, "%d", iNum) ;

hdc = GetDC (hwnd) ;

TextOut (hdc, 0, iLine * cyChar, szBuffer, strlen (szBuffer)) ;

ReleaseDC (hwnd, hdc) ;

iLine++ ;

return 0 ;

}

return DefWindowProc (hwnd, iMsg, wParam, lParam) ;

}

// Window 3: Display increasing sequence of Fibonacci numbers

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

LRESULT APIENTRY WndProc3 (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)

{

static int iNum = 0, iNext = 1, iLine ;

static short cyClient ;

char szBuffer[16] ;

int iTemp ;

HDC hdc ;

switch (iMsg)

{

case WM_SIZE :

cyClient = HIWORD (lParam) ;

return 0 ;

case WM_TIMER :

if (iNum < 0)

{

iNum = 0 ;

iNext = 1 ;

}

iLine = CheckBottom (hwnd, cyClient, iLine) ;

wsprintf (szBuffer, "%d", iNum) ;

hdc = GetDC (hwnd) ;

TextOut (hdc, 0, iLine * cyChar, szBuffer, strlen (szBuffer)) ;

ReleaseDC (hwnd, hdc) ;

iTemp = iNum ;

iNum = iNext ;

iNext += iTemp ;

iLine++ ;

return 0 ;

}

return DefWindowProc (hwnd, iMsg, wParam, lParam) ;

}

// Window 4: Display circles of random radii

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

LRESULT APIENTRY WndProc4 (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)

{

static short cxClient, cyClient ;

HDC hdc ;

int iDiameter ;

switch (iMsg)

{

case WM_SIZE :

cxClient = LOWORD (lParam) ;

cyClient = HIWORD (lParam) ;

return 0 ;

case WM_TIMER :

InvalidateRect (hwnd, NULL, TRUE) ;

UpdateWindow (hwnd) ;

iDiameter = rand() % (max (1, min (cxClient, cyClient))) ;

hdc = GetDC (hwnd) ;

Ellipse (hdc, (cxClient - iDiameter) / 2,

(cyClient - iDiameter) / 2,

(cxClient + iDiameter) / 2,

(cyClient + iDiameter) / 2) ;

ReleaseDC (hwnd, hdc) ;

return 0 ;

}

return DefWindowProc (hwnd, iMsg, wParam, lParam) ;

}

// Main window to create child windows

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

LRESULT APIENTRY WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)

{

static char *szChildClass[] = { "Child1", "Child2",

"Child3", "Child4" } ;

static HWND hwndChild[4] ;

static WNDPROC ChildProc[] = { WndProc1, WndProc2,

WndProc3, WndProc4 } ;

HINSTANCE hInstance ;

int i, cxClient, cyClient ;

WNDCLASSEX wndclass ;

switch (iMsg)

{

case WM_CREATE :

hInstance = (HINSTANCE) GetWindowLong (hwnd, GWL_HINSTANCE) ;

wndclass.cbSize = sizeof (wndclass) ;

wndclass.style = CS_HREDRAW | CS_VREDRAW ;

wndclass.cbClsExtra = 0 ;

wndclass.cbWndExtra = 0 ;

wndclass.hInstance = hInstance ;

wndclass.hIcon = NULL ;

wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;

wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;

wndclass.lpszMenuName = NULL ;

wndclass.hIconSm = NULL ;

for (i = 0 ; i < 4 ; i++)

{

wndclass.lpfnWndProc = ChildProc[i] ;

wndclass.lpszClassName = szChildClass[i] ;

RegisterClassEx (&wndclass) ;

hwndChild[i] = CreateWindow (szChildClass[i], NULL,

WS_CHILDWINDOW | WS_BORDER | WS_VISIBLE,

0, 0, 0, 0, hwnd, (HMENU) i, hInstance, NULL) ;

}

cyChar = HIWORD (GetDialogBaseUnits ()) ;

SetTimer (hwnd, 1, 10, NULL) ;

return 0 ;

case WM_SIZE :

cxClient = LOWORD (lParam) ;

cyClient = HIWORD (lParam) ;

for (i = 0 ; i < 4 ; i++)

MoveWindow (hwndChild[i], (i % 2) * cxClient / 2,

(i > 1) * cyClient / 2,

cxClient / 2, cyClient / 2, TRUE) ;

return 0 ;

case WM_TIMER :

for (i = 0 ; i < 4 ; i++)

SendMessage (hwndChild[i], WM_TIMER, wParam, lParam) ;

return 0 ;

case WM_CHAR :

if (wParam == '\x1B')

DestroyWindow (hwnd) ;

return 0 ;

case WM_DESTROY :

KillTimer (hwnd, 1) ;

PostQuitMessage (0) ;

return 0 ;

}

return DefWindowProc (hwnd, iMsg, wParam, lParam) ;

}

Рис. 14.2. Программа MULTI1

Данная программа не использует ничего такого, чего мы не видели раньше. Главное окно создает четыре дочерних окна, каждое из которых занимает четверть рабочей области родительского окна. Главное окно также устанавливает таймер Windows и посылает сообщения WM_TIMER каждому из четырех дочерних окон.

Обычно, Windows-программа могла бы сохранять достаточно информации для того, чтобы обновлять содержимое окон в процессе обработки сообщения WM_PAINT. Программа MULTI1 не делает этого, а окна рисуются и обновляются настолько быстро, что, видимо, в этом и нет необходимости.

Генератор простых чисел, созданный в функции WndProc2, не очень эффективен, но он работает. Число является простым, если у него нет других делителей кроме 1 и самого себя. Чтобы проверить, является ли конкретное число простым, не требуется делить его на все числа и проверять остатки от деления. Достаточно извлечь квадратный корень из этого числа. Вычисление квадратного корня является причиной странного, казалось бы, введения арифметики с плавающей точкой в программу, основанную на операциях с целыми.

В программе MULTI1 нет ничего неправильного. Использование таймера Windows — это отличный путь имитации многозадачности в ранних версиях Windows и Windows 95. Однако, использование таймера иногда замедляет программу. Если программа может обновить все свои окна во время обработки одного сообщения WM_TIMER, имея запас времени, то при этом не используются все возможности компьютера.

Одно из возможных решений этой проблемы — выполнение двух и более обновлений во время обработки одного сообщения WM_TIMER. Но скольких? Это зависит в основном от скорости машины, которая может меняться в широких пределах. Единственное, что не хотелось бы делать, так это писать программу, настроенную только на конкретную машину — 25МГц 386 или 50МГц 486 или какую-нибудь 700МГц 786.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]