命名管道(Win32, C++)

命名管道简单封装

 

CNamedPipe.h

#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 = INVALID_HANDLE_VALUE;
    HANDLE m_hReadEvent = NULL;
    HANDLE m_hWriteEvent = NULL;
    LPVOID m_pBuffer = nullptr;
    bool m_bInit = false;
    bool m_bConnected = false;
};

CNamedPipe.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_pBuffer(nullptr),
    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

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

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

                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;
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值