Сейчас в интернетах можно встретить много Hello World'ов на WinApi, но когда новичок спрашивает как в этот Hello World добавить пару нужных функций — закидывают тухлыми яблоками и тут же отправляют в пресловутый и могучий MSDN.
В этой статье от новичка новичкам я расскажу, как построить два простых приложения которые будут взаимодействовать друг с другом — одно будет принимать на вход параметры геометрической прогрессии и передавать её второму, второе же приложение будет рассчитывать n-ый член прогрессии, записывать его в файл и передавать ответ первому приложению.
Итак, поехали (©Гагарин).
С тем что нам нужно мы уже определились во введении, разрабатывать мы будем в Visual Studio 2008, так как она единственная лежала на сервере университета.
Создаем проект пустой проект Application A, а в нем создадим обертку для упрощенния создания окна (эта обертка понадобится и в другом проекте, а так же может понадобится Вам в будущем).
— kWnd.h —
#include <windows.h>
class kWnd
{
public:
kWnd(LPCTSTR windowName, HINSTANCE hInst, int cmdShow,
LRESULT(WINAPI *pWndProc)(HWND, UINT, WPARAM, LPARAM),
LPCTSTR menuName = NULL,
int x = CW_USEDEFAULT, int y = 0,
int width = CW_USEDEFAULT, int height = 0,
UINT classStyle = CS_HREDRAW | CS_VREDRAW,
DWORD windowStyle = WS_OVERLAPPEDWINDOW,
HWND hParent = NULL);
HWND GetHWnd() {return hWnd;}
protected:
HWND hWnd;
WNDCLASSEX wc;
};
— kWnd.cpp —
KWnd.cpp
#include "KWnd.h"
kWnd::kWnd(LPCTSTR windowName, HINSTANCE hInst, int cmdShow,
LRESULT (WINAPI *pWndProc)(HWND,UINT,WPARAM,LPARAM),
LPCTSTR menuName, int x, int y, int width, int height,
UINT classStyle, DWORD windowStyle, HWND hParent)
{
char szClassName[] = "KWndClass";
wc.cbSize = sizeof(wc);
wc.style = classStyle;
wc.lpfnWndProc = pWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInst;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = menuName;
wc.lpszClassName = szClassName;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
// Регистрируем класс окна
if (!RegisterClassEx(&wc)) {
char msg[100] = "Cannot register class: ";
strcat(msg, szClassName);
MessageBox(NULL, msg, "Error", MB_OK);
return;
}
// Создаем окно
hWnd = CreateWindow(szClassName, windowName, windowStyle,
x, y, width, height, hParent, (HMENU)NULL, hInst, NULL);
if (!hWnd) {
char text[100] = "Cannot create window: ";
strcat(text, windowName);
MessageBox(NULL, text, "Error", MB_OK);
return;
}
// Показываем окно
ShowWindow(hWnd, cmdShow);
}
Хоть статья и для начинающих — на описании процесса создания окна мы останавливаться не будем, так как этой информации в интернете предостаточно.
Далее нам нужно в файл ресурсов добавить диалоговое окно:
Файлы ресурсов->Application A.rc -> Добавить ресурс->Dialog
Выглядеть это должно примерно так:
Приступим к кодингу.
#include <windows.h>
#include <stdio.h>
#include "resource.h"
#include "kWnd.h"
#define MYDISPLAY 1
char a1[100];
char razn[100];
char colvo[100];
int i;
int n = 0;
int summ = 0;
typedef struct tagMYREC
{
char a1[100];
char razn[100];
char colvo[100];
char n[100];
char summ[100];
} MYREC;
COPYDATASTRUCT MyCDS;
MYREC MyRec;
PCOPYDATASTRUCT pMyCDS;
BOOL CALLBACK DialogProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
//=======================================================================
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
kWnd mainWnd("Application A", hInstance, nCmdShow, WndProc,
MAKEINTRESOURCE(IDR_MENU1), 100, 100, 450, 150);
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (msg.wParam);
}
//=======================================================================
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static HINSTANCE hInst;
switch(uMsg)
{
case WM_CREATE:
DialogBox(hInst, MAKEINTRESOURCE(IDD_DIALOG1), hWnd, DialogProc);
break;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
BOOL CALLBACK DialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HWND hwndServer;
WIN32_FIND_DATA FindFileData;
HANDLE hFile;
switch(uMsg)
{
case WM_INITDIALOG:
hwndServer = FindWindow(NULL, "Application B");
if(!hwndServer)
{
hFile = FindFirstFile("Application B.exe", &FindFileData);
WinExec("App B.exe", 1);
FindClose(hFile);
}
break;
case WM_COPYDATA:
pMyCDS = (PCOPYDATASTRUCT) lParam;
SetDlgItemText(hDlg, IDC_EDIT4, (LPCSTR)((MYREC *)(pMyCDS->lpData))->n);
SetDlgItemText(hDlg, IDC_EDIT5, (LPCSTR)((MYREC *)(pMyCDS->lpData))->summ);
return true;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDEXIT:
EndDialog(hDlg, 0);
PostQuitMessage(0);
case IDOK:
GetDlgItemText(hDlg, IDC_EDIT1, a1, 22);
GetDlgItemText(hDlg, IDC_EDIT2, razn, 22);
GetDlgItemText(hDlg, IDC_EDIT3, colvo, 22);
hwndServer = FindWindow(NULL, "Application B");
if(!hwndServer)
{
MessageBox(hDlg, "Ошибка связи!", "Application A", MB_OK);
break;
}
strcpy(MyRec.a1, a1);
strcpy(MyRec.razn, razn);
strcpy(MyRec.colvo, colvo);
MyCDS.dwData = MYDISPLAY; // function identifier
MyCDS.cbData = sizeof( MyRec ); // size of data
MyCDS.lpData = &MyRec; // data structure
SendMessage( hwndServer,
WM_COPYDATA,
(WPARAM)(HWND) hDlg,
(LPARAM) (LPVOID) &MyCDS );
break;
return TRUE;
}
}
return FALSE;
}
Код комментировать я думаю особо не надо, но вкратце расскажу.
Сначала объявляются необходимые переменные и структура передаваемых данных.
В функции WinMain создается само окно.
В функции WndProc объявляется диалоговое окно и говорится, что функция обработки этого окна будет — DialogProc.
В этой функции сначала проверяется запущено ли приложение B, если нет и exe'шник этого приложения находится в одной папке с приложением A — оно запускается. В событии WM_COPYDATA говорится, что делать с принятыми данными(которые подсчитает за нас приложение B). А что с ними делать? Правильно! Их надо подставить в наше диалоговое окно. Далее следует событие WM_COMMAND, которое при нажатии на клавишу «Отправить» еще раз проверяет существует ли второе приложение, затем вкладывает указанные данные в нашу структуру и отправляет приложению B сообщение о том, что то приложение должно получить наши данные и вызвать свое сообщение WM_COPYDATA.
Вот и все — не каких проверок на то заполнены ли поля и заполнены ли они корректно мы делать не будем в этой статье.
А теперь приступим к приложению B. Первые два файла — kWnd.h и kWnd.cpp остаются неизменными. Само приложение мы будем строить уже не на базе диалогового окна, а на базе чистого хардкора.
Извиняюсь за то, что код не оформлен в тег Source, но по неведомой причине хабрапарсер не пускает его.
Если этот топик утвердят — я сразу же отредактирую его.
Application B.cpp
#include <windows.h>
#include <stdio.h>
#include "kWnd.h"
#include "resource.h"
#define MYDISPLAY 1
PCOPYDATASTRUCT pMyCDS; // структура передаваемых данных
int i;
int a1;
int razn;
int colvo;
int n = 0;
int summ = 0;
char str[100], str1[100], str2[100], str3[100], str4[100]; // строка записи в файл
DWORD dwSize;
HANDLE File; // сам файл
typedef struct tagMYREC // структура данных
{
char a1[100];
char razn[100];
char colvo[100];
char n[100];
char summ[100];
} MYREC;
COPYDATASTRUCT MyCDS;
MYREC MyRec;
enum UserMsg { UM_THREAD_DONE = WM_USER+1 };
struct ThreadManager {
ThreadManager(char _name) : name(_name) { nValue = 0; }
HWND hWnd;
char name;
int nValue;
int summValue;
};
ThreadManager tm_A(char("Поток A"));
DWORD WINAPI ThreadFuncA(LPVOID);
//===============Обьявление функций===================
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
//=====================================================
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
MSG msg;
// создание окна
kWnd mainWnd("Application B", hInstance, nShowCmd, WndProc, NULL, 100, 100, 450, 220);
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (msg.wParam);
}
//=====================================================
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static HINSTANCE hInst;
HDC hdc; //контекст для рисования
PAINTSTRUCT ps; //структура для рисования
// переменная дескриптора окна которому следует послать информацию
HWND hwndServer;
static HANDLE hThreadA;
ThreadManager* pTm;
WIN32_FIND_DATA FindFileData;
HANDLE hFile;
HWND edit;
char buff[260];
switch(uMsg)
{
case WM_CREATE:
hwndServer = FindWindow(NULL, "Application A");
if(!hwndServer)
{
hFile = FindFirstFile("Application A.exe", &FindFileData);
WinExec("Application B.exe", 1);
FindClose(hFile);
}
edit = CreateWindow("EDIT","text.txt", //Создаю едит
WS_CHILD | WS_VISIBLE |WS_BORDER,
10, 10, 50, 20,
hWnd,
(HMENU) 123,
(HINSTANCE) GetWindowLong(hWnd, GWL_HINSTANCE),
NULL);
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
TextOut(hdc, 100, 50, "Полученные значения:", sizeof("Полученные значения:"));
TextOut(hdc, 120, 80, str1, sizeof(str1));
TextOut(hdc, 120, 100, str2, sizeof(str2));
TextOut(hdc, 120, 120, str3, sizeof(str3));
TextOut(hdc, 50, 140, str4, sizeof(str4));
EndPaint(hWnd, &ps);
break;
case WM_COPYDATA:
pMyCDS = (PCOPYDATASTRUCT) lParam;
// подсчет n-го члена и суммы
a1 = atol(((MYREC *)(pMyCDS->lpData))->a1);
razn = atol(((MYREC *)(pMyCDS->lpData))->razn);
colvo = atol(((MYREC *)(pMyCDS->lpData))->colvo);
n = a1;
summ = a1;
////////////////////
tm_A.hWnd = hWnd;
hThreadA = CreateThread(NULL, 0, ThreadFuncA, &tm_A, 0, NULL);
if (!hThreadA)
MessageBox(hWnd, "Error of create hThreadA", NULL, MB_OK);
WaitForSingleObject(hThreadA, INFINITE);
////////////////////////////
sprintf(((MYREC *)(pMyCDS->lpData))->n, "%d", n);
sprintf(((MYREC *)(pMyCDS->lpData))->summ, "%d", summ);
hwndServer = FindWindow(NULL, "Application A");
if(!hwndServer)
{
MessageBox(hWnd, "Ошибка связи!", "Application B", MB_OK);
break;
}
strcpy(MyRec.n, ((MYREC *)(pMyCDS->lpData))->n);
strcpy(MyRec.summ, ((MYREC *)(pMyCDS->lpData))->summ);
MyCDS.dwData = MYDISPLAY;
MyCDS.cbData = sizeof( MyRec ); // size of data
MyCDS.lpData = &MyRec; // data structure
SendMessage( hwndServer, WM_COPYDATA, (WPARAM)(HWND) hWnd, (LPARAM) (LPVOID) &MyCDS );
sprintf(str1, "Первый член прогрессии: %s", ((MYREC *)(pMyCDS->lpData))->a1);
sprintf(str2, "Разность прогрессии: %s", ((MYREC *)(pMyCDS->lpData))->razn);
sprintf(str3, "Количество членов в прогрессии: %s", ((MYREC *)(pMyCDS->lpData))->colvo);
// создание строки для записи в файл
edit = FindWindowEx(hWnd, NULL, "Edit", NULL);
SendMessage(edit, WM_GETTEXT, (WPARAM)260, (LPARAM)buff);
sprintf(str, "Первый член = %s, разность = %s, n = %s, последний член = %s, сумма = %snr", ((MYREC *)(pMyCDS->lpData))->a1, ((MYREC *)(pMyCDS->lpData))->razn, ((MYREC *)(pMyCDS->lpData))->colvo, MyRec.n, MyRec.summ);
// и сама запись в файл
File=CreateFile(buff, GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
SetFilePointer(File, 0, 0, FILE_END); // ставим указатель в конеЦ
WriteFile(File,&str,sizeof(str),&dwSize,NULL);
sprintf(str4, "Успешно записано в файл: %s", buff);
InvalidateRect(hWnd, NULL, TRUE);
break;
case UM_THREAD_DONE:
pTm = (ThreadManager*)wParam;
sprintf(str1, "%s: count = %d", pTm->name, pTm->nValue);
InvalidateRect(hWnd, NULL, FALSE);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
//=====================================================
DWORD WINAPI ThreadFuncA(LPVOID lpv)
{
ThreadManager* pTm = (ThreadManager*)lpv;
for(int i = 0; i<colvo-1; i++)
{
n = n * razn;
summ = n + summ;
}
return 0;
}
//====================================================================
Вооот, этот длинный файл и делает всю сложную работу — принимает сообщение, вычисляет(в многопоточном режиме(не удивляетесь, это было одним из пунктов задания)), записывает, отправляет назад.
Этот код хоть и не много длиннее но мало чем отличается от первого, функции в комментариях не нуждаются (нуждаются конечно, но статья и так получилась длиннее чем ожидалось, так что увы… в MSDN...)
И вот что у нас получилось…
Я очень надеюсь что моя статья была кому либо полезна и очень извиняюсь перед теми кому она не понравилась. Жду комментариев к коду (говнокоду).
Автор: fritz321