Logo Search packages:      
Sourcecode: qnapi version File versions  Download package

qinterprocesschannel_win32.cpp

/****************************************************************************
**
** Copyright (C) 2008 Krzemin <pkrzemin@o2.pl>
**
** This file may be used under the terms of the GNU General Public License
** version 2 as published by the Free Software Foundation and appearing in the
** file GPL.txt included in the packaging of this file.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
****************************************************************************/

#include "qinterprocesschannel_win32.h"


/*!
      \class QInterProcessChannel
      
      \brief A generic network-based communication channel for applications.
      
      QInterProcessChannel offers communication facilities between several instances of
      the same application. It initializes itself so that the first created instance acts
      as a "server" which recieve messages from the others instances. This is particularly
      handy when creating a single-instance application which, for example, uses file
      association through a command line interface.
      
      \see QSingleApplication
*/

/*!
      \brief Constructor
      
      Check for a valid server and create one if none found.
*/
QInterProcessChannel::QInterProcessChannel(QObject *p)
 : QThread(p)
{
      uniqPrefix = QApplication::applicationName();
      pipeName = QString("\\\\.\\pipe\\%1%2").arg(uniqPrefix).arg("_uniquePipeName");
      globalMutexStr = uniqPrefix + QString("_globalMutexId");
      blockerMutexStr = uniqPrefix + QString("_blockerMutexId");
      init();
}

/*!
      \brief Destructor
*/
QInterProcessChannel::~QInterProcessChannel()
{
      //while(isRunning())
      //    quit();
      
      close();
}

/*!
      \return Wether the instance has a server role
*/
bool QInterProcessChannel::isServer() const
{
      return serverMode;
}

/*!
      \return The current buffered message
*/
QString QInterProcessChannel::messageBuffer() const
{
      return sMsg;
}

/*!
      \brief Sets a buffered message
*/
void QInterProcessChannel::setMessageBuffer(const QString& s)
{
      sMsg = s;
}

/*!
      \overload
      
      Sends the content of the current message buffer
*/
void QInterProcessChannel::sendMessage()
{
      sendMessage(sMsg);
      sMsg.clear();
}

/*!
      \overload
*/
void QInterProcessChannel::sendMessage(const QString& s)
{
      sendMessage(s.toLocal8Bit());
}
/*!
      \brief Send a message to server instance
*/
void QInterProcessChannel::sendMessage(const QByteArray& msg)
{
      if(msg.count())
      {
            // a lot of simultaneous clients cause that pipe sometimes omits
            // some of these clients despite results of writing functions are ok ;/
            // workaround: reserve mutex, wait 100ms before every write operation
            
            HANDLE mutex = CreateMutexA(NULL, FALSE, qPrintable(blockerMutexStr));
            WaitForSingleObject(mutex, INFINITE);
            Sleep(100);
            
            HANDLE hPipe;
            
            // loop to get pipe's handle
            for(;;)
            {
                  hPipe = CreateFileA( 
                                    qPrintable(pipeName),
                                    GENERIC_WRITE,
                                    0,
                                    NULL,
                                    OPEN_EXISTING,
                                    0,
                                    NULL);
                  
                  if(hPipe != INVALID_HANDLE_VALUE)
                        break;
                  
                  if(GetLastError() != ERROR_PIPE_BUSY)
                  {
                        ReleaseMutex(mutex);
                        return;
                  }
                  
                  // wait max 1s and retry
                  WaitNamedPipeA(qPrintable(pipeName), 1000);
            }
            
            // ok, now we have pipe's handle, we can prepare buffers and write to the pipe
            
            char buffer[bufferSize];
            strncpy(buffer, msg.constData(), bufferSize);
            buffer[bufferSize-1] = 0;
            
            DWORD bytesWritten;
            bool writeSuccess = WriteFile( 
                                                hPipe,
                                                buffer,
                                                strlen(buffer) + 1,
                                                &bytesWritten,
                                                NULL);
            
            if (!writeSuccess)
            {
                  ReleaseMutex(mutex);
                  return;
            }
            
            CloseHandle(hPipe);
            ReleaseMutex(mutex);
      } else {
            //qWarning("::sendMessage(%d): Empty messages are not carried out...", GetCurrentProcessId());
      }
}

/*!
      \brief Closes the communication channel
*/
void QInterProcessChannel::close()
{
      if(serverMode)
      {
            // if we want to quit pipe server thread, we have to send --quit message
            sendMessage(QString("--quit"));
      }
}

/*!
      \internal
*/
void QInterProcessChannel::run()
{
      if(!serverMode) return;
      
      HANDLE hPipe;
      char buffer[bufferSize];
      bool connected, readSuccess;
      DWORD bytesRead;
      
      // thread loop
      for(;;)
      {
            hPipe = CreateNamedPipeA(
                              qPrintable(pipeName),
                              PIPE_ACCESS_INBOUND,
                              PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
                              1,
                              0,
                              bufferSize,
                              0,
                              NULL);
            
            if(hPipe == INVALID_HANDLE_VALUE)
                  break;
            
            connected = ConnectNamedPipe(hPipe, NULL) ?
                                    TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
            
            if(!connected)
            {
                  CloseHandle(hPipe);
                  continue;
            }
            
            readSuccess = ReadFile(
                                    hPipe,
                                    buffer,
                                    bufferSize,
                                    &bytesRead,
                                    NULL);
            buffer[bufferSize - 1] = 0;
            
            if(!readSuccess || !bytesRead)
            {
                  CloseHandle(hPipe);
                  continue;
            }
            
            QStringList argv = QManagedRequest::splitArguments(QString(buffer));
            const QString cmd = argv.at(0);
            
            // if server instance is going to suspend thread...
            if ( cmd == "--quit" ) {
                  CloseHandle(hPipe);
                  break;
            } else if ( cmd == "--request" ) {
                  argv.removeAt(0);
                  emit request(argv);
            } else {
                  emit message(QString(buffer));
            }
            
            CloseHandle(hPipe);
      }
}

/*!
      \internal
*/
void QInterProcessChannel::init()
{
      //while(isRunning())
      //    quit();
      
      // create global mutex to check single instance
      CreateMutexA(NULL, FALSE, qPrintable(globalMutexStr));
      serverMode = !(GetLastError() == ERROR_ALREADY_EXISTS);
      
      // start listening thread in single instance mode
      if(serverMode) start();
}

Generated by  Doxygen 1.6.0   Back to index