Программирование для Windows NT© Александр Фролов, Григорий ФроловТом 27, часть 2, М.: Диалог-МИФИ, 1996, 272 стр. Передача сообщений между приложениямиМетод передачи данных между процессами, основанный на использовании файлов, отображенных на виртуальную память, работает достаточно быстро, так как процессы имеют прямой доступ к общим страницам памяти. Тем не менее, при использовании этого метода необходимо заботиться о синхронизации процессов, для чего следует использовать объекты синхронизации. Если скорость передачи данных не является критичной, можно воспользоваться удобным способом передачи данных, не требующим синхронизации. Этот метод основан на передаче сообщения WM_COPYDATA из одного приложения в другое при помощи функции SendMessage (функцию PostMessage для передачи этого сообщения использовать нельзя). Сообщение WM_COPYDATA использует оба параметра - wParam и lParam. Через параметр wParam необходимо передать идентификатор окна, посылающего сообщение. Параметр lParam используется для передачи указателя на предварительно заполненную структуру COPYDATASTRUCT, в которой находится ссылка на передаваемые данные. Если приложение обрабатывает сообщение WM_COPYDATA, то соответствующий обработчик должен вернуть значение TRUE, а если нет - FALSE. Ниже мы привели формат структуры COPYDATASTRUCT: typedef struct tagCOPYDATASTRUCT { DWORD dwData; // 32-разрядные данные DWORD cbData; // размер передаваемого буфера с данными PVOID lpData; // указатель на буфер с данными } COPYDATASTRUCT; Перед посылкой сообщения WM_COPYDATA приложение должно заполнить структуру COPYDATASTRUCT. В поле dwData можно записать произвольное 32-разрядное значение, которое будет передано вместе с сообщением. В поле lpData вы дополнительно можете записать указатель на область данных, полученную, например, при помощи функции HeapAlloc. Размер этой области следует записать в поле cbData. Когда функция окна принимающего приложения получит сообщение WM_COPYDATA, она должна скопировать в свой локальный буфер данные, которые были переданы вместе с сообщением. И это единственное, что можно с этими данными сделать. Их, например, нельзя изменять. Не следует также надеятся, что данные сохранятся до обработки другого сообщения, поэтому копирование следует выполнить во время обработки сообщения WM_COPYDATA. Если вам достаточно передать из одного приложения в другое 32-разрядное значение, в поле lpData можно записать константу NULL. Приложение RCLOCKДля иллюстрации методов работы с сообщением WM_COPYDATA мы подготовили два приложения, которые называются RCLOCK и STIME. Приложение RCLOCK раз в секунду получает от приложения STIME сообщение WM_COPYDATA, вместе с которым передается строка текущего времени. Полученая строка отображается в небольшом окне, расположенном в левом нижнем углу рабочего стола (рис. 2.2). Рис. 2.2. Окно приложения RCLOCK Заметим, что сразу после запуска (если приложение STIME еще не активно) в окне приложения RCLOCK отображается строка <Unknown>, как это показано на рис. 2.3. Рис. 2.3. Исхндное состояние окна приложения RCLOCK Если при активном приложении RCLOCK завершить приложение STIME, последнее передаст строку <Terminated>, которая будет показана в окне приложения RCLOCK. Для того чтобы завершить работу приложения RCLOCK вы должны сделать его окно текущим, щелкнув по нему левой клавишей мыши, а затем нажать комбинацию клавиш <Ctrl+F4>. При необходимости вы сможете реализовать более удобный способ самостоятельно. В 11 томе “Библиотеки системного программиста” мы привели исходные тексты приложения TMCLOCK, из которого вы можете взять некоторые идеи для улучшения пользовательского интерфейса. Например, вы можете организовать перемещение окна приложения мышью, обрабатывая сообщение WM_NCHITTEST. Исходные тексты приложения RCLOCKГлавный файл исходных текстов приложения RCLOCK представлен в листинге 2.3. Листинг 2.3. Файл rclock/rclock.c // ================================================== // Приложение RCLOCK (серверное) // Демонстрация использования сообщения WM_COPYDATA // для передачи данных между процессами // // (С) Фролов А.В., 1996 // Email: frolov@glas.apc.org // ================================================== #define STRICT #include <windows.h> #include <windowsx.h> #include <commctrl.h> #include "resource.h" #include "afxres.h" #include "rclock.h" HINSTANCE hInst; char szAppName[] = "RclockApp"; char szAppTitle[] = "Remote Clock"; // Метрики шрифта с фиксированной шириной символов LONG cxChar, cyChar; RECT rc; char szBuf[80]; // ----------------------------------------------------- // Функция WinMain // ----------------------------------------------------- int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX wc; HWND hWnd; MSG msg; // Сохраняем идентификатор приложения hInst = hInstance; // Преверяем, не было ли это приложение запущено ранее hWnd = FindWindow(szAppName, NULL); if(hWnd) { // Если было, выдвигаем окно приложения на // передний план if(IsIconic(hWnd)) ShowWindow(hWnd, SW_RESTORE); SetForegroundWindow(hWnd); return FALSE; } // Регистрируем класс окна memset(&wc, 0, sizeof(wc)); wc.cbSize = sizeof(WNDCLASSEX); wc.hIconSm = LoadImage(hInst, MAKEINTRESOURCE(IDI_APPICONSM), IMAGE_ICON, 16, 16, 0); wc.style = 0; wc.lpfnWndProc = (WNDPROC)WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst; wc.hIcon = LoadImage(hInst, MAKEINTRESOURCE(IDI_APPICON), IMAGE_ICON, 32, 32, 0); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); wc.lpszMenuName = MAKEINTRESOURCE(IDR_APPMENU); wc.lpszClassName = szAppName; if(!RegisterClassEx(&wc)) if(!RegisterClass((LPWNDCLASS)&wc.style)) return FALSE; // Создаем главное окно приложения hWnd = CreateWindow(szAppName, szAppTitle, WS_POPUPWINDOW | WS_THICKFRAME, 100, 100, 100, 100, NULL, NULL, hInst, NULL); if(!hWnd) return(FALSE); // Размещаем окно в нижней левой части рабочего стола GetWindowRect(GetDesktopWindow(), &rc); MoveWindow(hWnd, rc.right - cxChar * 25, rc.bottom - cyChar * 3, cxChar * 10, cyChar * 2, TRUE); // Отображаем окно и запускаем цикл // обработки сообщений ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); while(GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } // ----------------------------------------------------- // Функция WndProc // ----------------------------------------------------- LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { // Это сообщение посылается приложением STIME case WM_COPYDATA: { // Копируем данные, полученные от приложения STIME, // во внутренний буфер strcpy(szBuf, ((PCOPYDATASTRUCT)lParam)->lpData); // Перерисовываем содержимое окна, отображая в нем // полученную строку символов InvalidateRect(hWnd, NULL, TRUE); break; } HANDLE_MSG(hWnd, WM_CREATE, WndProc_OnCreate); HANDLE_MSG(hWnd, WM_DESTROY, WndProc_OnDestroy); HANDLE_MSG(hWnd, WM_PAINT, WndProc_OnPaint); default: return(DefWindowProc(hWnd, msg, wParam, lParam)); } } // ----------------------------------------------------- // Функция WndProc_OnCreate // ----------------------------------------------------- BOOL WndProc_OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct) { HDC hdc; TEXTMETRIC tm; hdc = GetDC(hWnd); // Выбираем в контекст отображения шрифт с фиксированной // шириной букв SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT)); // Определяем и сохраняем метрики шрифта GetTextMetrics(hdc, &tm); cxChar = tm.tmMaxCharWidth; cyChar = tm.tmHeight + tm.tmExternalLeading; ReleaseDC(hWnd, hdc); // Выполняем инициализацию буфера szBuf, содержимое // которого отображается в окне приложения strcpy(szBuf, (LPSTR)"<Unknown>"); return TRUE; } // ----------------------------------------------------- // Функция WndProc_OnDestroy // ----------------------------------------------------- #pragma warning(disable: 4098) void WndProc_OnDestroy(HWND hWnd) { PostQuitMessage(0); return 0L; } // ----------------------------------------------------- // Функция WndProc_OnPaint // ----------------------------------------------------- #pragma warning(disable: 4098) void WndProc_OnPaint(HWND hWnd) { HDC hdc; PAINTSTRUCT ps; RECT rc; // Перерисовываем внутреннюю область окна hdc = BeginPaint(hWnd, &ps); GetClientRect(hWnd, &rc); // Рисуем в окне строку символов, полученную от // приложения STIME DrawText(hdc, szBuf, -1, &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER); EndPaint(hWnd, &ps); return 0; } Определения и глобальные переменныеВ глобальном массиве szAppName хранится текстовая строка с названием приложения. Это название будет использовано приложением STIME для поиска главного окна приложения RCLOCK. В глобальных переменных cxChar и cyChar хранятся метрики шрифта с фиксированной шириной символов, которые будут определены на этапе создания главного окна приложения при обработке сообщения WM_CREATE. Структура rc типа RECT предназначена для хранения размеров окна рабочего стола. Буфер szBuf используется для хранения данных, передаваемых из приложения STIME при помощи сообщения WM_COPYDATA. Функция WinMainФункция WinMain приложения RCLOCK сразу после запуска приложения выполняет поиск своей копии, используя для этого функцию FindWindow. Если такая копия найдена, главное окно этой копии выдвигается на передний план функцией SetForegroundWindow, после чего работа функции WinMain завершается. Такая техника уже использовалась нами ранее. В том случае, когда запускается первая копия приложения RCLOCK, функция WinMain выполняет обычные действия. Она регистрирует класс окна и создает главное окно приложения. Для того чтобы это окно имело вид, показанный на рис. 2.2, для него указываются стили WS_POPUPWINDOW и WS_THICKFRAME: hWnd = CreateWindow(szAppName, szAppTitle, WS_POPUPWINDOW | WS_THICKFRAME, 100, 100, 100, 100, NULL, NULL, hInst, NULL); Для определения размеров и расположения главного окна приложения RCLOCK функция WinMain определяет размеры окна рабочего стола, сохраняя их в глобальной переменной rc: GetWindowRect(GetDesktopWindow(), &rc); Размещение главного окна приложения RCLOCK выполняется функцией MoveWindow, как это показано ниже: MoveWindow(hWnd, rc.right - cxChar * 25, rc.bottom - cyChar * 3, cxChar * 10, cyChar * 2, TRUE); Заметим, что метрики шрифта cxChar и cyChar определяются при обработке сообщения WM_CREATE, который получает управление при вызове функции CreateWindow. Поэтому после возвращения из функции CreateWindow содержимое глобальных переменных cxChar и cyChar будет отражать размеры рабочего стола. После изменения размеров и расположения главного окна приложения RCLOCK выполняется отображение этого окна и запуск обычного цикла обработки сообщений. Функция WndProcВ задачу функции WndProc входит обработка сообщений WM_CREATE, WM_DESTROY, WM_PAINT и WM_COPYDATA. Для обработки первых трех сообщений при помощи макрокоманды HANDLE_MSG вызываются функции WndProc_OnCreate, WndProc_OnDestroy и WndProc_OnPaint, соответственно. Для сообщения WM_COPYDATA в файле windowsx.h, к сожалению, не предусмотрены специальные макрокоманды. Мы могли бы подготовить такую макрокоманду самостоятельно, однако, так как обработка сообщения WM_COPYDATA очень проста, мы использовали классический способ: case WM_COPYDATA: { strcpy(szBuf, ((PCOPYDATASTRUCT)lParam)->lpData); InvalidateRect(hWnd, NULL, TRUE); break; } Напомним, что вместе с параметром lParam сообщения WM_COPYDATA передается указатель на структуру COPYDATASTRUCT. Приложение, посылающее другому приложению сообщение WM_COPYDATA, подготавливает область данных, записывая ее адрес в поле lpData структуры типа COPYDATASTRUCT. Принимающее приложение должно скопировать эти данные в свой внутренний буфер. В нашем случае в качестве данных передается строка символов, закрытая двоичным нулем, поэтому для копирования мы используем функцию strcpy. После выполнения копирования обработчик сообщения WM_COPYDATA вызывает функцию InvalidateRect, что в результате приводит к перерисовке главного окна приложения. В этом окне обработчик сообщения WM_PAINT нарисует текстовую строку, полученную от другого приложения и скопированную только что в буфер szBuf. Функция WndProc_OnCreateФункция WndProc_OnCreate вызывается при создании главного окна приложения. Эта функция получает контекст отображения, выбирает в него шрифт с фиксированной шириной букв и определяет его метрики. Ширина и высота символов сохраняются, соответственно, в глобальных переменных cxChar и cyChar. Эти значения используются для вычисления размеров главного окна приложения. В заверешении функция WndProc_OnCreate записывает в глобальный буфер szBuf строку <Unknown>: strcpy(szBuf, (LPSTR)"<Unknown>"); Эта строка будет отображаться в главном окне приложения до тех пор, пока вы не запустите приложение STIME. Функция WndProc_OnDestroyЭта функция завершает цикл обработки сообщений, вызывая для этого функцию PostQuitMessage. Функция WndProc_OnPaintПри обработке сообщения WM_PAINT функция WndProc_OnPaint отображает в главном окне приложения RCLOCK текстовую строку, записанную в буфере szBuf. Для рисования строки используется функция DrawText, так как с ее помощью легко выполнить центровку строки в окне по вертикали и горизонтали. Файл rclock.hВ файле rclock.h (листинг 2.4) находятся прототипы функций, определенных в приложении RCLOCK. Листинг 2.4. Файл rclock/rclock.h // ----------------------------------------------------- // Описание функций // ----------------------------------------------------- LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); void WndProc_OnDestroy(HWND hWnd); BOOL WndProc_OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct); void WndProc_OnPaint(HWND hWnd); Файл resource.hФайл resource.h (листинг 2.5) создается автоматически и содержит определения для файла описания ресурсов приложения RCLOCK. Листинг 2.5. Файл rclock/resource.h //{{NO_DEPENDENCIES}} // Microsoft Developer Studio generated include file. // Used by RCLOCK.RC // #define IDR_APPMENU 102 #define IDI_APPICON 103 #define IDI_APPICONSM 104 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 106 #define _APS_NEXT_COMMAND_VALUE 40006 #define _APS_NEXT_CONTROL_VALUE 1010 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif Файл rclock.rcВ файле rclock.rc (листинг 2.6) определены ресурсы приложения RCLOCK. Листинг 2.6. Файл rclock/rclock.rc //Microsoft Developer Studio generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "afxres.h" ////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ////////////////////////////////////////////////////////////// // English (U.S.) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) #ifdef _WIN32 LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #pragma code_page(1252) #endif //_WIN32 #ifdef APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE DISCARDABLE BEGIN "resource.h\0" END 2 TEXTINCLUDE DISCARDABLE BEGIN "#include ""afxres.h""\r\n" "\0" END 3 TEXTINCLUDE DISCARDABLE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // // Icon // // Icon with lowest ID value placed first to ensure // application icon // remains consistent on all systems. IDI_APPICON ICON DISCARDABLE "RCLOCK.ICO" IDI_APPICONSM ICON DISCARDABLE "RCLOCKSM.ICO" ////////////////////////////////////////////////////////////// // // DESIGNINFO // #ifdef APSTUDIO_INVOKED GUIDELINES DESIGNINFO DISCARDABLE BEGIN IDD_DIALOG1, DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 162 TOPMARGIN, 7 BOTTOMMARGIN, 52 END END #endif // APSTUDIO_INVOKED #endif // English (U.S.) resources ////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED Приложение STIMEПриложение STIME работает в паре с приложением RCLOCK, периодически раз в секунду посылая последнему текстовые строки с помощью сообщения WM_COPYDATA. Главное окно приложения STIME не имеет никаких особенностей, поэтому мы его не приводим. Исходные тексты приложения STIMEГлавный файл исходных текстов приложения STIME представлен в листинге 2.7. Листинг 2.7. Файл rclock/stime/stime.c // ================================================== // Приложение STIME (работает вместе с приложением RTIME) // Демонстрация использования сообщения WM_COPYDATA // для передачи данных между процессами // // (С) Фролов А.В., 1996 // Email: frolov@glas.apc.org // ================================================== #define STRICT #include <windows.h> #include <windowsx.h> #include <commctrl.h> #include <time.h> #include "resource.h" #include "afxres.h" #include "stime.h" HINSTANCE hInst; char szAppName[] = "StimeApp"; char szAppTitle[] = "Time Sender"; // Имя приложения RTIME char szServerAppName[] = "RclockApp"; // Идентификатор главного окна приложения RTIME HWND hWndServer; // Структура для передачи данных между процессами // при помощи сообщения WM_COPYDATA COPYDATASTRUCT cd; // Буферы для передаваемых данных char szBuf[80]; char szTerminated[] = "<Terminated<"; // ----------------------------------------------------- // Функция WinMain // ----------------------------------------------------- int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX wc; HWND hWnd; MSG msg; // Сохраняем идентификатор приложения hInst = hInstance; // Преверяем, не было ли это приложение запущено ранее hWnd = FindWindow(szAppName, NULL); if(hWnd) { // Если было, выдвигаем окно приложения на // передний план if(IsIconic(hWnd)) ShowWindow(hWnd, SW_RESTORE); SetForegroundWindow(hWnd); return FALSE; } // Ищем окно серверного приложения RCLOCK и сохраняем // его идентификатор hWndServer = FindWindow(szServerAppName, NULL); if(hWndServer == NULL) { // Если окно серверного приложения не найдено, // выводим сообщение об ошибке и завершаем работу // приложения MessageBox(NULL, "Server RCLOCK not found", szAppName, MB_ICONEXCLAMATION | MB_OK); return FALSE; } // Регистрируем класс окна memset(&wc, 0, sizeof(wc)); wc.cbSize = sizeof(WNDCLASSEX); wc.hIconSm = LoadImage(hInst, MAKEINTRESOURCE(IDI_APPICONSM), IMAGE_ICON, 16, 16, 0); wc.style = 0; wc.lpfnWndProc = (WNDPROC)WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst; wc.hIcon = LoadImage(hInst, MAKEINTRESOURCE(IDI_APPICON), IMAGE_ICON, 32, 32, 0); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); wc.lpszMenuName = MAKEINTRESOURCE(IDR_APPMENU); wc.lpszClassName = szAppName; if(!RegisterClassEx(&wc)) if(!RegisterClass((LPWNDCLASS)&wc.style)) return FALSE; // Создаем главное окно приложения hWnd = CreateWindow(szAppName, szAppTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInst, NULL); if(!hWnd) return(FALSE); // Отображаем окно и запускаем цикл // обработки сообщений ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); while(GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } // ----------------------------------------------------- // Функция WndProc // ----------------------------------------------------- LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { HANDLE_MSG(hWnd, WM_TIMER, WndProc_OnTimer); HANDLE_MSG(hWnd, WM_CREATE, WndProc_OnCreate); HANDLE_MSG(hWnd, WM_DESTROY, WndProc_OnDestroy); default: return(DefWindowProc(hWnd, msg, wParam, lParam)); } } // ----------------------------------------------------- // Функция WndProc_OnCreate // ----------------------------------------------------- BOOL WndProc_OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct) { // Создаем таймер с периодом работы 1 сек SetTimer(hWnd, CLOCK_TIMER, 1000, NULL); return TRUE; } // ----------------------------------------------------- // Функция WndProc_OnTimer // ----------------------------------------------------- #pragma warning(disable: 4098) void WndProc_OnTimer(HWND hWnd, UINT id) { time_t t; struct tm *ltime; // Определяем текущее время time(&t); ltime = localtime(&t); // Формируем текстовую строку времени wsprintf(szBuf, "%02d:%02d:%02d", ltime->tm_hour, ltime->tm_min,ltime->tm_sec); // Записываем адрес и размер строки в структуру // типа COPYDATASTRUCT cd.lpData = szBuf; cd.cbData = strlen(szBuf) + 1; // Посылаем сообщение серверному приложению RCLOCK SendMessage(hWndServer, WM_COPYDATA, (WPARAM)hWnd, (LPARAM)&cd); return 0; } // ----------------------------------------------------- // Функция WndProc_OnDestroy // ----------------------------------------------------- #pragma warning(disable: 4098) void WndProc_OnDestroy(HWND hWnd) { // Перед завершением работы приложения передаем // серверу строку <Terminated> cd.lpData = szTerminated; cd.cbData = strlen(szTerminated) + 1; SendMessage(hWndServer, WM_COPYDATA, (WPARAM)hWnd, (LPARAM)&cd); // Удаляем таймер KillTimer(hWnd, CLOCK_TIMER); PostQuitMessage(0); return 0L; } Определения и глобальные переменныеПомимо всего прочего в области глобальных переменных определен массив szServerAppName, в котором хранится имя приложения RCLOCK. Это имя будет использовано в функции WinMain для поиска главного окна серверного приложения. Идентификатор главного окна найденного приложения RCLOCK хранится в глобальной переменной hWndServer. Этот идентификатор используется для посылки сообщения WM_COPYDATA функцией SendMessage. В области глобальных переменных определена структура cd типа COPYDATASTRUCT, которая совместно с глобальным буфером szBuf используется для передачи текстовой строки в приложение RCLOCK. Кроме того, определен буфер szTerminated, в котором находится строка символов Terminated. Эта строка передается в приложение RCLOCK перед завершением работы приложения STIME. Функция WinMainПосле поиска своей собственной копии приложение STIME ищет окно серверного приложения RCLOCK: hWndServer = FindWindow(szServerAppName, NULL); Если это приложение не найдено, выдается сообщение об ошибке, вслед за чем работа приложения STIME завершается. В случае успешного поиска идентификатор найденного окна приложения RCLOCK записывается в глобальную переменную hWndServer. Вслед за этим выполняется процедура регистрации класса главного окна приложения STIME, создается главное окно приложения и запускается обычный цикл обработки сообщений. Функция WndProcФункция WndProc обрабатывает сообщения WM_CREATE, WM_DESTROY и WM_TIMER, вызывая для этого функции WndProc_OnCreate, WndProc_OnDestroy и WndProc_OnTimer, соответственно. Функция WndProc_OnCreateПри создании главного окна приложения STIME обработчик сообщения WM_CREATE создает таймер с периодом работы 1 сек, вызывая для этого функцию SetTimer: SetTimer(hWnd, CLOCK_TIMER, 1000, NULL); Созданный таймер будет иметь идентификатор CLOCK_TIMER. Функция WndProc_OnTimerФункция WndProc_OnTimer получает управление примерно один раз в секунду, обрабатывая сообщения WM_TIMER, поступающие от таймера с идентификатором CLOCK_TIMER. Прежде всего эта функция формирует строку символов с текущим временем в формате “ЧЧ:ММ:СС”, вызывая библиотечные фукнции системы разработки time и localtime: time(&t); ltime = localtime(&t); wsprintf(szBuf, "%02d:%02d:%02d", ltime->tm_hour, ltime->tm_min,ltime->tm_sec); Затем выполняется инициализация полей структуры cd типа COPYDATASTRUCT. В процессе инициализации мы записываем адрес буфера, содержащего строку символов, в поле lpData, а размер этого буфера (с учетом двоичного нуля, закрывающего строку) - в поле cbData: cd.lpData = szBuf; cd.cbData = strlen(szBuf) + 1; Поле dwData не используется. Заметим, что хотя серверное приложение RCLOCK при копировании полученных данных не использует поле cbData (так как мы передаем строку символов, закрытую двоичным нулем), при подготовке сообщения WM_COPYDATA к отправке необходимо заполнить оба поля: и lpData, и cbData. Посылка сообщения WM_COPYDATA выполняется очень просто: SendMessage(hWndServer, WM_COPYDATA, (WPARAM)hWnd, (LPARAM)&cd); Сообщение посылается в окно с идентификатором hWndServer, который был определен в функции WinMain. В качестве параметра wParam вместе с этим сообщением необходимо передать идентификатор окна посылающего приложения, то есть идентификатор окна приложения STIME. Через параметр lParam передается адрес заполненной структуры cd типа COPYDATASTRUCT. Функция WndProc_OnDestroyПеред завершением своей работы приложение STIME посылает приложению RCLOCK строку <Terminated>, которая будет отображена в окне приложения RCLOCK. Для посылки используется только что описанная нами методика: cd.lpData = szTerminated; cd.cbData = strlen(szTerminated) + 1; SendMessage(hWndServer, WM_COPYDATA, (WPARAM)hWnd, (LPARAM)&cd); Далее функция WndProc_OnDestroy удаляет таймер и завершает цикл обработки сообщений, вызывая для этого функцию PostQuitMessage. Файл stime.hВ файле stime.h (листинг 2.8) определен идентификатор таймера CLOCK_TIMER, а также прототипы функций. Листинг 2.8. Файл rclock/stime/stime.h #define CLOCK_TIMER 100 LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); void WndProc_OnDestroy(HWND hWnd); BOOL WndProc_OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct); void WndProc_OnPaint(HWND hWnd); void WndProc_OnTimer(HWND hWnd, UINT id); Файл resource.hФайл resource.h (листинг 2.9) создается автоматически и содержит определения для файла описания ресурсов приложения STIME. Листинг 2.9. Файл rclock/stime/resource.h //{{NO_DEPENDENCIES}} // Microsoft Developer Studio generated include file. // Used by STIME.RC // #define IDR_APPMENU 102 #define IDI_APPICON 103 #define IDI_APPICONSM 104 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 106 #define _APS_NEXT_COMMAND_VALUE 40006 #define _APS_NEXT_CONTROL_VALUE 1010 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif Файл stime.rcВ файле stime.rc (листинг 2.10) определены ресурсы приложения STIME. Листинг 2.10. Файл rclock/stime/stime.rc //Microsoft Developer Studio generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "afxres.h" ////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ////////////////////////////////////////////////////////////// // English (U.S.) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) #ifdef _WIN32 LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #pragma code_page(1252) #endif //_WIN32 #ifdef APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE DISCARDABLE BEGIN "resource.h\0" END 2 TEXTINCLUDE DISCARDABLE BEGIN "#include ""afxres.h""\r\n" "\0" END 3 TEXTINCLUDE DISCARDABLE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // // Icon // // Icon with lowest ID value placed first to ensure // application icon // remains consistent on all systems. IDI_APPICON ICON DISCARDABLE "STIME.ICO" IDI_APPICONSM ICON DISCARDABLE "STIMESM.ICO" ////////////////////////////////////////////////////////////// // // DESIGNINFO // #ifdef APSTUDIO_INVOKED GUIDELINES DESIGNINFO DISCARDABLE BEGIN IDD_DIALOG1, DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 162 TOPMARGIN, 7 BOTTOMMARGIN, 52 END END #endif // APSTUDIO_INVOKED #endif // English (U.S.) resources ////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED |