63853


名前付きパイプのプログラムを作成しています。

サーバー側(C++)のプログラムは以下の通りです。

[code]

    #include <iostream>
    #include <Windows.h>
    
    DWORD createPipeThread( LPVOID lpParameter )
    {
        HANDLE pipe = ( (HANDLE*)lpParameter )[0];
        HANDLE closeEvent = ( (HANDLE*)lpParameter )[1];
    
        for( ;; ) {
    
            OVERLAPPED overlapped;
            size_t size = sizeof( OVERLAPPED );
            std::memset( &overlapped, 0, size );
            overlapped.hEvent = ::CreateEvent( nullptr, false, false, nullptr );
            ::ConnectNamedPipe( pipe, &overlapped );
    
            HANDLE events[] = { overlapped.hEvent, closeEvent };
            int eventIndex = ::WaitForMultipleObjects( 2, events, false, INFINITE ) - WAIT_OBJECT_0;
            ::CloseHandle( overlapped.hEvent );
    
            if( eventIndex == 0 ){
                std::wstring str = L"Hello, world";
                DWORD result = 0;
                ::WriteFile( pipe, str.c_str(), ( str.length() + 1 ) * sizeof( wchar_t ), &result, nullptr );
    
                ::DisconnectNamedPipe( pipe );
            } else {
                break;
            }
        }
    
        return 0;
    }
    
    int main()
    {
        HANDLE pipe = ::CreateNamedPipe( L"\\\\.\\pipe\\NamedPipeTest", PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED, PIPE_WAIT | PIPE_READMODE_BYTE | PIPE_TYPE_BYTE, PIPE_UNLIMITED_INSTANCES, 1024, 1024, 1000, nullptr );
        HANDLE closeEvent = ::CreateEvent( nullptr, false, false, nullptr );
        HANDLE handles[] = { pipe, closeEvent };
    
        HANDLE thread = ::CreateThread( nullptr, 0, (LPTHREAD_START_ROUTINE)createPipeThread, handles, 0, nullptr );
    
        std::getchar();
        ::SetEvent( closeEvent );
    
        ::WaitForSingleObject( thread, 10000 );
        ::CloseHandle( closeEvent );
        ::CloseHandle( pipe );
    }
    
[/code]

クライアント側(C++)のコードは以下のとおりです。これは思ったとおり(Hello, Worldが出力される)に動きます。

[code]

    #include <iostream>
    #include <Windows.h>
    
    int main()
    {
        HANDLE pipe = ::CreateFile( L"\\\\.\\pipe\\NamedPipeTest", GENERIC_READ, 0, nullptr, OPEN_EXISTING, 0, 0 );
        if( pipe == INVALID_HANDLE_VALUE ) return 1;
    
        const size_t bufSize = 1024;
        wchar_t buf[bufSize];
        std::memset( buf, 0, bufSize * sizeof( wchar_t ) );
        DWORD result = 0;
        ::ReadFile( pipe, buf, bufSize * sizeof( wchar_t ), &result, nullptr );
        std::wcout << buf << std::endl;
    
        ::CloseHandle( pipe );
    
        return 0;
    }
    
[/code]

同様にクラアントをC#で書いたのですが、思ったとおりに動きません。何も出力されずに終わってしまいます。

[code]

    namespace NamedPipeClientCSharp {
        class Program {
            static void Main( string[] args ) {
                using( var pipe = new System.IO.Pipes.NamedPipeClientStream( ".", "NamedPipeTest", System.IO.Pipes.PipeDirection.In ) ) {
                    pipe.Connect();
    
                    /* NG 動作せず
                    byte[] buf = new byte[2048];
                    pipe.Read( buf, 0, 2048 );
                    System.Console.WriteLine( System.Text.Encoding.Unicode.GetString( buf ) );
                    */
    
                    // NG 動作せず
                    using( var stream = new System.IO.StreamReader( pipe, System.Text.Encoding.Unicode ) )
                        System.Console.WriteLine( stream.ReadToEnd() );
                }
            }
        }
    }
    
[/code]

stream.ReadToEnd()が空文字列を出力して終了してしまいます。  
どのように修正すれば動くのかわかりましたら、教えていただけますでしょうか。


CC BY-SA 4.0
2020-03-14T08:23:02.510
63823
2020-03-15T03:40:52.080
2020-03-14T11:43:02.583
20304
20304
question
c# c++
WindowsAPIで作った名前付きパイプを.NETから読み出したい

[code]

    ::WriteFile( pipe, str.c_str(), ( str.length() + 1 ) * sizeof( wchar_t ), &result, nullptr );
    
    ::DisconnectNamedPipe( pipe );
    
[/code]

サーバー側のこのコード、相手が読み出す前に切断してしまっています。

C#版は`pipe.Connect()`によりサーバーの状態確認を行っています。この間にサーバー側が切断してしまうために読み出せなくなっていると考えられます。ちなみに何回か実行すると、読み出しに成功するときもあるようです。  
C++版は`CreateFile`直後にノータイムで`ReadFile`を行っているために読み出しに成功しているだけで、例えば`Sleep(1)`を挟むだけで読み出しに失敗します。

[Named Pipe Operations](https://docs.microsoft.com/en-
us/windows/win32/ipc/named-pipe-operations)では

> When a client and server finish using a pipe instance, the server should
> first call the `FlushFileBuffers` function, to ensure that all bytes or
> messages written to the pipe are read by the client.

サーバー側は[`FlushFileBuffers`](https://docs.microsoft.com/en-
us/windows/win32/api/fileapi/nf-fileapi-
flushfilebuffers)を呼ぶべきとされています。これで読み出し漏れが防げることでしょう。


CC BY-SA 4.0
2020-03-15T03:40:52.080
63853
2020-03-15T03:40:52.080
4236
63823
answer