进程基本概念和通信方式

进程

基本概念-进程和子进程

  • 程序是计算机指令的集合,它以文件的形式存储在磁盘上,而**进程通常被定义为一个正在运行的程序的实例,是一个程序在其自身的地址空间中的一次执行活动.**一个程序可以对应多个进程.

  • 进程是资源申请,高度和独立运行的单位,因此,它使用系统中的运行资源,程序不能申请系统资源,不能被系统高度也不能作为独立运行的单位,因此它不占系统运行资源.

  • 进程组成:

    • 操作系统用来管理进行的内核对象

      • 内核对象也是系统用来存放关于进程的统计信息的地方。内核对象是操作系统内部分配的一个内在块,该内存块是一种数据结构,其成员负责维护该对象的各种信息

    • 地址空间

      • 它包含了所有可执行模块或DLL模块的代码和数据,他也包含动态内存分配的空间,例如线程的栈和堆分配空间

  • 进程从来不执行任何东西,他只是线程的容器,若要进行某些操作,他必须拥有一个在他的环境汇总运行的线程,此线程负责执行包含在进程的地址空间的中的代码。真正完成代码执行的是线程,进程只是纯种的容器(线程的执行环境)

  • 子进程:由另一进程(对应称之为父进程)所创建的进程

  • 单任务的同步机制

  • 需要保护地址空间

  • 子进程的线程既可以在父进程终止之后执行我们的代码也可以在父进程运行的过程中执行代码

进程的创建以及使用

//创建一个用EDGE浏览器打开百度
#include <windows.h>
#include <stdio.h>
#include <tchar.h>

void RunExe()
{
	STARTUPINFOW strStarupInfo;
	memset(&strStarupInfo, 0, sizeof(strStarupInfo));
	strStarupInfo.cb = sizeof(strStarupInfo);//结构的小,以字节为单位

	PROCESS_INFORMATION szProcessInformation;
	memset(&szProcessInformation, 0, sizeof(szProcessInformation));

	TCHAR szApplicationName[] =
		_T("C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe");
	TCHAR lpCommandLine[] = _T("\"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe\"http://www.baidu.com");

	int iRet = CreateProcess(
		//szApplicationName,
		NULL,
		lpCommandLine,
		NULL,
		NULL,
		FALSE,
		CREATE_NEW_CONSOLE,
		NULL,
		NULL,
		&strStarupInfo,
		&szProcessInformation
	);	
	if (iRet)
	{
		//创建成功
		WaitForSingleObject(szProcessInformation.hProcess, 3000);
		CloseHandle(szProcessInformation.hProcess);
		CloseHandle(szProcessInformation.hThread);
		szProcessInformation.dwProcessId = 0;
		szProcessInformation.dwThreadId = 0; 
		szProcessInformation.hThread = NULL;
		szProcessInformation.hProcess = NULL;
	}
	else
	{
		printf_s("Create Failed iRet = %d errorcode = %d\n", iRet,GetLastError());
	}
}
// CreateProcessW(
// 	_In_opt_ LPCWSTR lpApplicationName,
// 	_Inout_opt_ LPWSTR lpCommandLine,
// 	_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
// 	_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
// 	_In_ BOOL bInheritHandles,
// 	_In_ DWORD dwCreationFlags,
// 	_In_opt_ LPVOID lpEnvironment,
// 	_In_opt_ LPCWSTR lpCurrentDirectory,
// 	_In_ LPSTARTUPINFOW lpStartupInfo,
// 	_Out_ LPPROCESS_INFORMATION lpProcessInformation
// );
int main()
{
	printf("This is Bingo\n");
	RunExe();
	system("pause");
	return 0;
}

进程间的通信方式

  1. socket 编程 IP和端口
  2. 剪切板 剪切板的内核对象
  3. 邮槽 邮槽的内核对象
  4. 匿名管道
  5. 命名管道
  6. Copy_data_findwindows wm_copydata 很多书籍都没有

剪切板

  • 剪切板

    • 系统维护管理的一块内存区域。
  • 剪切板审计:

    • 审计计算
    • windows程序控制(安全企业)
    • 不让复制和剪切
  • 虚拟机和物理机:开发用虚拟机外网用物理机相互的拷贝。物理机-》虚拟机,虚拟机 !-> 物理机 限制剪切板的作用

void CClipBoardDlg::OnBnClickedSendBtn()
{
	// 1 打开剪切板
	if (OpenClipboard())
	{
		//清空剪切板
		EmptyClipboard();
		char* szSendBuf;  //要发送的数据

		//获取编辑框的内容
		CStringW strSendW;
		GetDlgItemText(IDC_SEND_EDIT, strSendW);
		CStringA strSend = (CStringA)strSendW;//确保发送和接收是同一长度的

		//分配一个句柄,内存对象的句柄就是hClip   全局变量
		HANDLE hClip = GlobalAlloc(GMEM_MOVEABLE, strSend.GetLength() + 1);

		//将剪切板句柄加锁
		szSendBuf = (char*)GlobalLock(hClip);
		strcpy(szSendBuf, strSend);
		TRACE("szSendBuf = %s", szSendBuf);
		GlobalUnlock(hClip);
		//将数据放入剪切板
		SetClipboardData(CF_TEXT,hClip);
		//关闭剪切板
		CloseClipboard();
	}

}

void CClipBoardDlg::OnBnClickedRecvBtn()
{
	// 1 打开剪切板
	if (OpenClipboard())
	{
		//确认剪切板是否可用
		if (IsClipboardFormatAvailable(CF_TEXT))
		{
			HANDLE hClip;
			char* pBuf;
			//向剪切板要数据
			hClip = GetClipboardData(CF_TEXT);
			pBuf = (char*)GlobalLock(hClip);
			USES_CONVERSION;
			LPCWSTR strBuf = A2W(pBuf);
			GlobalUnlock(hClip);
			SetDlgItemText(IDC_RECV_EDIT, strBuf);
		}
		
		CloseClipboard();
	}
}

邮槽

​ 使用邮槽通信的进程分为服务端和客户端邮槽由服务端创建,在创建时需要指定邮槽名,创建后服务端得到邮槽的句柄。在邮槽创建后,客户端可以通过邮槽名打开邮槽,在获得句柄后可以向邮槽写入消息
邮槽通信是单向的,只有服务端才能从邮槽中读取消息,客户端只能写入消息
通过邮槽通信的数据可以是任意格式的,但是一条消息不能大于424字节
邮槽除了在本机内进行进程间通信外,在主机间也可以通信。但是在主机间进行邮槽通信,数据通过网络传播时使用的是数据报协议(UDP),所以是一种不可靠的通信。通过网络进行邮槽通信时,客户端必须知道服务端的主机名或域名

CreateMailslot

服务端

void CChildView::OnSlot()
{
	//    "\\\\.\\mailslot\\Mymailslot    \\.\mailslot\Mymailslot 
	//  1  创建一个邮槽
	LPCTSTR szSlotName = TEXT("\\\\.\\mailslot\\Mymailslot");
	HANDLE hSlot = CreateMailslot(szSlotName,
		0,                             // no maximum message size 
		MAILSLOT_WAIT_FOREVER,         // no time-out for operations 
		NULL); // default security

	if (hSlot == INVALID_HANDLE_VALUE)
	{
		TRACE("CreateMailslot failed with %d\n", GetLastError());
		return;
	}
	// 2 读取数据
	char szBuf[100] = { 0 };
	DWORD dwRead;
	TRACE("Begin ReadFile");
	if (!ReadFile(hSlot, szBuf, 100, &dwRead, NULL))
	{
		MessageBox(_T("读取数据失败"));
		CloseHandle(hSlot);
		return;
	}
	TRACE("End ReadFile");
	MessageBox((CStringW)szBuf);
	CloseHandle(hSlot);
}


客户端

void CChildView::OnSend()
{
	// TODO: 在此添加命令处理程序代码
	// 创建一个文件句柄
	LPCTSTR szSlotName = TEXT("\\\\.\\mailslot\\Mymailslot");
	HANDLE hMailSlot =
		CreateFile(szSlotName, FILE_GENERIC_WRITE,
			FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hMailSlot == INVALID_HANDLE_VALUE)
	{
		TRACE("CreateFile failed with %d\n", GetLastError());
		return;
	}

	//写入数据
	char szBuf[] = "Bingo is handsome";
	DWORD dwWrite;
	TRACE("Send begin");
	if (!WriteFile(hMailSlot, szBuf, strlen(szBuf) + 1, &dwWrite, NULL))
	{
		MessageBox(_T("写入数据失败"));
		CloseHandle(hMailSlot);
		return;
	}

	TRACE("Send end");
	CloseHandle(hMailSlot);
}

匿名管道

匿名管道是一个没有命名的单向管道,本质上就是一个共享的内存区域。通常用来在父进程和子进程之间通信。只能实现本地两个进程之间的通信。不能实现网络通信

共享内存

CreatePipe(
	_Out_ PHADLE hReadPipe,  //该变量接收管道的读取句柄
	_Out_ PHADLE hWritePipe, //该变量接收管道的写句柄
	_In_opt_ LPSECURITY_ATTRIBUTES lpPipeAttributes,
    //NULL
	_In_ DWORD nSize//管道缓冲区的大小0,默认缓冲区大小
);
是否能被子进程继承

父进程

void CChildView::OnPipeCreate()
{
	// TODO: 在此添加命令处理程序代码
	//创建匿名管道
	SECURITY_ATTRIBUTES sa;
	sa.bInheritHandle = TRUE;
	sa.lpSecurityDescriptor = NULL;
	sa.nLength = sizeof(SECURITY_ATTRIBUTES);
	if (!CreatePipe(&hReadPipe, &hWritePipe, &sa, 0))
	{
		MessageBox(_T("匿名管道创建失败"));
		return;
	}
	//创建子进程
	STARTUPINFO strStartupInfo; //用来指定新进程窗口如何显示
	memset(&strStartupInfo, 0, sizeof(strStartupInfo));
	strStartupInfo.cb = sizeof(strStartupInfo);
	strStartupInfo.dwFlags = STARTF_USESTDHANDLES;
	strStartupInfo.hStdInput = hReadPipe;
	strStartupInfo.hStdOutput = hWritePipe;
	strStartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);

	PROCESS_INFORMATION szProcessInformation;
	memset(&szProcessInformation, 0, sizeof(szProcessInformation));

	int iRet = CreateProcess(
		_T("MailSlotClient.exe"),
		NULL,
		NULL,
		NULL,
		TRUE,
		0,
		NULL,
		NULL,
		&strStartupInfo,
		&szProcessInformation
	);
	if (iRet)
	{
		//创建成功
		CloseHandle(szProcessInformation.hProcess);
		CloseHandle(szProcessInformation.hThread);
		szProcessInformation.dwProcessId = 0;
		szProcessInformation.dwThreadId = 0;
		szProcessInformation.hThread = NULL;
		szProcessInformation.hProcess = NULL;
		
	}
	else
	{
		
		CloseHandle(hReadPipe);
		CloseHandle(hWritePipe);
		hReadPipe = NULL;
		hWritePipe = NULL;
		MessageBox(_T("创建子进程失败"));
		return;
	}

	
}


void CChildView::OnPipeRead()
{
	char szBuf[100] = { 0 };
	DWORD dwRead;
	TRACE("Begin ReadFile");
	if (!ReadFile(hReadPipe, szBuf, 100, &dwRead, NULL))
	{
		MessageBox(_T("读取数据失败"));
		return;
	}
	TRACE("End PipeReadFile");
	MessageBox((CStringW)szBuf);
}


void CChildView::OnPipeWrite()
{
	//写入数据
	char szBuf[] = "Bingo hen shuai";
	DWORD dwWrite;
	if (!WriteFile(hWritePipe, szBuf, strlen(szBuf) + 1, &dwWrite, NULL))
	{
		MessageBox(_T("写入数据失败"));
		return;
	}
}

子进程

hReadCliPipe =GetStdHandle(STD_INPUT_HANDLE);
hWriteCliPipe = GetStdHandle(STD_OUTPUT_HANDLE);
void CChildView::OnCliPipeRead()
{
	char szBuf[100] = { 0 };
	DWORD dwRead;
	TRACE("Begin ReadFile");  //查找所有引用shift + alt + F
	if (!ReadFile(hReadCliPipe, szBuf, 100, &dwRead, NULL))
	{
		MessageBox(_T("读取数据失败"));
		return;
	}
	TRACE("End PipeReadFile");
	MessageBox((CStringW)szBuf);
}


void CChildView::OnCliPipeWrite()
{
	char szBuf[] = "Bingo Bingo";
	DWORD dwWrite;
	if (!WriteFile(hWriteCliPipe, szBuf, strlen(szBuf) + 1, &dwWrite, NULL))
	{
		MessageBox(_T("写入数据失败"));
		CloseHandle(hWriteCliPipe);
		return;
	}
	CloseHandle(hWriteCliPipe);
}

命名管道

与 socket相似,支持网络之间不同进程的通信

HANDLE CreateNamedPipeA(

 LPCSTR         lpName,  // \.\pipe<i>pipename

 DWORD         dwOpenMode,

 DWORD         dwPipeMode,

 DWORD         nMaxInstances,

 DWORD         nOutBufferSize,

 DWORD         nInBufferSize,

 DWORD         nDefaultTimeOut,

 LPSECURITY_ATTRIBUTES lpSecurityAttributes

);
------------------------------
等待连接
BOOL ConnectNamedPipe(
  HANDLE       hNamedPipe,
  LPOVERLAPPED lpOverlapped
);
------------------------------
------------------------------WaitNamedPipe(szNamedPipeName, NMPWAIT_WAIT_FOREVER)
------------------------------
客户端
void CChildView::OnConnectNamedPipe()
{
	LPCTSTR szNamedPipeName = TEXT("\\\\.\\pipe\\mypipe");
	if (0 == WaitNamedPipe(szNamedPipeName, NMPWAIT_WAIT_FOREVER))
	{
		MessageBox(_T("当前没有可以利用的管道"));
		return;
	}
	hNamedPipe =
		CreateFile(szNamedPipeName, GENERIC_READ | GENERIC_WRITE,
			0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hNamedPipe == INVALID_HANDLE_VALUE)
	{
		TRACE("CreateFile failed with %d\n", GetLastError());
		MessageBox(_T("打开命名管道失败!"));
		hNamedPipe = NULL;
		return;
	}

}


void CChildView::OnReadNamedPipe()
{
	char szBuf[100] = { 0 };
	DWORD dwRead;
	if (!ReadFile(hNamedPipe, szBuf, 100, &dwRead, NULL))
	{
		MessageBox(_T("读取数据失败"));
		return;
	}
	MessageBox((CStringW)szBuf);
}


void CChildView::OnWriteNamedPipe()
{
	char szBuf[] = "NAMEDPIPE CLIENT";
	DWORD dwWrite;
	if (!WriteFile(hNamedPipe, szBuf, strlen(szBuf) + 1, &dwWrite, NULL))
	{
		MessageBox(_T("写入数据失败"));
		CloseHandle(hWriteCliPipe);
		return;
	}
	CloseHandle(hWriteCliPipe);
}
服务端
void CChildView::OnCreateNamedPipe()
{
	//1 创建一个命名管道
	LPCTSTR szPipeName = TEXT("\\\\.\\pipe\\mypipe");
	hNamedPipe = CreateNamedPipe(szPipeName,PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
		PIPE_TYPE_BYTE, 1, 1024, 1024, 0, NULL);
	if (hNamedPipe == INVALID_HANDLE_VALUE)
	{
		TRACE("CreateNamedhPipe failed with %d\n", GetLastError());
		MessageBox(_T("创建命名管道失败"));
		return;
	}
	// 2 等待客户端的连接
	HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	if (NULL == hEvent)
	{
		MessageBox(_T("创建事件失败"));
		CloseHandle(hNamedPipe);
		hNamedPipe = NULL;
		return;
	}

	OVERLAPPED ovlap;
	ZeroMemory(&ovlap, sizeof(OVERLAPPED));
	ovlap.hEvent = hEvent;
	//等待连接
	if (!ConnectNamedPipe(hNamedPipe, &ovlap))
	{
		if (ERROR_IO_PENDING != GetLastError())
		{
			MessageBox(_T("等待客户端连接失败"));
			CloseHandle(hNamedPipe);
			CloseHandle(hEvent);
			hNamedPipe = NULL;
			hEvent = NULL;
			return;
		}
	}
	if (WaitForSingleObject(hEvent,INFINITE) == WAIT_FAILED)
	{
		MessageBox(_T("等待对象失败"));
		CloseHandle(hNamedPipe);
		CloseHandle(hEvent);
		hNamedPipe = NULL;
		hEvent = NULL;
		return;
	}
}


void CChildView::OnSreadNamedPipe()
{
	char szBuf[100] = { 0 };
	DWORD dwRead;
	if (!ReadFile(hNamedPipe, szBuf, 100, &dwRead, NULL))
	{
		MessageBox(_T("读取数据失败"));
		return;
	}
	MessageBox((CStringW)szBuf);
}


void CChildView::OnSwriteNamedpipe()
{
	//写入数据
	char szBuf[] = "OnNamedPipe Server";
	DWORD dwWrite;
	if (!WriteFile(hNamedPipe, szBuf, strlen(szBuf) + 1, &dwWrite, NULL))
	{
		MessageBox(_T("写入数据失败"));
		return;
	}
}

WM_COPYDATA

wParam传递数据的窗口的句柄

​ 帕拉姆:指向COPYDATASTRUCT结构的指针,该结构包含要传递的数据。如果接收方应用程序处理此消息,则应返回TRUE;否则,应返回FALSE

SPY++ 工具 专门用来查找窗口句柄

​ 要给进程发数据,首先要拿到进程的窗口句柄,也就必须先拿到标题

​ 句柄是不确定的,标题是确定的

---------------发送端--------------------
void CWMCOPYDATASENDDlg::OnBnClickedSend()
{
	// TODO: 在此添加控件通知处理程序代码
	//必须要知道标题 句柄
	CString strWindowTitle = _T("MFCRecv");
	CString strDataToSend = _T("Hello, this is Bingo WM_COPYDATA");
	//句柄
	HWND hRecvWnd = ::FindWindow(NULL, strWindowTitle.GetBuffer(0));
	if (hRecvWnd != NULL && ::IsWindow(hRecvWnd))
	{
		//数据的封装
		COPYDATASTRUCT cpd;
		cpd.dwData = 0;
		cpd.cbData = strDataToSend.GetLength() * sizeof(TCHAR);
		cpd.lpData = (PVOID)strDataToSend.GetBuffer(0);
		::SendMessage(hRecvWnd, WM_COPYDATA, (WPARAM)(AfxGetApp()->m_pMainWnd), (LPARAM)&cpd);

	}
	strDataToSend.ReleaseBuffer();
}

---------------接收端--------------------
BOOL CWMCOPYDATADlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)
{
	//消息响应函数
	//解析数据
	LPCTSTR szText = (LPCTSTR)(pCopyDataStruct->lpData);
	DWORD dwLength = (DWORD)pCopyDataStruct->cbData;

	TCHAR szRecvText[1024] = { 0 };
	memcpy(szRecvText, szText, dwLength);
	MessageBox(szRecvText, _T("Bingo"), MB_OK);

	return CDialogEx::OnCopyData(pWnd, pCopyDataStruct);
}

进程的通信总结

  • 剪切板比较简单。剪切板和匿名管道只能实现同一机器的两个进程通信,而不能实现网络进程之间的通信。
  • 邮槽是基于广播的,可以一对多发送。但只能一个发送,一个接收,要想同时发送接收,须写两次代码。邮槽的缺点:传输的数据量很小424字节以下。
  • 命名管道和邮槽可以进行网络通信。命名管道只能是点对点的单一通信。
  • WM_COPY_DATA封装数据和解析数据。非常方便。如果数据量大,建议使用命名管道
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值