How to create an anonymous pipe that gives access to everyone

Article translations Article translations
Article ID: 813414 - View products that this article applies to.
Expand all | Collapse all

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.

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.

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.

REFERENCES

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

Properties

Article ID: 813414 - Last Review: January 10, 2007 - Revision: 2.0
APPLIES TO
  • Microsoft Platform Software Development Kit-January 2000 Edition
Keywords: 
kbclient kbserver kbpipes kbsecurity kbhowto KB813414

Give Feedback

 

Contact us for more help

Contact us for more help
Connect with Answer Desk for expert help.
Get more support from smallbusiness.support.microsoft.com