windows 为管道客户端创建文件失败,错误为231 ERROR_PIPE_忙碌

svgewumm  于 2022-12-19  发布在  Windows
关注(0)|答案(1)|浏览(412)

bounty将在2小时后过期。回答此问题可获得+50声望奖励。IOviSpot希望引起更多人关注此问题。

我正在尝试让两个DLL互相通信。一个用作管道服务器,只要程序运行就保持活动状态,另一个用作管道客户端,在管道服务器处于活动状态时,可以在给定的情况下连接和断开连接。请考虑以下函数:

DLL1(管道服务器)

void DLL1_Begin() // This gets called only once, when the program starts
{
    g_hPipeServer = CreateNamedPipe(TEXT("\\\\.\\pipe\\MyPipe123"),
            PIPE_ACCESS_DUPLEX,
            PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_NOWAIT,
            1,
            4,
            4,
            NMPWAIT_USE_DEFAULT_WAIT,
            NULL);

    if (g_hPipeServer == INVALID_HANDLE_VALUE)
    {
        char error[256];
        sprintf_s(error, "Server pipe error %lu", GetLastError());
        MessageBox(NULL, error, "Fatal error", MB_OK | MB_ICONERROR);
        exit(1);
    }

    ConnectNamedPipe(g_hPipeServer, NULL);
}

void DLL1_End() // This gets called only once, when the program closes
{
    DisconnectNamedPipe(g_hPipeServer);
    CloseHandle(g_hPipeServer);
}

DLL2(管道客户端)

// Like DLL1, DLL2 gets initialized once and is always attached to the program.
// These functions are called occasionally and are meant solely for the Pipe.

void DLL2_InitPipeClient()
{
    g_hClientPipe = CreateFile(TEXT("\\\\.\\pipe\\MyPipe123"),
        GENERIC_READ | GENERIC_WRITE,
        0,
        NULL,
        OPEN_EXISTING,
        0,
        NULL);

    if (g_hClientPipe == INVALID_HANDLE_VALUE) // This scope is of interest
    {
        char error[256];
        sprintf_s(error, "Client pipe error %lu", GetLastError());
        MessageBox(NULL, error, "Fatal error", MB_OK | MB_ICONERROR);
        exit(1);
    }
}

void DLL2_EndPipeClient()
{
    CloseHandle(g_hClientPipe);
    g_hClientPipe = NULL;
}

具体步骤如下:
1.程序启动,连接DLL1并调用DLL1_Begin
1.在任何时候,用户与按钮交互,DLL2被连接,DLL2_InitPipeClient被调用。只要程序在运行,DLL2就保持连接到程序
1.在任何时候,用户与另一个按钮交互,调用DLL2_EndPipeClient,然后立即调用DLL2_InitPipeClient
1.客户端管道第二次无法使用CreateFile初始化,给出error 231
用户需要能够随时“重新启动”客户端管道。为什么服务器管道会忙碌呢?CloseHandle总是返回true,但在此之后,调用CreateFile将返回INVALID_HANDLE_VALUEerror 231
请注意,我一次只能有一个管道客户端。我尝试将CreateNamedPipe的maxinstances参数设置为一个更高的值以防万一,但错误没有消失,我无法初始化客户端管道...我该如何解决这个问题?
顺便说一下,通信是来回的,我只是不能让Client管道关闭并重新启动,而且我不能让它一直处于活动状态,这超出了我的能力范围。

uxhixvfz

uxhixvfz1#

根据[MS.文件]:ConnectNamedPipe函数(namedpipeapi. h)-备注(强调的是我的):
命名管道服务器进程可以将
ConnectNamedPipe与新创建的管道示例一起使用。***它还可以与以前连接到另一个客户端进程的示例一起使用;在这种情况下,服务器进程必须首先调用DisconnectNamedPipe函数,以便在句柄可以重新连接到新客户端
之前断开句柄与先前客户端的连接。否则,ConnectNamedPipe返回零,并且如果先前客户端已经关闭其句柄,则GetLastError返回ERROR_NO_DATA,或者如果先前客户端尚未关闭其句柄,则GetLastError返回ERROR_PIPE_CONNECTED。
我修改了你的消息来源,让你有了一个可行的例子。

  • 包括h*:
#pragma once

// Dumped everything here for convenience.
// In real world, each .dll would have its own header file(s).

#include <stdio.h>
#include <Windows.h>

#define PIPE_NAME "\\\\.\\pipe\\DemoPipe1234"

#if defined(_BUILD_NO_DLLS)
#  define DLL_EXPORT_API
#else
#  if defined(DLL_EXPORTS)
#    define DLL_EXPORT_API __declspec(dllexport)
#  else
#    define DLL_EXPORT_API __declspec(dllimport)
#  endif
#endif

#if defined(__cpluscplus)
extern "C" {
#endif

DLL_EXPORT_API void DLL1_Begin();
DLL_EXPORT_API void ReconnectServerEndpoint();
DLL_EXPORT_API void DLL1_End();

DLL_EXPORT_API void DLL2_InitPipeClient();
DLL_EXPORT_API void DLL2_EndPipeClient();

#if defined(__cpluscplus)
}
#endif
  • 服务器c*:
#define DLL_EXPORTS
#include "inc.h"

HANDLE g_hPipeServer = INVALID_HANDLE_VALUE;

void DLL1_Begin()
{
    //printf("%s - 0x%016llX\n", __FUNCTION__, g_hPipeServer);
    g_hPipeServer = CreateNamedPipe(PIPE_NAME,
            PIPE_ACCESS_DUPLEX,
            PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_NOWAIT,
            1,
            4,
            4,
            NMPWAIT_USE_DEFAULT_WAIT,
            NULL);

    if (g_hPipeServer == INVALID_HANDLE_VALUE) {
        printf("Server pipe creation error %lu", GetLastError());
    }
}

void ReconnectServerEndpoint()
{
    if (g_hPipeServer == INVALID_HANDLE_VALUE) {
        printf("Can't reconnect invalid pipe\n");
        return;
    }
    if (!DisconnectNamedPipe(g_hPipeServer)) {
        printf("Server pipe disconnect error %lu\n", GetLastError());
    }
    if (!ConnectNamedPipe(g_hPipeServer, NULL)) {
        DWORD gle = GetLastError();
        if (gle != ERROR_PIPE_LISTENING) {
            printf("Server pipe connect error %lu\n", gle);
        }
    }
}

void DLL1_End()
{
    //printf("%s - 0x%016llX\n", __FUNCTION__, g_hPipeServer);
    if (g_hPipeServer != INVALID_HANDLE_VALUE) {
        if (!DisconnectNamedPipe(g_hPipeServer)) {
            printf("Server pipe disconnect error %lu\n", GetLastError());
        }
        CloseHandle(g_hPipeServer);
        g_hPipeServer = INVALID_HANDLE_VALUE;
    }
}
  • 附件c*:
// Like DLL1, DLL2 gets initialized once and is always attached to the program.
// These functions are called occasionally and are meant solely for the Pipe.

#define DLL_EXPORTS
#include "inc.h"

HANDLE g_hClientPipe = INVALID_HANDLE_VALUE;

void DLL2_InitPipeClient()
{
    //printf("%s - 0x%016llX\n", __FUNCTION__, g_hClientPipe);
    g_hClientPipe = CreateFile(PIPE_NAME,
        GENERIC_READ | GENERIC_WRITE,
        0,
        NULL,
        OPEN_EXISTING,
        0,
        NULL);

    if (g_hClientPipe == INVALID_HANDLE_VALUE) {
        printf("Client pipe creation error %lu\n", GetLastError());
    }
}

void DLL2_EndPipeClient()
{
    //printf("%s - 0x%016llX\n", __FUNCTION__, g_hClientPipe);
    if (g_hClientPipe != INVALID_HANDLE_VALUE) {
        CloseHandle(g_hClientPipe);
        g_hClientPipe = INVALID_HANDLE_VALUE;
    }
}
  • 主00.c *:
#include "inc.h"
#include <conio.h>

#if defined(_BUILD_NO_DLLS)
#  include "cli.c"
#  include "srv.c"
#endif

void loop()
{
    char c = 0;
    while (c != 0x1B) {
        //printf("\n************ MENU ************\nPress:\n- c to connect client\n- d to disconnect client\n- ESC to quit\n- Anything else to do nothing\n******************************\n");
        printf("\nPress: c to connect client, d to disconnect client, ESC to exit:\n");
        c = getch();
        switch (c) {
            case 0x1B: {
                printf("Exit\n");
                break;
            }
            case 0x43:
            case 0x63: {
                printf("Connect client\n");
                DLL2_InitPipeClient();
                break;
            }
            case 0x44:
            case 0x64: {
                printf("Disonnect client\n");
                DLL2_EndPipeClient();
                ReconnectServerEndpoint();
                break;
            }
            default: printf("Do nothing\n");
        }
    Sleep(500);
    }
}

int main()
{
    DLL1_Begin();

    loop();

    DLL1_End();
    printf("\nDone.\n\n");
    return 0;
}

备注

  • 我添加了客户端断开连接时应调用的 ReconnectServerEndpoint
  • 我将可执行文件链接到2 .dlls。我不知道您应该如何执行操作,也许可以动态加载 .dlls,但这不会产生任何影响
    输出
[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q074753431]> sopr.bat
### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ###

[prompt]> "c:\Install\pc032\Microsoft\VisualStudioCommunity\2019\VC\Auxiliary\Build\vcvarsall.bat" x64 > nul

[prompt]> dir /b
cli.c
inc.h
main00.c
srv.c

[prompt]> cl /nologo /MD /DDLL srv.c  /link /NOLOGO /DLL /OUT:dll1.dll
srv.c
   Creating library dll1.lib and object dll1.exp

[prompt]> cl /nologo /MD /DDLL cli.c  /link /NOLOGO /DLL /OUT:dll2.dll
cli.c
   Creating library dll2.lib and object dll2.exp

[prompt]> cl /nologo /MD /W0 main00.c  /link /NOLOGO /OUT:main_dll.exe dll1.lib dll2.lib
main00.c

[prompt]> dir /b
cli.c
cli.obj
dll1.dll
dll1.exp
dll1.lib
dll2.dll
dll2.exp
dll2.lib
inc.h
main00.c
main00.obj
main_dll.exe
srv.c
srv.obj

[prompt]>
[prompt]> main_dll.exe

Press: c to connect client, d to disconnect client, ESC to exit:
Connect client

Press: c to connect client, d to disconnect client, ESC to exit:
Disonnect client

Press: c to connect client, d to disconnect client, ESC to exit:
Connect client

Press: c to connect client, d to disconnect client, ESC to exit:
Disonnect client

Press: c to connect client, d to disconnect client, ESC to exit:
Connect client

Press: c to connect client, d to disconnect client, ESC to exit:
Disonnect client

Press: c to connect client, d to disconnect client, ESC to exit:
Connect client

Press: c to connect client, d to disconnect client, ESC to exit:
Connect client
Client pipe creation error 231

Press: c to connect client, d to disconnect client, ESC to exit:
Disonnect client

Press: c to connect client, d to disconnect client, ESC to exit:
Disonnect client

Press: c to connect client, d to disconnect client, ESC to exit:
Exit

Done.

在上面的输出中,可以看到 *ERROR_PIPE_忙碌 * 仅在客户端尝试连接两次(已经有一个连接的客户端)时发生。
有关更多详细信息,您可以查看:

相关问题