Взаимодействие процессов (приложений) на WinApi

в 3:03, , рубрики: c++, WinAPI, для начинающих, Песочница, метки: , ,

Сейчас в интернетах можно встретить много Hello World'ов на WinApi, но когда новичок спрашивает как в этот Hello World добавить пару нужных функций — закидывают тухлыми яблоками и тут же отправляют в пресловутый и могучий MSDN.

В этой статье от новичка новичкам я расскажу, как построить два простых приложения которые будут взаимодействовать друг с другом — одно будет принимать на вход параметры геометрической прогрессии и передавать её второму, второе же приложение будет рассчитывать n-ый член прогрессии, записывать его в файл и передавать ответ первому приложению.

image

Итак, поехали (©Гагарин).

С тем что нам нужно мы уже определились во введении, разрабатывать мы будем в 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

Выглядеть это должно примерно так:

image

Приступим к кодингу.

#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...)

И вот что у нас получилось…

image

image

Я очень надеюсь что моя статья была кому либо полезна и очень извиняюсь перед теми кому она не понравилась. Жду комментариев к коду (говнокоду).

Автор: fritz321

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js