操作系统与网络 2019-1-27

本文介绍了一个简易任务管理器项目的实现,包括获取进程路径、结束进程的方法,以及通过WM_COPYDATA消息实现进程间通信的过程。文章详细解释了如何在MFC环境下创建和操作进程快照,获取进程信息,并通过发送自定义消息实现不同应用程序之间的数据交换。

1.继续简易任务管理器项目

1.1 获取进程的路径

1.首先我们需要一个当前进程的句柄: HANDLE h_process = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID) (第一个参数是进程的权限,第二个参数是进程的继承性,第三个参数是要打开的进程ID);
2.如果进程句柄获取成功,那么使用 QueryFullProcessImageName 函数来获取进程的路径;

// 显示所有的进程信息
void CTaskManagerDlg::ShowAllProcessInfo()
{
	// 创建存进程信息的结构体
	PROCESSENTRY32 pe = {sizeof(pe)};

	// 创建进程的快照
	HANDLE h_snapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if(h_snapshot != INVALID_HANDLE_VALUE)
	{
		// 遍历快照中所有的进程
		BOOL b_flag = ::Process32First(h_snapshot, &pe);

		// 当有进程时,进行循环遍历
		while (b_flag)
		{
			// =============将进程的信息放到列表中=====================
			// 进程名
			m_lsProcessInfo.InsertItem(m_lsProcessInfo.GetItemCount(), pe.szExeFile);
			// 进程ID
			CString str_process_id;
			str_process_id.Format(L"%d", pe.th32ProcessID);
			m_lsProcessInfo.SetItemText(m_lsProcessInfo.GetItemCount()-1, 1, str_process_id);
			// 线程的数量
			CString str_thread_count;
			str_thread_count.Format(L"%d", pe.cntThreads);
			m_lsProcessInfo.SetItemText(m_lsProcessInfo.GetItemCount()-1, 2, str_thread_count);
			// ================================查找进程路径=================================
			// 进程路径
			HANDLE h_process = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID);	// 打开一个进程
			if(h_process)
			{
				wchar_t wsz_path[MAX_PATH] = {0};			// 接受进程的路径
				DWORD dw_size = sizeof(wsz_path);
				QueryFullProcessImageName(h_process, 0, wsz_path, &dw_size);	// 获取进程的路径
				//GetModuleFileNameEX(h_process, NULL, wsz_path, MAX_PATH);
				m_lsProcessInfo.SetItemText(m_lsProcessInfo.GetItemCount()-1, 3, wsz_path);
			}
			::CloseHandle(h_process);
			h_process = 0;
			// ================================查找进程路径=================================
			// =============将进程的信息放到列表中=====================
			b_flag = ::Process32Next(h_snapshot, &pe);					// 查找下一个进程
		}
	}

	// 关闭句柄
	::CloseHandle(h_snapshot);
	h_snapshot = 0;
}

1.2 完成 结束进程 按钮函数

1.首先给 结束进程 按钮添加消息处理函数;
2.获取列表中被选中的项的索引: int index = m_lsProcessInfo.GetSelectionMark() ;
3.若有选中项,则获取进程的ID: CString str_process_id = m_lsProcessInfo.GetItemText(index, 1) ;
4.将ID转换为整型并打开该进程句柄: DWORD dw_process_id = _wtoi(str_process_id) ; HANDLE h_process = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dw_process_id) ;
5.结束进程并关闭句柄: ::TerminateProcess(h_process, 0) ; ::CloseHandle(h_process) ; h_process = 0 ;

// 结束进程
void CTaskManagerDlg::OnBnClickedButton1()
{
	// 获取列表中选中的项
	int index = m_lsProcessInfo.GetSelectionMark();
	if(index != -1)		// 当没选中项的时候返回值为-1
	{
		// 拿进程的ID
		CString str_process_id = m_lsProcessInfo.GetItemText(index, 1);
		DWORD dw_process_id = _wtoi(str_process_id);		// 转换成整型
		HANDLE h_process = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dw_process_id);	// 打开选中的进程句柄
		if(h_process)
		{
			// 结束进程
			::TerminateProcess(h_process, 0);
			::CloseHandle(h_process);
			h_process = 0;
		}
	}
}

2.进程间通信

2.1 创建一个 其他项目->空白解决方案 ;

2.2 添加->新建项目 ,创建一个基于对话框的MFC项目,名字为 Send ;再 添加->新建项目 创建一个基于对话框的MFC项目,名字为 Recv ;

1.现在的情况是在一个工作项目中我们创建了两个子工作目录;
2.对于这两个项目 Send 和 Recv 我们可以同时运行: 右键坚决方案中的Recv -> 调试 -> 启动新实例 ; 右键坚决方案中的Send -> 调试 -> 启动新实例 即可同时开启两个对话框;

2.3 两个对话框进行通信

1.先将 Send 对话框上添加一个 Edit Control 控件和一个 Button 按钮命名为 发消息 控件;在 Recv 对话框上添加一个 Edit Control 控件;
2.现在我们想在 Send 对话框的 Edit 控件上输入一串字符,点击 发消息 按钮,这一串字符在 Recv 对话框上的 Edit 控件中进行显示;
3.首先对于 Send 对话框,我们给其上的 Edit 控件添加一个变量,类别选择 Value ,变量类型选择 CString ,名为 m_str_send ;
4.从控件中往下取数据使用的函数 UpDateData(TRUE) ,当参数为 FALSE 时是向控件上发送字符串;
5.取下来数据之后我们需要发送数据,发送消息我们需要获取要接受数据窗口的句柄: HWND h_recv_wnd = ::FindWindow(0, L"Recv") ;
6.发送消息: ::SendMessage(h_recv_wnd, UM_SEND_DATA, m_str_send.GetLength(), (LPARAM)m_str_send.GetBuffer()) ;
7. UM_SEND_DATA 是一个我们定义在总工程文件夹下的 MSG 文件夹下的 Msg.h 中定义的,要使用 UM_SEND_DATA 宏需要引用头文件: #include “…/MSG/Msg.h” ;
8.对于 Recv 对话框,我们也给其上的 Edit 控件添加一个 value 类别的 CString m_str_recv ;
9.给 RecvDlg 类添加一个自定义消息处理函数: afx_msg LRESULT OnRecvData(WPARAM wParam, LPARAM lParam) ;
10.添加消息映射表: ON_MESSAGE(UM_SEND_DATA, &CRecvDlg::OnRecvData) ;
11.在函数 CRecvDlg::OnRecvData 中进行接受数据: int str_length = wParam ; wchar_t pwsz = (wchar_t)lParam ;

// Send
void CSendDlg::OnBnClickedButton1()
{
	// 获取edit控件中的字符
	UpdateData(TRUE);
	//TRACE(m_str_send);

	// 获取Recv这个窗口的句柄
	//HWND h_recv_wnd = ::FindWindow(0, L"Recv");
	//::SendMessage(h_recv_wnd, UM_SEND_DATA, m_str_send.GetLength(), (LPARAM)m_str_send.GetBuffer());
}

// Recv
LRESULT CRecvDlg::OnRecvData(WPARAM wParam, LPARAM lParam)
{
	int str_length = wParam;
	wchar_t pwsz = (wchar_t)lParam;

	return 0;
}

12.下断点进行测试,发现 str_length 的值正确,但是 pwsz 的值不正确,这是因为对于两个程序 Send 和 Recv ,它们都有自己的虚拟内存地址,我们从 Send 给 Recv 发送数据,数据的地址在 Send 程序中假设是 0x10 ,那么在 Recv 中就会自动搜索 Recv 的 0x10 处的内容,但是这两个程序的虚拟内存地址并不是相同的,相同的地址存储的内容可能是不同的,因此我们需要为发送的数据创建一块公用的地址来进行储存;
13.我们需要修改发送端发送的消息类型,不能发送自定义消息,而需要发送 WM_COPYDATA 消息;

// 在发送端
void CSendDlg::OnBnClickedButton1()
{
	// 获取edit控件中的字符
	UpdateData(TRUE);

	// 发送 WM_COPYDATA 消息
	COPYDATASTRUCT cds;
	cds.dwData = 0;
	cds.cbData = (m_str_send.GetLength()+1)*2;
	cds.lpData = m_str_send.GetBuffer();

	HWND h_recv_wnd = ::FindWindow(0, L"Recv");
	::SendMessage(h_recv_wnd, WM_COPYDATA, (WPARAM)(this->m_hWnd), (LPARAM)&cds);
}

14.对于 Recv 端,我们需要给 RecvDlg 类添加一个处理 COPYDATA 的消息处理函数;
15.在 CRecvDlg::OnCopyData 函数中获取字符串并放到控件中;

BOOL CRecvDlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)
{
	m_str_recv = (wchar_t*)(pCopyDataStruct->lpData);
	UpdateData(FALSE);

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

2.4 在 Send 对话框上添加 剪贴板Ctrl+c 按钮

1.给 Send 对话框添加 剪贴板Ctrl+c 按钮,同时给 Recv 对话框添加 剪贴板Ctrl+v 按钮;
2.给 剪贴板Ctrl+c 按钮添加消息处理函数;

void CSendDlg::OnBnClickedButton2()
{
	// 1.打开 剪贴板

	// 2.清空 剪贴板

	// 3.分配一个全局堆空间

	// 4.给全剧堆加锁

	// 5.把数据放到全局堆中

	// 6.给全局堆解锁

	// 7.将全局堆交给剪贴板(设置剪贴板的数据格式)

	// 8.关闭剪贴板
	
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值