Win32 - 进程间通信(IPC)管道

1、匿名管道

匿名管道是一个没有命名的单向管道,本质上就是一个共享的内存,抽象成是管道。

通常用来在父进程和子进程之间通信。只能实现本地两个进程之间的通信。不能实现网络通信。

匿名管道(Anonymous Pipe):
匿名管道是一种无名称的管道,仅限于在父子进程或者兄弟进程之间进行通信。
匿名管道只能在同一个计算机上的相关进程之间进行通信,不能跨网络使用。
匿名管道是单向的,只能支持单向的数据流传输,例如从父进程到子进程。
匿名管道一般用于简单的进程间通信需求,例如父进程将数据传递给子进程进行处理。

实现功能:父进程启动子进程

示例代码:


// ParentDlg.h: 头文件
//

#pragma once


// CParentDlg 对话框
class CParentDlg : public CDialogEx
{
// 构造
public:
	CParentDlg(CWnd* pParent = nullptr);	// 标准构造函数

// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_PARENT_DIALOG };
#endif

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV 支持


// 实现
protected:
	HICON m_hIcon;

	// 生成的消息映射函数
	virtual BOOL OnInitDialog();
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	DECLARE_MESSAGE_MAP()
public:
	HANDLE m_hRead ;
	HANDLE m_hWrite ;

	afx_msg void OnBnClickedBtnCreate();
};

// ParentDlg.cpp: 实现文件
//

#include "pch.h"
#include "framework.h"
#include "Parent.h"
#include "ParentDlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// CParentDlg 对话框



CParentDlg::CParentDlg(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_PARENT_DIALOG, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CParentDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CParentDlg, CDialogEx)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_BTN_Create, &CParentDlg::OnBnClickedBtnCreate)
END_MESSAGE_MAP()


// CParentDlg 消息处理程序

BOOL CParentDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// 设置此对话框的图标。  当应用程序主窗口不是对话框时,框架将自动
	//  执行此操作
	SetIcon(m_hIcon, TRUE);			// 设置大图标
	SetIcon(m_hIcon, FALSE);		// 设置小图标

	// TODO: 在此添加额外的初始化代码

	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

// 如果向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。  对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。

void CParentDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // 用于绘制的设备上下文

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// 使图标在工作区矩形中居中
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// 绘制图标
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialogEx::OnPaint();
	}
}

//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CParentDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}


// 启动子进程
void CParentDlg::OnBnClickedBtnCreate()
{
	//创建匿名管道
//第1,2个参数:读写句柄
//第3个参数:安全描述符
	SECURITY_ATTRIBUTES sa;
	sa.bInheritHandle = TRUE;//子进程继承父进程的句柄
	sa.lpSecurityDescriptor = NULL;//安全描述符
	sa.nLength = sizeof(SECURITY_ATTRIBUTES);//结构体大小
	//第4个参数:缓冲区大小
	if (!CreatePipe(&m_hRead, &m_hWrite, &sa, 0))
	{
		MessageBox(L"创建匿名管道失败", L"提示");
		return;
	}



	TCHAR szCmdLine[MAX_PATH] = L"Sub.exe";
	//启动信息
	STARTUPINFO si = { 0 };
	si.cb = sizeof(STARTUPINFO);
	si.dwFlags = STARTF_USESTDHANDLES;//标准输入输出
	si.hStdInput = m_hRead;//标准输入
	si.hStdOutput = m_hWrite;//写入
	si.hStdError = GetStdHandle(STD_ERROR_HANDLE);//标准输入输出错误句柄
	//进程信息
	PROCESS_INFORMATION pi = { 0 };

	if (!CreateProcess(NULL, szCmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
	{
		CloseHandle(m_hRead);
		CloseHandle(m_hWrite);
		m_hRead = NULL;
		m_hWrite = NULL;
		MessageBox(L"创建子进程失败", L"提示");
		return;
	}
}

再创建一个Sub的MFC测试程序,然后编译运行将子进程的*.exe放到父进程中

image

运行结果:

image

 


实现功能:父进程写入数据、子进程读出数据,注:子进程写入数据、父进程读取数据原理一致

父进程:


// ParentDlg.h: 头文件
//

#pragma once


// CParentDlg 对话框
class CParentDlg : public CDialogEx
{
// 构造
public:
	CParentDlg(CWnd* pParent = nullptr);	// 标准构造函数

// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_PARENT_DIALOG };
#endif

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV 支持


// 实现
protected:
	HICON m_hIcon;

	// 生成的消息映射函数
	virtual BOOL OnInitDialog();
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	DECLARE_MESSAGE_MAP()
public:
	HANDLE m_hRead ;
	HANDLE m_hWrite ;

	afx_msg void OnBnClickedBtnCreate();
	afx_msg void OnBnClickedBtnWrite();
	CString m_str;// 绑定编辑框
};

// ParentDlg.cpp: 实现文件
//

#include "pch.h"
#include "framework.h"
#include "Parent.h"
#include "ParentDlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// CParentDlg 对话框



CParentDlg::CParentDlg(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_PARENT_DIALOG, pParent)
	, m_str(_T(""))
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CParentDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Text(pDX, IDC_EDIT1, m_str);
}

BEGIN_MESSAGE_MAP(CParentDlg, CDialogEx)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_BTN_Create, &CParentDlg::OnBnClickedBtnCreate)
	ON_BN_CLICKED(IDC_BTN_Write, &CParentDlg::OnBnClickedBtnWrite)
END_MESSAGE_MAP()


// CParentDlg 消息处理程序

BOOL CParentDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// 设置此对话框的图标。  当应用程序主窗口不是对话框时,框架将自动
	//  执行此操作
	SetIcon(m_hIcon, TRUE);			// 设置大图标
	SetIcon(m_hIcon, FALSE);		// 设置小图标

	// TODO: 在此添加额外的初始化代码

	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

// 如果向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。  对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。

void CParentDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // 用于绘制的设备上下文

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// 使图标在工作区矩形中居中
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// 绘制图标
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialogEx::OnPaint();
	}
}

//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CParentDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}


// 启动子进程
void CParentDlg::OnBnClickedBtnCreate()
{
	//创建匿名管道
//第1,2个参数:读写句柄
//第3个参数:安全描述符
	SECURITY_ATTRIBUTES sa;
	sa.bInheritHandle = TRUE;//子进程继承父进程的句柄
	sa.lpSecurityDescriptor = NULL;//安全描述符
	sa.nLength = sizeof(SECURITY_ATTRIBUTES);//结构体大小
	//第4个参数:缓冲区大小
	if (!CreatePipe(&m_hRead, &m_hWrite, &sa, 0))
	{
		MessageBox(L"创建匿名管道失败", L"提示");
		return;
	}



	TCHAR szCmdLine[MAX_PATH] = L"Sub.exe";
	//启动信息
	STARTUPINFO si = { 0 };
	si.cb = sizeof(STARTUPINFO);
	si.dwFlags = STARTF_USESTDHANDLES;//标准输入输出
	si.hStdInput = m_hRead;//标准输入
	si.hStdOutput = m_hWrite;//写入
	si.hStdError = GetStdHandle(STD_ERROR_HANDLE);//标准输入输出错误句柄
	//进程信息
	PROCESS_INFORMATION pi = { 0 };

	if (!CreateProcess(NULL, szCmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
	{
		CloseHandle(m_hRead);
		CloseHandle(m_hWrite);
		m_hRead = NULL;
		m_hWrite = NULL;
		MessageBox(L"创建子进程失败", L"提示");
		return;
	}
}



void CParentDlg::OnBnClickedBtnWrite()
{
	if (m_hWrite == NULL)
	{
		MessageBox(L"请先点击【创建子进程】", L"提示", MB_ICONWARNING);
		return;
	}

	// 从编辑框获取用户输入
	UpdateData(TRUE);   // 将界面输入同步到 m_str

	if (m_str.IsEmpty())
	{
		MessageBox(L"请输入要发送的内容", L"提示", MB_ICONINFORMATION);
		return;
	}

	// 写入管道(宽字符,包含结尾的 L'\0')
	DWORD dwWrite = 0;
	// 字节数 = (字符数 + 1) * sizeof(wchar_t)
	DWORD dwBytesToWrite = (m_str.GetLength() + 1) * sizeof(wchar_t);
	BOOL bRet = WriteFile(m_hWrite, (LPCVOID)(LPCTSTR)m_str, dwBytesToWrite, &dwWrite, NULL);

	if (!bRet || dwWrite != dwBytesToWrite)
	{
		MessageBox(L"写入管道失败", L"错误", MB_ICONERROR);
		return;
	}

	// 可选:清空输入框表示已发送(保留也可以)
	// m_str.Empty();
	// UpdateData(FALSE);
	MessageBox(L"数据已发送", L"提示", MB_OK);
}

子进程:


// SubDlg.h: 头文件
//

#pragma once


// CSubDlg 对话框
class CSubDlg : public CDialogEx
{
// 构造
public:
	CSubDlg(CWnd* pParent = nullptr);	// 标准构造函数

// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_SUB_DIALOG };
#endif

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV 支持


// 实现
protected:
	HICON m_hIcon;

	// 生成的消息映射函数
	virtual BOOL OnInitDialog();
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	DECLARE_MESSAGE_MAP()
public:
	HANDLE m_hRead;
	HANDLE m_hWrite;
	afx_msg void OnBnClickedBtnRead();
	CString m_str; // 绑定编辑框,用于显示接收的内容
};

// SubDlg.cpp: 实现文件
//

#include "pch.h"
#include "framework.h"
#include "Sub.h"
#include "SubDlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// CSubDlg 对话框



CSubDlg::CSubDlg(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_SUB_DIALOG, pParent)
	, m_str(_T(""))
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CSubDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Text(pDX, IDC_EDIT1, m_str);
}

BEGIN_MESSAGE_MAP(CSubDlg, CDialogEx)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_BTN_Read, &CSubDlg::OnBnClickedBtnRead)
END_MESSAGE_MAP()


// CSubDlg 消息处理程序

BOOL CSubDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// 设置此对话框的图标。  当应用程序主窗口不是对话框时,框架将自动
	//  执行此操作
	SetIcon(m_hIcon, TRUE);			// 设置大图标
	SetIcon(m_hIcon, FALSE);		// 设置小图标

	// TODO: 在此添加额外的初始化代码

	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

// 如果向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。  对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。

void CSubDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // 用于绘制的设备上下文

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// 使图标在工作区矩形中居中
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// 绘制图标
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialogEx::OnPaint();
	}
}

//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CSubDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}


void CSubDlg::OnBnClickedBtnRead()
{
	m_hRead = GetStdHandle(STD_INPUT_HANDLE); // 获取被父进程重定向的标准输入句柄(管道读端)
	m_hWrite = GetStdHandle(STD_OUTPUT_HANDLE);
	if (m_hRead == INVALID_HANDLE_VALUE || m_hRead == NULL)
	{
		MessageBox(L"无法获取标准输入句柄", L"错误", MB_ICONERROR);
		return;
	}

	// 读取数据(宽字符)
	wchar_t buffer[256] = { 0 };
	DWORD dwRead = 0;
	// 读取缓冲区大小(字节)
	// 读取缓冲区大小(字节)
	if (!ReadFile(m_hRead, buffer, sizeof(buffer) - sizeof(wchar_t), &dwRead, NULL))
	{
		DWORD err = GetLastError();
		if (err == ERROR_BROKEN_PIPE)
			MessageBox(L"管道已被父进程关闭", L"提示", MB_ICONWARNING);
		else
			MessageBox(L"读取管道失败", L"错误", MB_ICONERROR);
		return;
	}

	// 确保读到的数据以 null 结尾(ReadFile 不会自动添加)
	buffer[dwRead / sizeof(wchar_t)] = L'\0';

	// 显示到编辑框
	m_str = buffer;
	UpdateData(FALSE);   // 刷新界面

	// 可选:弹出提示框(非必须)
	// MessageBox(buffer, L"收到数据", MB_OK);
}

运行结果:

image

 

2、命名管道

与Socket相似,支持网络之间进程的通信。

命名管道(Named Pipe):
命名管道是具有唯一名称的管道,可以通过该名称在不同进程之间进行通信。
命名管道可以在本地机器上的不同进程之间进行通信,也可以在网络上的不同计算机之间进行通信。
命名管道支持全双工通信,可以同时进行读取和写入操作。
命名管道可以被多个进程打开和使用,允许多个读取者和写入者同时进行操作。
命名管道通常用于需要持久性连接和长期通信的场景,例如服务器和客户端之间的通信。

服务器代码:


// pipServerDlg.h: 头文件
//

#pragma once


// CpipServerDlg 对话框
class CpipServerDlg : public CDialogEx
{
// 构造
public:
	CpipServerDlg(CWnd* pParent = nullptr);	// 标准构造函数

// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_PIPSERVER_DIALOG };
#endif

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV 支持


// 实现
protected:
	HICON m_hIcon;
	HANDLE m_hPipe;

	// 生成的消息映射函数
	virtual BOOL OnInitDialog();
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	DECLARE_MESSAGE_MAP()
public:
	afx_msg void OnBnClickedBtnCreate();
	afx_msg void OnBnClickedBtnWrite();
	afx_msg void OnBnClickedBtnRead();
};

// pipServerDlg.cpp: 实现文件
//

#include "pch.h"
#include "framework.h"
#include "pipServer.h"
#include "pipServerDlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// CpipServerDlg 对话框



CpipServerDlg::CpipServerDlg(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_PIPSERVER_DIALOG, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CpipServerDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CpipServerDlg, CDialogEx)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_BTN_Create, &CpipServerDlg::OnBnClickedBtnCreate)
	ON_BN_CLICKED(IDC_Btn_Write, &CpipServerDlg::OnBnClickedBtnWrite)
	ON_BN_CLICKED(IDC_Btn_Read, &CpipServerDlg::OnBnClickedBtnRead)
END_MESSAGE_MAP()


// CpipServerDlg 消息处理程序

BOOL CpipServerDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// 设置此对话框的图标。  当应用程序主窗口不是对话框时,框架将自动
	//  执行此操作
	SetIcon(m_hIcon, TRUE);			// 设置大图标
	SetIcon(m_hIcon, FALSE);		// 设置小图标

	// TODO: 在此添加额外的初始化代码

	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

// 如果向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。  对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。

void CpipServerDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // 用于绘制的设备上下文

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// 使图标在工作区矩形中居中
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// 绘制图标
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialogEx::OnPaint();
	}
}

//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CpipServerDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}



void CpipServerDlg::OnBnClickedBtnCreate()
{
	//第一个参数:名字,名字格式:\\.\pipe\pipename
//第二个参数:打开模式 PIPE_ACCESS_DUPLEX双工模式,FILE_FLAG_OVERLAPPED 在读写过程中会发送一个信号
//第三个参数:管道模式  PIPE_TYPE_MESSAGE 消息的方式
//第四个参数:最大实例个数
//第五个参数:输出的缓冲大小
//第六个参数:输入的缓冲区大小
//第七个参数:默认的超时时间
//第八个参数:安全描述
	m_hPipe = CreateNamedPipe(L"\\\\.\\pipe\\test", PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_MESSAGE, 1, 1024, 1024, 0, NULL);

	if (m_hPipe == INVALID_HANDLE_VALUE)
	{
		MessageBox(L"创建命名管道失败");
		return;
	}

	//创建匿名事件对象
	HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);


	//异步 I/O 操作的参数和状态信息
	OVERLAPPED ol = { 0 };
	ol.hEvent = hEvent;

	//等待客户端的链接
	//ConnectNamedPipe 等待客户端有没有连接管道
	if (FALSE == ConnectNamedPipe(m_hPipe, &ol))
	{
		if (GetLastError() != ERROR_IO_PENDING)
		{
			MessageBox(L"等待客户端链接失败");
			return;
		}
	}

	//判断有没有信号
	if (WaitForSingleObject(hEvent, INFINITE) == WAIT_FAILED)
	{
		MessageBox(L"等待对象失败");
		CloseHandle(m_hPipe);
		CloseHandle(hEvent);
		m_hPipe = NULL;
		return;
	}
	CloseHandle(hEvent);
}


void CpipServerDlg::OnBnClickedBtnWrite()
{
	wchar_t szBuff[100] = L"12345678";
	DWORD  dwWrite = 0;
	if (FALSE == WriteFile(m_hPipe, szBuff, wcslen(szBuff) * 2 + 1, &dwWrite, NULL))
	{
		MessageBox(L"写入数据失败", L"错误", MB_ICONERROR);
		return;
	}
}


void CpipServerDlg::OnBnClickedBtnRead()
{
	wchar_t szBuff[100];
	DWORD  dwRead = 0;

	if (FALSE == ReadFile(m_hPipe, szBuff, sizeof(szBuff), &dwRead, NULL))
	{
		MessageBox(L"读取数据失败", L"错误", MB_ICONERROR);
		return;
	}
	MessageBox(szBuff);
}

客户端代码:


// pipClientDlg.h: 头文件
//

#pragma once


// CpipClientDlg 对话框
class CpipClientDlg : public CDialogEx
{
// 构造
public:
	CpipClientDlg(CWnd* pParent = nullptr);	// 标准构造函数

// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_PIPCLIENT_DIALOG };
#endif

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV 支持


// 实现
protected:
	HICON m_hIcon;
	HANDLE m_hPipe;

	// 生成的消息映射函数
	virtual BOOL OnInitDialog();
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	DECLARE_MESSAGE_MAP()
public:
	afx_msg void OnBnClickedBtnConnect();
	afx_msg void OnBnClickedBtnRead();
	afx_msg void OnBnClickedBtnWrite();
};

// pipClientDlg.cpp: 实现文件
//

#include "pch.h"
#include "framework.h"
#include "pipClient.h"
#include "pipClientDlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// CpipClientDlg 对话框



CpipClientDlg::CpipClientDlg(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_PIPCLIENT_DIALOG, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CpipClientDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CpipClientDlg, CDialogEx)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_Btn_Connect, &CpipClientDlg::OnBnClickedBtnConnect)
	ON_BN_CLICKED(IDC_Btn_Read, &CpipClientDlg::OnBnClickedBtnRead)
	ON_BN_CLICKED(IDC_Btn_Write, &CpipClientDlg::OnBnClickedBtnWrite)
END_MESSAGE_MAP()


// CpipClientDlg 消息处理程序

BOOL CpipClientDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// 设置此对话框的图标。  当应用程序主窗口不是对话框时,框架将自动
	//  执行此操作
	SetIcon(m_hIcon, TRUE);			// 设置大图标
	SetIcon(m_hIcon, FALSE);		// 设置小图标

	// TODO: 在此添加额外的初始化代码

	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

// 如果向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。  对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。

void CpipClientDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // 用于绘制的设备上下文

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// 使图标在工作区矩形中居中
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// 绘制图标
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialogEx::OnPaint();
	}
}

//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CpipClientDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}


void CpipClientDlg::OnBnClickedBtnConnect()
{
	//判断有没有可以用的管道
	if (FALSE == WaitNamedPipe(L"\\\\.\\pipe\\test", NMPWAIT_WAIT_FOREVER))
	{
		MessageBox(L"当前没有可以利用的命名管道");
		return;
	}

	//打开管道
	//第一个参数:管道名
	//第二个参数:读写模式
	//第三个参数:共享模式 0表示不共享
	//第四个参数:安全属性
	//第五个参数:创建方式
	//第六个参数:文件属性
	//第七个参数:提供文件的拓展属性
	m_hPipe = CreateFile(L"\\\\.\\pipe\\test", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (m_hPipe == INVALID_HANDLE_VALUE)
	{
		MessageBox(L"打开管道失败");
		m_hPipe = NULL;
		return;
	}
}


void CpipClientDlg::OnBnClickedBtnRead()
{
	wchar_t szBuff[100];
	DWORD  dwRead = 0;

	if (FALSE == ReadFile(m_hPipe, szBuff, sizeof(szBuff), &dwRead, NULL))
	{
		MessageBox(L"读取数据失败", L"错误", MB_ICONERROR);
		return;
	}
	MessageBox(szBuff);
}


void CpipClientDlg::OnBnClickedBtnWrite()
{
	wchar_t szBuff[100] = L"12345678";
	DWORD  dwWrite = 0;
	if (FALSE == WriteFile(m_hPipe, szBuff, wcslen(szBuff) * 2 + 1, &dwWrite, NULL))
	{
		MessageBox(L"写入数据失败", L"错误", MB_ICONERROR);
		return;
	}
}

服务器命名管道的创建

//命名管道
void CServerDlg::OnBnClickedBtnCreate()
{
	//第一个参数:名字,名字格式:\\.\pipe\pipename
	//第二个参数:打开模式 PIPE_ACCESS_DUPLEX双工模式,FILE_FLAG_OVERLAPPED 在读写过程中会发送一个信号
	//第三个参数:管道模式  PIPE_TYPE_MESSAGE 消息的方式
	//第四个参数:最大实例个数
	//第五个参数:输出的缓冲大小
	//第六个参数:输入的缓冲区大小
	//第七个参数:默认的超时时间
	//第八个参数:安全描述
	m_hPipe=CreateNamedPipe(L"\\\\.\\pipe\\test", PIPE_ACCESS_DUPLEX| FILE_FLAG_OVERLAPPED, PIPE_TYPE_MESSAGE,1,1024,1024,0,NULL);

	if (m_hPipe == INVALID_HANDLE_VALUE)
	{
		MessageBox(L"创建命名管道失败");
		return;
	}

	//创建匿名事件对象
	HANDLE hEvent=CreateEvent(NULL, TRUE, FALSE,NULL);


	//异步 I/O 操作的参数和状态信息
	OVERLAPPED ol = {0};
	ol.hEvent = hEvent;

	//等待客户端的链接
	//ConnectNamedPipe 等待客户端有没有连接管道
	if (FALSE == ConnectNamedPipe(m_hPipe, &ol))
	{
		if (GetLastError() != ERROR_IO_PENDING)
		{
			MessageBox(L"等待客户端链接失败");
			return;
		}
	}

	//判断有没有信号
	if (WaitForSingleObject(hEvent, INFINITE) == WAIT_FAILED)
	{
		MessageBox(L"等待对象失败");
		CloseHandle(m_hPipe);
		CloseHandle(hEvent);
		m_hPipe = NULL;
		return;
	}
	CloseHandle(hEvent);
}

客户端管道的连接

void CClientDlg::OnBnClickedBtnConnect()
{
	//判断有没有可以用的管道
	if (FALSE == WaitNamedPipe(L"\\\\.\\pipe\\test", NMPWAIT_WAIT_FOREVER))
	{
		MessageBox(L"当前没有可以利用的命名管道");
		return;
	}

	//打开管道
	//第一个参数:管道名
	//第二个参数:读写模式
	//第三个参数:共享模式 0表示不共享
	//第四个参数:安全属性
	//第五个参数:创建方式
	//第六个参数:文件属性
	//第七个参数:提供文件的拓展属性
	CClientDlg::m_hPipe=CreateFile(L"\\\\.\\pipe\\test", GENERIC_READ | GENERIC_WRITE, 0,NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,NULL);
	if (m_hPipe == INVALID_HANDLE_VALUE)
	{
		MessageBox(L"打开管道失败");
		m_hPipe = NULL;
		return;
	}
}

 

运行结果:

image

posted @ 2026-06-09 22:40  [BORUTO]  阅读(13)  评论(0)    收藏  举报