c - 开放管道上 CloseHandle 上的 sigsegv

标签 c winapi

我在 CloseHandle(pipe_settings->hPipe) 上收到一个 sigsegv;我在这个例子中也使用了线程,我认为这与任何事情都没有关系,因为管道是在同一个线程中打开和关闭的。

管道句柄似乎也有效。类似于:0xcc。 事件句柄也正确关闭。

这是一个完全有效的示例,您应该能够将其放入 CLion 中并开始使用。此代码在任何 Windows 安装上都应该是安全的。

#include <stdio.h>
#include <windows.h>
#include <unistd.h>

#define UNICODE

#define PIPE_BUFFER_SIZE 4096
#define PIPE_TIMEOUT 10000
#define PIPE_MAX_CONNECT_TIMEOUT 10000
#define PIPE_CONNECT_FAIL_REPEAT_DELAY 1000
#define DUST_TEST_PIPE_NAME "\\\\.\\pipe\\DustTestPipe"
#define PIPE_TEST_TIMEOUT 100000

struct pipe_settings {
    HANDLE hPipe;
    OVERLAPPED ol;
    LPCTSTR name;
    DWORD pipe_timeout;
};

WINBOOL PipeClose(struct pipe_settings *pipe_settings)
{
    //Dies on CloseHandle(pipe_settings->hPipe) but not on CloseHandle(pipe_settings->ol.hEvent)
    return (CloseHandle(pipe_settings->ol.hEvent) && CloseHandle(pipe_settings->hPipe));
}

void InitializePipeSettings(struct pipe_settings *pipe_settings)
{
    pipe_settings->hPipe = INVALID_HANDLE_VALUE;
    pipe_settings->ol.hEvent = INVALID_HANDLE_VALUE;
    pipe_settings->ol.Internal = 0;
    pipe_settings->ol.InternalHigh = 0;
    pipe_settings->ol.Offset = 0;
    pipe_settings->ol.OffsetHigh = 0;
    pipe_settings->ol.Pointer = NULL;
}

DWORD PipeConnectToClient(struct pipe_settings *pipe_settings)
{
    HANDLE h;
    DWORD err;

    InitializePipeSettings(pipe_settings);

    for(int x = 0; x < PIPE_MAX_CONNECT_TIMEOUT; x += PIPE_CONNECT_FAIL_REPEAT_DELAY)
    {

        h = CreateFile(
                pipe_settings->name,
                GENERIC_WRITE,
                FILE_SHARE_READ | FILE_SHARE_WRITE,
                NULL,
                OPEN_EXISTING,
                FILE_FLAG_OVERLAPPED,
                NULL
        );

        err = GetLastError();

        if (h != INVALID_HANDLE_VALUE)
            break;

        if(err == ERROR_FILE_NOT_FOUND) {
            //The pipe might not be open yet
            sleep(PIPE_CONNECT_FAIL_REPEAT_DELAY/1000);
            continue;
        }

        else if (err != ERROR_PIPE_BUSY) {
            return err;
        }

        if (!WaitNamedPipe(pipe_settings->name, (DWORD)PIPE_MAX_CONNECT_TIMEOUT - x))
        {
            return ERROR_PIPE_BUSY;
        }
    }

    if(h == INVALID_HANDLE_VALUE)
        return err;

    pipe_settings->hPipe = h;

    DWORD dwMode = PIPE_READMODE_BYTE;

    if(!SetNamedPipeHandleState(h, &dwMode, NULL, NULL))
        return GetLastError();

    return ERROR_SUCCESS;
}

DWORD PipeListenForServerConnect(struct pipe_settings *pipe_settings)
{
    InitializePipeSettings(pipe_settings);

    HANDLE hPipe = CreateNamedPipe(
            pipe_settings->name,
            PIPE_ACCESS_INBOUND |    // read/write access
            FILE_FLAG_OVERLAPPED,    // overlapped mode
            PIPE_TYPE_BYTE |         // message-type pipe
            PIPE_READMODE_BYTE |     // message-read mode
            PIPE_WAIT,
            1,
            PIPE_BUFFER_SIZE,
            PIPE_BUFFER_SIZE,
            PIPE_TIMEOUT,
            NULL
    );

    if (hPipe == INVALID_HANDLE_VALUE)
    {
        return GetLastError();
    }

    OVERLAPPED ol;
    //DWORD dwWait;

    ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

    if(ol.hEvent == NULL)
    {
        return GetLastError();
    }

    if(ConnectNamedPipe(hPipe, &ol) == 0)
    {
        //error should be ERROR_IO_PENDING or ERROR_PIPE_CONNECTED
        DWORD err = GetLastError();

        //wait for the connection event
        if (err != ERROR_IO_PENDING && err != ERROR_PIPE_CONNECTED) {
            if (WaitForSingleObject(ol.hEvent, pipe_settings->pipe_timeout) != WAIT_OBJECT_0) {
                CloseHandle(hPipe);
                CloseHandle(ol.hEvent);
                return err;
            }
        }
    }

    pipe_settings->hPipe = hPipe;
    pipe_settings->ol.Pointer = ol.Pointer;
    pipe_settings->ol.Offset = ol.Offset;
    pipe_settings->ol.Internal = ol.Internal;
    pipe_settings->ol.hEvent = ol.hEvent;
    pipe_settings->ol.InternalHigh = ol.InternalHigh;
    pipe_settings->ol.OffsetHigh = ol.OffsetHigh;

    return ERROR_SUCCESS;
}

DWORD WINAPI ClientProc(LPVOID)
{
    struct pipe_settings pipe_settings;
    pipe_settings.name = TEXT(DUST_TEST_PIPE_NAME);
    pipe_settings.pipe_timeout = PIPE_TEST_TIMEOUT;

    DWORD err = PipeListenForServerConnect(&pipe_settings);
    //EXPECT_EQ((DWORD)ERROR_SUCCESS, err);

    WINBOOL pipe_close_success = PipeClose(&pipe_settings);
    //EXPECT_NE(1, pipe_close_success);

    return 0;
}

DWORD WINAPI ServerProc(LPVOID)
{
    struct pipe_settings pipe_settings;
    pipe_settings.name = TEXT(DUST_TEST_PIPE_NAME);
    pipe_settings.pipe_timeout = PIPE_TEST_TIMEOUT;

    DWORD err = PipeConnectToClient(&pipe_settings);
    //EXPECT_EQ((DWORD)ERROR_SUCCESS, err);

    WINBOOL pipe_close_success = PipeClose(&pipe_settings);
    //EXPECT_NE(0, pipe_close_success);

    return 0;
}



int main(void) {

    DWORD dwThreadId1;
    DWORD dwThreadId2;

    HANDLE h1 = CreateThread(NULL, 0, ServerProc, NULL, 0, &dwThreadId1);
    HANDLE h2 = CreateThread(NULL, 0, ClientProc, NULL, 0, &dwThreadId2);

    WaitForSingleObject(h1, INFINITE);
    WaitForSingleObject(h2, INFINITE);
    // your code goes here
    return 0;
}

最佳答案

CreateEvent() 不使用 INVALID_HANDLE_VALUE 来表示无效句柄,而是使用 NULL 来代替。因此,您需要将 InitializePipeSettings() 初始化 ol.hEventNULL:

void InitializePipeSettings(struct pipe_settings *pipe_settings)
{
    pipe_settings->hPipe = INVALID_HANDLE_VALUE;
    pipe_settings->ol.hEvent = NULL; // <-- here
    pipe_settings->ol.Internal = 0;
    pipe_settings->ol.InternalHigh = 0;
    pipe_settings->ol.Offset = 0;
    pipe_settings->ol.OffsetHigh = 0;
    pipe_settings->ol.Pointer = NULL;
}

然后在 PipeClose() 中,您需要在调用 CloseHandle()< 之前检查每个句柄是否有 INVALID_HANDLE_VALUE/NULL/ 就可以了。您还应该让 PipeClose() 重置其关闭的句柄,以便可以在同一个 pipe_settings 上安全地多次调用 PipeClose() >:

WINBOOL PipeClose(struct pipe_settings *pipe_settings)
{
    BOOL bResult = TRUE;
    if (pipe_settings->ol.hEvent != NULL)
    {
        bResult = bResult && CloseHandle(pipe_settings->ol.hEvent);
        pipe_settings->ol.hEvent = NULL;
    }
    if (pipe_settings->hPipe != INVALID_HANDLE_VALUE)
    {
        bResult = bResult && CloseHandle(pipe_settings->hPipe);
        pipe_settings->hPipe = INVALID_HANDLE_VALUE;
    }
    return bResult;
}

关于c - 开放管道上 CloseHandle 上的 sigsegv,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32189856/

相关文章:

c++ - 进程内存映射(Linux Windows)

c++ - MDI 子窗口大小

powershell - 如何以编程方式设置 Windows 10 移动热点?

c++ - 翻译后顶点缓冲区中的数据会改变吗?

c - Xcode中编译时全局变量错误(C编程)

c - 如何交换(或交换)数组中的两个数字?

c - 从源代码编译 glibc 时出错

c - 为什么我的循环没有在我预期的时候退出?

c++ - Win32 : More "object oriented" window message handling system

c++ - 复制托管 Silverlight 控件的内容