Win32 命名管道

命名管道简单封装

CNamedPipe.h

cpp 复制代码
#pragma once
#include <string>
#include <windows.h>
#include <tchar.h>

#pragma warning(disable:4200)

class CNamedPipe
{
public:
	CNamedPipe();

	~CNamedPipe();

    CNamedPipe(const CNamedPipe& r) = delete;
    CNamedPipe& operator = (const CNamedPipe& r) = delete;

    //
    // @brief: 创建命名管道
    // @param: lpName           管道名
    // @ret: bool               true: 创建成功 false: 创建失败
	bool Create(LPCTSTR lpName);

    //
    // @brief: 等待客户端连接命名管道
    // @param: nTimeOut         超时等待(毫秒)
    // @ret: bool               true: 连接成功 false: 连接失败
	bool WaitConnect(DWORD nTimeOut = INFINITE);

    //
    // @brief: 关闭由Create 创建的管道
    // @param: void
    // @ret: bool               true: 关闭 成功 false: 关闭 失败
    bool Disconnect();

    //
    // @brief: 打开已存在的命名管道
    // @param: lpName           管道名
    // @ret: bool               true: 打开成功 false: 打开失败
	bool Open(LPCTSTR lpName, DWORD nTimeOut = INFINITE);

    //
    // @brief: 管道是否有效
    // @param: void
    // @ret: bool               true: 可用 false: 无效
    bool IsValid();

    //
    // @brief: 关闭管道
    // @param: void
    // @ret: void
	void Close(void);

    //
    // @brief: 从读取管道数据
    // @param: lpData           数据存放缓冲
    // @param: nSize            缓冲大小(字节)
    // @param: lpBytesRead      指向实际读取大小(字节)的指针
    // @param: nTimeOut         读取超时(毫秒)
    // @ret: bool               true: 读取成功 false: 读取失败
	bool Read(LPVOID lpData, DWORD nSize, LPDWORD lpBytesRead = nullptr, DWORD nTimeOut = INFINITE);

    //
    // @brief: 向管道写入数据
    // @param: lpData           写入数据指针
    // @param: nSize            写入数据大小(字节)
    // @param: lpBytesWritten   指向实际写入大小(字节)的指针
    // @param: nTimeOut         写入超时(毫秒)
    // @ret: bool               true: 写入成功 false: 写入失败
	bool Write(LPCVOID lpData, DWORD nSize, LPDWORD lpBytesWritten = nullptr, DWORD nTimeOut = INFINITE);

private:

    //
    // @brief: 初始化对象占用
    // @param: void
    // @ret: void
    bool Initialize();

    //
    // @brief: 释放对象占用
    // @param: void
    // @ret: void
    void Uninitialize();

private:
	HANDLE m_hNamedPipe;
	HANDLE m_hReadEvent;
	HANDLE m_hWriteEvent;
    LPVOID m_pBuffer;
    bool m_bInit;
	bool m_bConnected;
};

CNamedPipe.cpp

cpp 复制代码
#include "CNamedPipe.h"
#include <StrSafe.h>
#include <tchar.h>

#define PIPE_NAME_PREFIX             TEXT(R"(\\.\pipe\)")    //管道前缀名
#define PIPE_MAX_TIMEOUT             (3000)                  //管道打开超时
#define PIPE_BUF_MAX_SIZE            (1024 * 1024)           //管道发送缓冲大小(字节)
#define PIPE_MAX_CONNECT             (16)                    //IPC最大连接数

typedef struct _PIPE_DATA
{
    DWORD dwSize = 0;
    BYTE data[0];
}PIPE_DATA, * PPIPE_DATA;

CNamedPipe::CNamedPipe():
    m_hNamedPipe(INVALID_HANDLE_VALUE),
    m_hReadEvent(NULL),
    m_hWriteEvent(NULL),
    m_bConnected(false),
    m_bInit(false)
{
    //初始化读写缓冲与事件句柄
    Initialize();
}

CNamedPipe::~CNamedPipe()
{
    //释放读写缓冲与事件句柄
    Uninitialize();
}

bool CNamedPipe::Create(LPCTSTR lpName)
{
    TCHAR szPipeName[MAX_PATH];
    SECURITY_ATTRIBUTES sa = { 0 };
    SECURITY_DESCRIPTOR sd = { 0 };
    bool isSuccess = false;

    sa.nLength = sizeof(sa);
    sa.bInheritHandle = FALSE;
    sa.lpSecurityDescriptor = &sd;

    if (INVALID_HANDLE_VALUE != m_hNamedPipe)
    {
        return true;
    }

    //设置权限, 防止低权限进程不能打开高权限进程创建的管道
    (void)::InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
    (void)::SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE);
    (void)::StringCchPrintf(szPipeName, _countof(szPipeName), TEXT("%s%s"), PIPE_NAME_PREFIX, lpName);

    do
    {
        m_hNamedPipe = ::CreateNamedPipe(
            szPipeName,
            PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
            PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
            PIPE_MAX_CONNECT,
            PIPE_BUF_MAX_SIZE,
            PIPE_BUF_MAX_SIZE,
            PIPE_MAX_TIMEOUT,
            &sa
        );

        if (INVALID_HANDLE_VALUE == m_hNamedPipe)
        {
            break;
        }

        isSuccess = true;

    } while (false);

    if (!isSuccess)
    {
        this->Close();
    }

    return isSuccess;
}

bool CNamedPipe::Open(LPCTSTR lpName, DWORD nTimeOut/* = INFINITE*/)
{
    TCHAR szPipeName[MAX_PATH] = { 0 };
    bool isSuccess = false;

    (void)::StringCchPrintf(szPipeName, _countof(szPipeName), TEXT("%s%s"), PIPE_NAME_PREFIX, lpName);

    if (INVALID_HANDLE_VALUE != m_hNamedPipe)
    {
        return true;
    }

    ULONGLONG ullCurTick = ::GetTickCount64();

    do
    {
        m_hNamedPipe = ::CreateFile(
            szPipeName,
            GENERIC_READ | GENERIC_WRITE,
            0,
            NULL,
            OPEN_EXISTING,
            FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
            NULL
        );

        //管道句柄有效则终止循环
        if (INVALID_HANDLE_VALUE != m_hNamedPipe)
        {
            isSuccess = true;
            break;
        }

        //若错误原因不是因为所有管道范例都在使用中, 则退出循环
        if (ERROR_PIPE_BUSY != ::GetLastError())
        {
            break;
        }

        //等待命名管道的实例可用于连接
        if (::WaitNamedPipe(szPipeName, 1000))
        {
            continue;
        }

        //无限等待则不需要检查超时
        if (INFINITE == nTimeOut)
        {
            continue;
        }

        //执行操作超时则退出循环
        if (::GetTickCount64() - ullCurTick > nTimeOut)
        {
            break;
        }

    } while (INVALID_HANDLE_VALUE == m_hNamedPipe);

    if (!isSuccess)
    {
        this->Close();
    }

    return isSuccess;
}

bool CNamedPipe::WaitConnect(DWORD nTimeOut)
{
    OVERLAPPED Overlapped = { 0 };
    bool isConnected = false;

    if (INVALID_HANDLE_VALUE == m_hNamedPipe)
    {
        return false;
    }

    Overlapped.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
    if (NULL == Overlapped.hEvent)
    {
        return false;
    }

    isConnected = ::ConnectNamedPipe(m_hNamedPipe, &Overlapped);

    if (!isConnected)
    {
        DWORD dwError = ::GetLastError();

        //管道关闭中
        if (ERROR_NO_DATA == dwError)
        {
            isConnected = false;
        }

        //操作处于挂起状态
        if (ERROR_IO_PENDING == dwError)
        {
            if (WAIT_OBJECT_0 == ::WaitForSingleObject(Overlapped.hEvent, nTimeOut))
            {
                isConnected = true;
            }
        }

        //管道已经连接
        if (ERROR_PIPE_CONNECTED == dwError)
        {
            isConnected = true;
        }
    }

    if (NULL != Overlapped.hEvent)
    {
        ::CloseHandle(Overlapped.hEvent);
    }

    m_bConnected = isConnected;

    return isConnected;
}

bool CNamedPipe::Disconnect()
{
    if (INVALID_HANDLE_VALUE == m_hNamedPipe)
    {
        return false;
    }

    //参数句柄必须由 CreateNamedPipe 函数创建
    return ::DisconnectNamedPipe(m_hNamedPipe);
}

void CNamedPipe::Close()
{
    if (INVALID_HANDLE_VALUE != m_hNamedPipe)
    {
        if (m_bConnected)
        {
            ::FlushFileBuffers(m_hNamedPipe);
            ::DisconnectNamedPipe(m_hNamedPipe);
            m_bConnected = false;
        }
        ::CloseHandle(m_hNamedPipe);
        m_hNamedPipe = INVALID_HANDLE_VALUE;
    }
}

bool CNamedPipe::IsValid()
{
    return INVALID_HANDLE_VALUE != m_hNamedPipe;
}

bool CNamedPipe::Read(LPVOID lpData, DWORD nSize, LPDWORD lpBytesRead/* = nullptr*/, DWORD nTimeOut)
{
    OVERLAPPED Overlapped = { 0 };
    Overlapped.hEvent = m_hReadEvent;
    DWORD dwBytesTransferred = 0;
    bool isSuccess = false;

    if (nullptr == m_pBuffer ||
        nullptr == lpData ||
        0 == nSize ||
        nSize > PIPE_BUF_MAX_SIZE
        )
    {
        return false;
    }

    PPIPE_DATA pData = (PPIPE_DATA)m_pBuffer;
    if (!::ReadFile(m_hNamedPipe, &pData->dwSize, sizeof(PIPE_DATA), NULL, &Overlapped))
    {
        //管道已结束
        if (ERROR_BROKEN_PIPE == ::GetLastError())
        {
            return false;
        }

        if (ERROR_IO_PENDING != ::GetLastError())
        {
            return false;
        }

        if (WAIT_OBJECT_0 != ::WaitForSingleObject(Overlapped.hEvent, nTimeOut))
        {
            return false;
        }
    }

    if (pData->dwSize > PIPE_BUF_MAX_SIZE)
    {
        return false;
    }

    if (!::ReadFile(m_hNamedPipe, pData->data, pData->dwSize, NULL, &Overlapped))
    {
        if (ERROR_IO_PENDING != ::GetLastError())
        {
            return false;
        }

        if (WAIT_OBJECT_0 != ::WaitForSingleObject(Overlapped.hEvent, nTimeOut))
        {
            return false;
        }
    }

    if (::GetOverlappedResult(m_hNamedPipe, &Overlapped, &dwBytesTransferred, true))
    {
        isSuccess = true;
        if (lpBytesRead)
        {
            *lpBytesRead = dwBytesTransferred;
        }
    }

    if (isSuccess)
    {
        if (nSize < pData->dwSize)
        {
            ::memcpy_s(lpData, nSize, pData->data, nSize);
        }
        else
        {
            ::memcpy_s(lpData, nSize, pData->data, pData->dwSize);
        }
    }

    return isSuccess;
}

bool CNamedPipe::Write(LPCVOID lpData, DWORD nSize, LPDWORD lpBytesWritten/* = nullptr*/, DWORD nTimeOut)
{
    OVERLAPPED Overlapped = { 0 };
    Overlapped.hEvent = m_hWriteEvent;
    DWORD dwBytesTransferred = 0;
    bool isSuccess = false;

    if (nullptr == m_pBuffer ||
        nullptr == lpData ||
        0 == nSize ||
        nSize > PIPE_BUF_MAX_SIZE
        )
    {
        return false;
    }

    PPIPE_DATA pData = (PPIPE_DATA)m_pBuffer;
    DWORD dwBytesToWrite = nSize + sizeof(PIPE_DATA);
    pData->dwSize = nSize;
    ::memcpy_s(pData->data, PIPE_BUF_MAX_SIZE, lpData, nSize);

    if (::WriteFile(m_hNamedPipe, pData, dwBytesToWrite, NULL, &Overlapped))
    {
        return true;
    }

    //管道正在被关闭
    if (ERROR_NO_DATA == ::GetLastError())
    {
        return false;
    }

    //管道已结束
    if (ERROR_BROKEN_PIPE == ::GetLastError())
    {
        return false;
    }

    //重叠
    if (ERROR_IO_PENDING != ::GetLastError())
    {
        return false;
    }

    if (WAIT_OBJECT_0 != ::WaitForSingleObject(Overlapped.hEvent, nTimeOut))
    {
        return false;
    }

    if (::GetOverlappedResult(m_hNamedPipe, &Overlapped, &dwBytesTransferred, true))
    {
        isSuccess = true;
        if (lpBytesWritten)
        {
            *lpBytesWritten = dwBytesTransferred;
        }
    }

    return isSuccess;
}

bool CNamedPipe::Initialize()
{
    bool isSuccess = false;

    if (m_bInit)
    {
        return true;
    }

    do
    {
        if (nullptr == m_pBuffer)
        {
            m_pBuffer = ::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, PIPE_BUF_MAX_SIZE + sizeof(PIPE_DATA));
        }

        if (nullptr == m_pBuffer)
        {
            break;
        }

        if (NULL == m_hReadEvent)
        {
            m_hReadEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
        }

        if (NULL == m_hReadEvent)
        {
            break;
        }

        if (NULL == m_hWriteEvent)
        {
            m_hWriteEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
        }

        if (NULL == m_hWriteEvent)
        {
            break;
        }

        isSuccess = true;

    } while (false);

    if (!isSuccess)
    {
        Uninitialize();
    }

    m_bInit = isSuccess;

    return m_bInit;
}

void CNamedPipe::Uninitialize()
{
    if (!m_bInit)
    {
        return;
    }

    //关闭管道
    this->Close();

    //释放读写缓冲
    if (nullptr != m_pBuffer)
    {
        ::HeapFree(::GetProcessHeap(), 0, m_pBuffer);
        m_pBuffer = nullptr;
    }

    //关闭事件
    if (m_hReadEvent)
    {
        CloseHandle(m_hReadEvent);
        m_hReadEvent = NULL;
    }

    if (m_hWriteEvent)
    {
        CloseHandle(m_hWriteEvent);
        m_hWriteEvent = NULL;
    }

    m_bInit = false;
}

main.cpp

cpp 复制代码
#include <iostream>
#include "Utils/CNamedPipe.h"
#include <thread>
#include <future>

int main()
{
    std::promise<bool> p;
    std::future<bool> f = p.get_future();

    std::thread(
        [&p](
            ) {
                static char szBuf[1024 * 64] = { 0 };
                CNamedPipe pipe;
                if (!pipe.Create(_T("FlameCyclone")))
                {
                    std::cout << "Create failed!" << std::endl;
                    p.set_value(false);
                    return;
                }

                p.set_value(true);

                if (!pipe.WaitConnect())
                {
                    std::cout << "WaitConnect failed!" << std::endl;
                    return;
                }

                while (true)
                {
                    bool isSuccess = pipe.Read(szBuf, sizeof(szBuf));
                    if (isSuccess)
                    {
                        std::cout << "recv: " << szBuf << std::endl;
                    }

                    //另一端断开则重新等待连接
                    if (ERROR_BROKEN_PIPE == ::GetLastError())
                    {
                        pipe.Disconnect();
                        if (!pipe.WaitConnect())
                        {
                            std::cout << "WaitConnect failed!" << std::endl;
                            return;
                        }
                    }
                }

        }
    ).detach();

    if (!f.get())
    {
        return -1;
    }

    CNamedPipe pipe;
    if (!pipe.Open(_T("FlameCyclone"), 5000))
    {
        std::cout << "Open failed!" << std::endl;
        return -1;
    }

    std::string strMsg;

    while (true)
    {
        std::cin >> strMsg;
        std::cout << "send: " << strMsg << std::endl;

        if (!pipe.Write(strMsg.c_str(), strMsg.size() + 1))
        {
            std::cout << "Write failed!" << std::endl;
            break;
        }
    }

    system("pause");
    return 0;
}
相关推荐
南东山人3 小时前
一文说清:C和C++混合编程
c语言·c++
n***85945 小时前
嵌入式 UI 开发的开源项目推荐
windows·开源·开源软件
Ysjt | 深5 小时前
C++多线程编程入门教程(优质版)
java·开发语言·jvm·c++
ephemerals__5 小时前
【c++丨STL】list模拟实现(附源码)
开发语言·c++·list
Microsoft Word6 小时前
c++基础语法
开发语言·c++·算法
小袁搬码6 小时前
Windows中指定路径安装DockerDesktop
windows·docker·容器·docker desktop
一只小小汤圆6 小时前
opencascade源码学习之BRepOffsetAPI包 -BRepOffsetAPI_DraftAngle
c++·学习·opencascade
legend_jz6 小时前
【Linux】线程控制
linux·服务器·开发语言·c++·笔记·学习·学习方法
嘿BRE7 小时前
【C++】几个基本容器的模拟实现(string,vector,list,stack,queue,priority_queue)
c++