tags:

views:

301

answers:

5

I´m writing two litle c++ apps that must communicate. First one will be a service which, every once in a while, must alert the user for something. Since a service cannot create windows I designed the app to be two separate executables.

The service will use a notifier to communicate.

The service needs only to send text messages to the notifier wich will show up a balloon at system tray.

I´m trying to use named pipes and I think I´m almost there, but not quite there. What I have so far is:

At the notifier side:

  m_hInPipe = CreateNamedPipe(L"\\\\.\\pipe\\nhsupspipe", PIPE_ACCESS_INBOUND,
                PIPE_WAIT, 1, 1024, 1024, 60, NULL);

Meaning I created a pipe named nhsupspipe, an inbound pipe.

At the service side:

if (!WriteFile(m_hOutPipe, "My message to the user?", 23, &escritos, &o))
     std::cout << "ERROR: " << GetLastError();

Debugging I can see that it is all ok, the pipe is created and the WriteFile writes my 23 bytes to the pipe.

My question is: How, in the notifier side I´ll be able to read these bytes? Is there any message sent to the process? Do I have to write a handler for the pipe? Anything?

+4  A: 

You need to use ReadFile or ReadFileEx (for Overlapped I/O) on notifier side typically in a thread or message loop. Also look at the documentation for CreateNamedPipe and WaitNamedPipe.

Ryan
+1 on ReadFileEx
kenny
A: 

Not exactly the question, but another option is CreateEvent() and a memory mapped file.

kenny
A: 

If I were doing this, I'd have the service side do the CreateNamedPipe (outbound), and then call ConnectNamedPipe to wait for the notifier to connect.

On the notifier side, I'd use CreateFile with FILE_FLAG_OVERLAPPED. With that, the handle to the pipe will be signaled when data becomes available to read. Your notifier will (presumably) also be maintaining a GUI, so you'll probably want to have it call MsgWaitForMultipleObjects in its event loop. That will wait for messages to be processed or data coming in on the pipe to be processed. That will work almost like a normal event loop, except that its return value is slightly different from GetMessage() -- it'll return WAIT_OBJECT_0 if your handle is signaled, or WAIT_OBJECT_0 + 1 if you have a message (assuming you only have it wait on the one handle -- it's really WAIT_OBJECT_0 + N, where N is the number of handles you had it wait on). For the most part, it's like a normal PeekMessage or GetMessage loop -- wait 'til there's something to do, do it, wait again.

Jerry Coffin
+1  A: 

In this circumstance, I'd probably use RPC instead of raw named pipes. With raw named pipes, you have to parse the data from the client which introduces the possibility of security bugs. With RPC you can let RPC parse the data for you which decreases the chance of introducing an error.

Larry Osterman
+2  A: 

Some simple snippets from the client (your service) & the server (the notifier) [Note: This is adapted from a project I've done a while ago which in turn was heavily "influenced" by the MSDN samples from CreateNamedPipe & co]:

Server side:

HANDLE hPipe = INVALID_HANDLE_VALUE;
bool bConnected = false;

 hPipe = CreateNamedPipe( L"\\\\.\\pipe\\nhsupspipe",
                          PIPE_ACCESS_DUPLEX,
                          PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
                          PIPE_UNLIMITED_INSTANCES,
                          sizeof( Message ),
                          0,
                          0,
                          NULL );

 // failed to create pipe?
 if( hPipe == INVALID_HANDLE_VALUE ){
   return -1;
 }

 // Wait for the client to connect; if it succeeds, 
 // the function returns a nonzero value. If the function
 // returns zero, GetLastError returns ERROR_PIPE_CONNECTED. 
 bConnected = ConnectNamedPipe( hPipe, NULL ) ? true : ( GetLastError() == ERROR_PIPE_CONNECTED );

 if( bConnected ){
  while( true ){ 
   unsigned long ulBytesRead = 0;
   // read client requests from the pipe. 
   bool bReadOk = ReadFile( hPipe,
                            &message,
                            sizeof( message ),
                            &ulBytesRead,
                            NULL );

   // bail if read failed [error or client closed connection]
   if( !bReadOk || ulBytesRead == 0 )
    break;

   // all ok, process the message received

  }
 }
 else{
   // the client could not connect, so close the pipe. 
   CloseHandle( hPipe );
 }

 return 0;

The client:

HANDLE hPipe = INVALID_HANDLE_VALUE;

 // create the named pipe handle
 hPipe = CreateFile( L"\\\\.\\pipe\\nhsupspipe",
                     GENERIC_READ | GENERIC_WRITE,
                     0, 
                     NULL, 
                     OPEN_EXISTING,
                     0,
                     NULL );

 // if everything ok set mode to message mode
 if( INVALID_HANDLE_VALUE != hPipe ){
  DWORD dwMode = PIPE_READMODE_MESSAGE;
  // if this fails bail out
  if( !SetNamedPipeHandleState( hPipe, &dwMode, NULL, NULL ) ){
   CloseHandle( hPipe ); 

   return -1;
  }
 }

 unsigned long ulBytesWritten = 0;
 bool bWriteOk = WriteFile( hPipe, 
                            ( LPCVOID )&message, 
                            sizeof( Message ), 
                            &ulBytesWritten, 
                            NULL );

 // check if the writing was ok
 if( !bWriteOk || ulBytesWritten < sizeof( Message ) ){
  return -1;
 }

 // written ok

 return 0;

The Message mentioned above is a structure that will be your message, which you will probably want to pack.

Since in your scenario the client (the service) will probably be up & running before the server (the notifier) you'll need to have in place some kind of reconnection strategy on the client side.

And on a slightly different note you should consider carefully what Mr. Osterman said in his reply (even for nothing else but because he's Larry Osterman).

Eugen Constantin Dinca