Notice: This website is an unofficial Microsoft Knowledge Base (hereinafter KB) archive and is intended to provide a reliable access to deleted content from Microsoft KB. All KB articles are owned by Microsoft Corporation. Read full disclaimer for more details.

How to create an anonymous pipe that gives access to everyone


Summary

This article describes how to create an anonymous pipe that gives access to everyone. This article contains a sample application that creates an anonymous session pipe that is used for communication between the server and the client. This article describes the following in detail:
  • Creating a server that writes the information at one end of the pipe.
  • Registering the name of the pipe in the registry.
  • Handling the Windows security when communicating over the pipe.
  • Creating a client that reads the information from the other end of the pipe.

↑ Back to the top


INTRODUCTION

This article describes how to create a pipe server that sends and receives data across domains without requiring user names and passwords. When a client connects to the pipe server, the server makes an access check, and then requests the client to authenticate. Depending on how the permissions of the pipe are defined, the access may be granted or denied. If the client user has an access token, the client can connect to the server. If the client does not have an access token, the client application must perform a logon to the server. However, sometimes you may have to give access to everyone. Therefore, you must create an anonymous, or null, session pipe. Because you are granting permissions to everyone, your pipe must not give access to any sensible information to the users.

↑ Back to the top


More Information

The sample application in this article is a simple client/server application that uses an anonymous named pipe. The server incrementally writes numbers to the pipe that are read and printed by the client. Because the pipe is anonymous, the client may connect from other domains through an anonymous logon. The sample application is explained as follows.

Server

The server creates the anonymous pipes, and then listens for clients. To create an anonymous pipe, make sure that you do the following:
  1. Add the name of the pipe to the registry.
  2. Define the LPSECURITY_ATTRIBUTES attributes that are passed to the CreateNamedPipe function.

Registry

In the registry of the server computer, the pipe name is added to the following registry subkey:
\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters\
The pipe name that is added to this registry entry is the name after the last backslash (\) character in the string that is used to open the pipe.

For example, for the \\myserver\pipe\AnonymousPipe pipe, you must add AnonymousPipe to the NullSessionPipes registry entry on the server computer. You can do this by using the RegisterNullSession function and the UnregisterNullSession function in the sample application.

Note myserver is a placeholder for the name of your server.

Security

Many programmers use NULL Discretionary Access Control List (DACL) to give anonymous access to shared resources. However, there are no restrictions that are defined to access the shared resource. You must create a security identifier (SID) that gives the anonymous logon and the read-only access to the pipe. The SID that you create in the sample is known by every installation of Microsoft Windows, and is named AnonymousLogin. This SID is described as S-1-5-7. For more information about these numbers, see the Winnt.h file. When the pipe client connects to the server, an anonymous logon is performed by making a call to the WNetAddConnection2 function, and the AnonymousLogin SID will grant the access. In the sample application, a small class that is named SEC is created to handle the complex access structures. This behavior eases the freeing of allocated memory. The BuildSecurityAttributes function in this class builds the SID and sets the permissions. The permissions for the anonymous logon are set as read-only because the pipe is a read-only pipe. You do this by making a call to the AddAccessAllowedAce function. If you want to create a pipe that has both read and write permissions, you must change the permissions with the AddAccessAllowedAce function.

The other SID must be defined by the owner who owns the named pipe, and this makes sure that the application has access to the named pipe. The owner SID is the user SID that is running the thread or the process. The GetUserSid function extracts the SID from the token of the user. The token is created when you log on and you specify your name and password that is used for all access checks.

With the attributes defined, the server calls the CreateNamedPipe function, and then waits for incoming client calls.

Client

The client opens the pipe with a call to the CreateFile function. If an error occurs, the client checks if it is a logon failure error, an access denied error, or a bad password error. If an error occurs, perform an anonymous logon by calling the WNetAddConnection2 function and passing an empty string as user name and password. When the null session is established, the client calls the CreateFile function again. After the client is connected, it prints a number each second.

Sample application

To create an application that communicates between the processes by using an anonymous pipe that gives access to everyone, follow these steps:
  1. Start Microsoft Visual C++ 6.0.
  2. On the File menu, click New.
  3. On the Projects tab, click Win32 Console Application, type MyApp in the Project name box, and then click OK.
  4. In the Win32 Console Application - Step 1 of 1 dialog box, click Finish.
  5. In the New Project Information window, click OK.
  6. On the File menu, click New. The New dialog box appears.
  7. Click C++ Source File. In the right pane, type MyApp in the File name box, and then click OK.
  8. In the left pane, click the FileView tab.
  9. Expand MyApp files, and then expand Source Files.
  10. Under Source Files, right-click MyApp.cpp, and then click Open. In the right pane, the code view of the MyApp.cpp file opens.
  11. Add the following code to the MyApp.cpp file:
    #define UNICODE
    #include <windows.h>
    #include <stdio.h>

    // Types

    class SEC
    {
    // Class to handle security attributes
    public:
    SEC();
    ~SEC();
    BOOL BuildSecurityAttributes( SECURITY_ATTRIBUTES* psa );

    private:
    BOOL GetUserSid( PSID* ppSidUser );

    BOOL allocated;
    PSECURITY_DESCRIPTOR psd;
    PACL pACL;
    PTOKEN_USER pTokenUser;
    };

    // Constants
    static const WCHAR* PipeName = L"AnonymousPipe";

    // Functions
    static void Client( const WCHAR* server );
    static void Server( void );
    static void WriteToPipe( HANDLE hPipe );
    static void ReadFromPipe( HANDLE hPipe );
    static void DisplayError( WCHAR *pszAPI);
    static BOOL WINAPI ConsoleCtrlHandler( DWORD CtrlType );

    static BOOL EstablishNullSession( const WCHAR* computerName );

    static BOOL RegisterNullSession( const WCHAR* pipeName );
    static BOOL UnregisterNullSession( const WCHAR* pipeName );

    static LONG RegInsertToMultiString( HKEY hKey, const WCHAR* valueName, const WCHAR* str );
    static LONG RegRemoveFromMultiString( HKEY hKey, const WCHAR* valueName, const WCHAR* str );
    static BOOL FindEntryInMultiString( const WCHAR* data, const WCHAR* entry, DWORD* offset );

    void
    wmain( int argc, WCHAR* argv[] )
    {
    if( argc < 2 )
    {
    wprintf( L"Usage: pipe.exe <server|client [server name for client]>\n" );
    exit(1);
    }

    if( !SetConsoleCtrlHandler( ConsoleCtrlHandler, TRUE ) )
    DisplayError( L"SetConsoleCtrlHandler" );

    if( wcsicmp( argv[1], L"client") == 0 )
    {
    WCHAR server[512];
    if( argc == 3 )
    {
    wcscpy( server, argv[2] );
    }
    else
    {
    wcscpy( server, L"." );
    }
    Client( server );
    }
    else
    if( wcsicmp( argv[1], L"server" ) == 0 )
    {
    Server();
    }
    else
    {
    wprintf( L"Usage: pipe.exe <server|client [server name for client]>\n" );
    exit(1);
    }
    }

    //Runs the server

    void
    Server( void )
    {
    SEC sec;
    HANDLE hPipe;
    BOOL isConnected;
    SECURITY_ATTRIBUTES sa;
    WCHAR server[512];

    swprintf( server, L"\\\\.\\pipe\\%s", PipeName );

    if( !RegisterNullSession( PipeName ) )
    {
    wprintf( L"WARNING: could not register %s for Null Session, server will not be accessible from outside domain.\n", server );
    wprintf( L"WARNING: only administrators can register a Null Session Pipe.\n" );
    }

    sec.BuildSecurityAttributes( &sa );

    wprintf( L"INFORM: to stop, press Ctrl-C or Ctrl-Break.\n" );
    while( 1 )
    {
    hPipe = CreateNamedPipe(
    server,
    PIPE_ACCESS_OUTBOUND, // read-only pipe
    PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
    PIPE_UNLIMITED_INSTANCES,
    sizeof(DWORD),
    0,
    NMPWAIT_USE_DEFAULT_WAIT,
    &sa );

    if( hPipe == INVALID_HANDLE_VALUE )
    DisplayError( L"CreatePipe" );

    // Wait for the client to connect.
    wprintf( L"INFORM: listening at %s, waiting for client to connect\n", server );
    isConnected = ConnectNamedPipe( hPipe, NULL ) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
    if( isConnected )
    {
    wprintf( L"INFORM: client connected.\n" );
    WriteToPipe( hPipe );
    }
    else
    // The client could not connect. Therefore, close the pipe.
    CloseHandle(hPipe);
    }
    }

    //Runs the client
    void
    Client( const WCHAR* server )
    {
    HANDLE hPipe;
    WCHAR fullName[512];
    BOOL triedLogon = FALSE;

    swprintf( fullName, L"\\\\%s\\pipe\\%s", server, PipeName );

    wprintf( L"INFORM: connecting to %s\n", fullName );

    while( 1 )
    {
    hPipe = CreateFile(
    fullName,
    GENERIC_READ,
    FILE_SHARE_READ,
    NULL,
    OPEN_EXISTING,
    SECURITY_ANONYMOUS,
    NULL);

    if( hPipe == INVALID_HANDLE_VALUE )
    {
    DWORD lastError = GetLastError(); // An error ocurred, try to analyse it

    if( (lastError == ERROR_LOGON_FAILURE) ||
    (lastError == ERROR_ACCESS_DENIED) ||
    (lastError == ERROR_INVALID_PASSWORD)) //Add other access errors here if it is required
    {
    if( triedLogon )
    {
    DisplayError( L"CreateFile" );
    }
    else
    if( EstablishNullSession( server ) )
    {
    wprintf( L"INFORM: established Null session.\n" );
    }
    else
    {
    wprintf( L"WARNING: could not establish Null Session.\n" );
    }
    triedLogon = TRUE;
    }
    else
    if( lastError == ERROR_PIPE_BUSY )
    {
    // All pipe instances are busy. Therefore, wait for 20 seconds.
    wprintf( L"INFORM: pipe busy, trying for 20secs to reconnect.\n" );
    if( !WaitNamedPipe( fullName, 20000) )
    DisplayError( L"WaitNamedPipe" );
    }
    else
    {
    DisplayError( L"CreateFile" );
    }
    }
    else
    {
    ReadFromPipe( hPipe );
    }
    }
    }

    /**
    Tries to establish a NULL session to the remote computer. A NULL session allows access to NullSessionPipes of
    the server.
    Input parameters: computerName: name of the server to connect to
    Output parameters: TRUE | FALSE
    */

    BOOL
    EstablishNullSession( const WCHAR* computerName )
    {
    NETRESOURCE nr;
    WCHAR server[MAX_PATH];
    DWORD ret;

    if( wcscmp( computerName, L"." ) == 0 )
    {
    wcscpy( server, computerName );
    }
    else
    {
    wcscpy( server, L"\\\\" );
    wcscat( server, computerName );
    }

    ZeroMemory( &nr, sizeof(nr) );
    nr.dwType = RESOURCETYPE_ANY;
    nr.lpRemoteName = server;

    ret = WNetAddConnection2( &nr, L"", L"", 0);
    if( ret != ERROR_SUCCESS )
    {
    DisplayError( L"WNetAddConnection2" );
    return FALSE;
    }

    return TRUE;
    }

    //Called when the user presses Ctrl-C or Ctrl-Break in the console window.

    BOOL WINAPI
    ConsoleCtrlHandler( DWORD CtrlType )
    {
    // clean up the registry
    UnregisterNullSession( PipeName );
    return FALSE;
    }

    /** Writes to the pipe
    Input parameters: hPipe: handle to an opened pipe
    */
    void
    WriteToPipe( HANDLE hPipe )
    {
    DWORD i = 0;
    DWORD cbWrite, cbWritten;

    cbWrite = sizeof(DWORD);
    while( 1 )
    {
    if( !WriteFile( hPipe, &i, cbWrite, &cbWritten, NULL) )
    {
    if( cbWrite != cbWritten) break;
    }
    i++;
    Sleep( 1000 );
    }
    // Flush the pipe to allow the client to read the contents of the pipe
    // before disconnecting. Disconnect the pipe, and then close the
    // handle to this pipe instance.

    FlushFileBuffers( hPipe );
    DisconnectNamedPipe( hPipe );
    CloseHandle( hPipe );

    wprintf( L"INFORM: client disconnected.\n" );
    }

    /** Reads from pipe
    Input parameters: hPipe: handle to opened pipe. */

    void
    ReadFromPipe( HANDLE hPipe )
    {
    DWORD i, cbRead;
    while( 1 )
    {
    if( !ReadFile( hPipe, &i, sizeof(DWORD), &cbRead, NULL) )
    {
    if( GetLastError() != ERROR_MORE_DATA )
    {
    CloseHandle( hPipe );
    DisplayError( L"ReadFile" );
    }
    }
    wprintf( L" %li ", i );
    };
    }

    /** Displays an error and exits the process
    Input parameters: pszAPI: name of the Win32 function that returned an error. */

    void
    DisplayError( WCHAR* pszAPI )
    {
    LPVOID lpvMessageBuffer;

    FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
    NULL, GetLastError(),
    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
    (LPTSTR)&lpvMessageBuffer, 0, NULL);

    //... now display this string
    wprintf( L"ERROR: API = %s.\n", pszAPI);
    wprintf( L" error code = %d.\n", GetLastError());
    wprintf( L" message = %s.\n", (char *)lpvMessageBuffer);

    // Free the buffer allocated by the system
    LocalFree( lpvMessageBuffer );

    ExitProcess( GetLastError() );
    }

    /** Constructor */
    SEC::
    SEC()
    {
    allocated = FALSE;
    psd = NULL;
    pACL = NULL;
    pTokenUser = NULL;
    }

    /** Destructor */
    SEC::
    ~SEC()
    {
    if( allocated )
    {
    if( psd ) HeapFree( GetProcessHeap(), 0, psd );
    if( pACL ) HeapFree( GetProcessHeap(), 0 , pACL );
    if( pTokenUser ) HeapFree( GetProcessHeap(), 0, pTokenUser );
    allocated = FALSE;
    }
    }

    /** Builds security attributes that allows read-only access to everyone
    Input parameters: psa: security attributes to build
    Output parameters: TRUE | FALSE */
    BOOL SEC::
    BuildSecurityAttributes( SECURITY_ATTRIBUTES* psa )
    {
    DWORD dwAclSize;
    PSID pSidAnonymous = NULL; // Well-known AnonymousLogin SID
    PSID pSidOwner = NULL;

    if( allocated ) return FALSE;

    SID_IDENTIFIER_AUTHORITY siaAnonymous = SECURITY_NT_AUTHORITY;
    SID_IDENTIFIER_AUTHORITY siaOwner = SECURITY_NT_AUTHORITY;

    do
    {
    psd = (PSECURITY_DESCRIPTOR) HeapAlloc( GetProcessHeap(),
    HEAP_ZERO_MEMORY,
    SECURITY_DESCRIPTOR_MIN_LENGTH);
    if( psd == NULL )
    {
    DisplayError( L"HeapAlloc" );
    break;
    }

    if( !InitializeSecurityDescriptor( psd, SECURITY_DESCRIPTOR_REVISION) )
    {
    DisplayError( L"InitializeSecurityDescriptor" );
    break;
    }

    // Build anonymous SID
    AllocateAndInitializeSid( &siaAnonymous, 1,
    SECURITY_ANONYMOUS_LOGON_RID,
    0,0,0,0,0,0,0,
    &pSidAnonymous
    );

    if( !GetUserSid( &pSidOwner ) )
    {
    return FALSE;
    }

    // Compute size of ACL
    dwAclSize = sizeof(ACL) +
    2 * ( sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) ) +
    GetLengthSid( pSidAnonymous ) +
    GetLengthSid( pSidOwner );

    pACL = (PACL)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwAclSize );
    if( pACL == NULL )
    {
    DisplayError( L"HeapAlloc" );
    break;
    }

    InitializeAcl( pACL, dwAclSize, ACL_REVISION);


    if( !AddAccessAllowedAce( pACL,
    ACL_REVISION,
    GENERIC_ALL,
    pSidOwner
    ))
    {
    DisplayError( L"AddAccessAllowedAce" );
    break;
    }


    if( !AddAccessAllowedAce( pACL,
    ACL_REVISION,
    FILE_GENERIC_READ, //GENERIC_READ | GENERIC_WRITE,
    pSidAnonymous
    ) )
    {
    DisplayError( L"AddAccessAllowedAce" );
    break;
    }

    if( !SetSecurityDescriptorDacl( psd, TRUE, pACL, FALSE) )
    {
    DisplayError( L"SetSecurityDescriptorDacl" );
    break;
    }

    psa->nLength = sizeof(SECURITY_ATTRIBUTES);
    psa->bInheritHandle = TRUE;
    psa->lpSecurityDescriptor = psd;

    allocated = TRUE;
    }while(0);

    if( pSidAnonymous ) FreeSid( pSidAnonymous );
    if( pSidOwner ) FreeSid( pSidOwner );

    if( !allocated )
    {
    if( psd ) HeapFree( GetProcessHeap(), 0, psd );
    if( pACL ) HeapFree( GetProcessHeap(), 0 , pACL );
    }

    return allocated;
    }

    /** Obtains the SID of the user running this thread or process.
    Output parameters: ppSidUser: the SID of the current user,
    TRUE | FALSE: could not obtain the user SID */
    BOOL SEC::
    GetUserSid( PSID* ppSidUser )
    {
    HANDLE hToken;
    DWORD dwLength;
    DWORD cbName = 250;
    DWORD cbDomainName = 250;

    if( !OpenThreadToken( GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken) )
    {
    if( GetLastError() == ERROR_NO_TOKEN )
    {
    if( !OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &hToken) )
    {
    return FALSE;
    }
    }
    else
    {
    return FALSE;
    }
    }


    if( !GetTokenInformation( hToken, // handle of the access token
    TokenUser, // type of information to retrieve
    pTokenUser, // address of retrieved information
    0, // size of the information buffer
    &dwLength // address of required buffer size
    ))
    {
    if( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
    {
    pTokenUser = (PTOKEN_USER) HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength );
    if( pTokenUser == NULL )
    {
    return FALSE;
    }
    }
    else
    {
    return FALSE;
    }
    }

    if( !GetTokenInformation( hToken, // handle of the access token
    TokenUser, // type of information to retrieve
    pTokenUser, // address of retrieved information
    dwLength, // size of the information buffer
    &dwLength // address of required buffer size
    ))
    {
    HeapFree( GetProcessHeap(), 0, pTokenUser );
    pTokenUser = NULL;

    return FALSE;
    }

    *ppSidUser = pTokenUser->User.Sid;
    return TRUE;
    }

    /** Registers a pipe as a NULL SESSION pipe that may be accessed by unauthenticated users.
    Input parameters: serverName: pipe to register
    Output parameters: TRUE: pipe registered
    | FALSE: could no register pipe. */

    BOOL
    RegisterNullSession( const WCHAR* pipeName )
    {
    LONG status;
    HKEY hKey;

    // Opens key
    status = RegCreateKeyEx( HKEY_LOCAL_MACHINE,
    L"SYSTEM\\CurrentControlSet\\Services\\lanmanserver\\parameters",
    0,
    NULL,
    REG_OPTION_NON_VOLATILE,
    KEY_QUERY_VALUE | KEY_SET_VALUE,
    NULL,
    &hKey,
    NULL);

    if( status != ERROR_SUCCESS )
    {
    return FALSE;
    }

    status = RegInsertToMultiString( hKey, L"NullSessionPipes", pipeName );

    RegCloseKey( hKey );

    if( status != ERROR_SUCCESS )
    {
    return FALSE;
    }

    return TRUE;
    }

    /** Unregisters a pipe from the NULL SESSION pipe list.
    Input parameters: pipeName: pipe to unregister
    Output parameters: TRUE: pipe unregistered or pipe was not registered
    | FALSE: could not unregister pipe
    */

    BOOL
    UnregisterNullSession( const WCHAR* pipeName )
    {
    LONG status;
    HKEY hKey;

    // Opens key
    status = RegCreateKeyEx( HKEY_LOCAL_MACHINE,
    L"SYSTEM\\CurrentControlSet\\Services\\lanmanserver\\parameters",
    0,
    NULL,
    REG_OPTION_NON_VOLATILE,
    KEY_QUERY_VALUE | KEY_SET_VALUE,
    NULL,
    &hKey,
    NULL);

    if( status != ERROR_SUCCESS )
    {
    return FALSE;
    }

    status = RegRemoveFromMultiString( hKey, L"NullSessionPipes", pipeName );

    RegCloseKey( hKey );

    if( (status != ERROR_SUCCESS) && (status != ERROR_INVALID_NAME) )
    {
    return FALSE;
    }

    return TRUE;
    }

    /** Inserts a string to a MULTI_SZ value. If the string is already present, nothing will be added. This
    avoids duplicated strings.
    Input parameters: hKey: handle to open key,
    valueName: name of the multistring value to which the str value will be appended,
    str: string to append
    <-- ERROR_SUCCESS
    | any error code returned by RegQueryValueEx and RegSetValueEx */

    LONG
    RegInsertToMultiString( HKEY hKey, const WCHAR* valueName, const WCHAR* str )
    {
    LONG status;
    BYTE* data;
    DWORD newSize, oldSize, strSize;

    // Obtains the current data size
    status = RegQueryValueEx( hKey,
    valueName,
    NULL,
    NULL,
    NULL,
    &oldSize );

    if( status != ERROR_SUCCESS )
    {
    return status;
    }

    // Allocates memory to hold all the data
    strSize = (wcslen(str) + 1) * sizeof(WCHAR);
    newSize = oldSize + strSize;
    data = (BYTE*)HeapAlloc( GetProcessHeap(), 0 , newSize );
    if( data == NULL )
    {
    return ERROR_OUTOFMEMORY;
    }

    // Obtains the current data
    status = RegQueryValueEx( hKey,
    valueName,
    NULL,
    NULL,
    data,
    &oldSize );
    if( status != ERROR_SUCCESS )
    {
    HeapFree( GetProcessHeap(), 0 , data );
    return status;
    }

    // Looks if the data is already there
    if( FindEntryInMultiString( (WCHAR*)data, str, NULL ) )
    {
    HeapFree( GetProcessHeap(), 0 , data );
    return ERROR_SUCCESS;
    }

    // Appends our entry
    CopyMemory( data+oldSize-2, str, strSize );

    // Append another NULL terminator
    data[newSize-2] = 0;
    data[newSize-1] = 0;

    // Sets the new data
    status = RegSetValueEx( hKey,
    valueName,
    0,
    REG_MULTI_SZ,
    data,
    newSize );

    HeapFree( GetProcessHeap(), 0 , data );

    return status;
    }

    /** Removes a string from a MULTI_SZ value.
    Input parameters: hKey: handle to open key,
    valueName: name of the multistring value to which the str value will be appended,
    str: string to append
    <-- ERROR_SUCCESS
    | ERROR_INVALID_NAME: str was not found
    | any error code returned by RegQueryValueEx and RegSetValueEx */

    LONG
    RegRemoveFromMultiString( HKEY hKey, const WCHAR* valueName, const WCHAR* str )
    {
    LONG status;
    BYTE* data;
    DWORD size, offset, strSize;
    BYTE* p;
    BOOL found = FALSE;

    // Obtains the current data size
    status = RegQueryValueEx( hKey,
    valueName,
    NULL,
    NULL,
    NULL,
    &size );
    if( status != ERROR_SUCCESS )
    {
    return status;
    }

    // Allocates memory to hold all the data
    strSize = (wcslen(str) + 1) * sizeof(WCHAR);
    data = (BYTE*)HeapAlloc( GetProcessHeap(), 0 , size );
    if( data == NULL )
    {
    return ERROR_OUTOFMEMORY;
    }

    // Obtains the current data
    status = RegQueryValueEx( hKey,
    valueName,
    NULL,
    NULL,
    data,
    &size );
    if( status != ERROR_SUCCESS )
    {
    HeapFree( GetProcessHeap(), 0 , data );
    return status;
    }

    // Searches the entry to be removed
    found = FindEntryInMultiString( (WCHAR*)data, str, &offset );
    if( !found )
    {
    HeapFree( GetProcessHeap(), 0 , data );
    return ERROR_INVALID_NAME;
    }

    // Writes over the entry, and then puts it back in the registry
    p = data + offset;

    CopyMemory( p, p + strSize, size - strSize - offset );

    status = RegSetValueEx( hKey,
    valueName,
    0,
    REG_MULTI_SZ,
    data,
    size - strSize );

    HeapFree( GetProcessHeap(), 0 , data );

    return status;
    }

    /** Input parameters: data: multi string in which the entry is to be found,
    entry: to find
    offset: if not NULL holds the number of bytes from the beginning of data where entry starts
    Output parameters: TRUE: found entry
    | FALSE: did not find entry */

    BOOL
    FindEntryInMultiString( const WCHAR* data, const WCHAR* entry, DWORD* offset )
    {
    BYTE* p;
    DWORD read, tmp;
    BOOL found = FALSE;

    p = (BYTE*)data;
    read = 0;
    while( !( (*p == 0 ) && ((*(p+1)) == 0 ) ) )
    {
    if( wcscmp( (WCHAR*)p, entry ) == 0 )
    {
    found = TRUE;
    break;
    }

    tmp = (wcslen( (WCHAR*)p ) + 1) * sizeof(WCHAR);
    read += tmp ;
    p += tmp;
    }

    if( offset )
    {
    if( found )
    {
    *offset = read;
    }
    else
    {
    *offset = 0;
    }
    }
    return found;
    }
  12. To build the application, you must add the Mpr.lib file to your project. To do this, follow these steps:
    1. On the Project menu, click Settings. The Project Settings dialog box appears.
    2. Click the Link tab, and then append Mpr.lib to the string in the Object/library modules box.
    3. Click OK.
  13. On the Build menu, click Build MyApp.exe.
  14. To run the application, follow these steps:
    1. Click Start, click Run, type cmd in the Open box, and then click OK.
    2. At the command prompt, run the following command:
      Path of MyApp\MyAPP server
      The server waits for the client to connect.

      Note Path of MyApp is a placeholder for the path where the MyApp.exe file is saved.
    3. Repeat step a, and then run the following command at the command prompt:
      Path of MyApp\MyAPP client Computer_Name
      The client connects to the server, and then prints the numbers sequentially that are read from the pipe.

      Note Computer_Name is a placeholder for the name of the client computer.

↑ Back to the top


References

For more information, visit the following Microsoft Developer Network (MSDN) Web sites:
Anonymous pipe security and access rights
http://msdn2.microsoft.com/en-us/library/aa365142.aspx

↑ Back to the top


Keywords: kbclient, kbserver, kbpipes, kbsecurity, kbwiprotr, kbwiproauthor, kbhowto, kb

↑ Back to the top

Article Info
Article ID : 813414
Revision : 4
Created on : 6/10/2019
Published on : 6/10/2019
Exists online : False
Views : 248