PDA

View Full Version : Terminating and restarting an external program in linux and windows



jiveaxe
17th January 2008, 11:14
Hi,
my program has now the autoupdate feature. It works in this manner. When the application starts it contacts the remote server, downloads the last release xml file and check for updates; if yes, it starts an external program (in linux: system("updater");) which asks if download the update and, after downloading, it closes the main program (in linux: system("killall mainapp");), installs the new files and restarts the main program (in linux: system("mainapp");) and closes itself.

1st question: is all this method ok? In linux it works but I don't know if there is a better way.

2nd question: how accomplish this in windows? how kill a running program and restart it?

Thanks

high_flyer
17th January 2008, 11:19
few threads (http://www.qtcentre.org/forum/search.php?searchid=218121)you might find interesting.

jiveaxe
17th January 2008, 11:33
few threads (http://www.qtcentre.org/forum/search.php?searchid=218121)you might find interesting.

Sorry, may you be more precise? You are advising me to use threads instead of my system? I did this way because the main program is using the files to be updated so I close it, install the new files and restart it. Using threads is safer?

Thanks

EDIT: only now I understand: threads=discussions.

Bytheway no one seems responding my questions.

high_flyer
17th January 2008, 11:38
Bytheway no one seems responding my questions.
I did! :)

-----------------------------------------------------------------

jiveaxe
17th January 2008, 12:57
No one of the discussions you suggested.

Regards

high_flyer
17th January 2008, 12:59
Well, I think several threads were dealing with very similar issues to yours.
But I guess that is a matter of opinion.

jiveaxe
17th January 2008, 17:44
Hi,
I have found here (http://www.physiology.wisc.edu/ravi/Software/killproc/) a program which does what i want. Attached to this post there is the source code.

I have downloaded and tested it on windows with mingw (with the command: g++ exam28.cpp): it compiles and works right.

After, I have tryed compiling the program with qmake && make but I got the following errors:


killwinproc.cpp: In function `int KILL_PROC_BY_NAME(const char*)':
killwinproc.cpp:138: error: cannot convert `char*' to `TCHAR*' in argument passing
killwinproc.cpp:223: error: cannot convert `WCHAR*' to `const char*' for argument `1' to `int strcmp(const char*, const char*)'

Why the same code and the same compiler gives errors with qmake && make?

Thanks

bender86
17th January 2008, 18:10
I wouldn't kill the application. I would find the way to make it quit gracefully.

You could do this way: when application starts, check if an update is available. If yes, launch updater (I would use QProcess, but I guess system() is fine) and quit. Meanwhile, updater asks user if download update files and downloads them. Main application should have all the time to quit*. Then updater installs update files, and reruns application. If user doesn't want to update, simply reruns application without download or install anything (after setted to not check for updates).

Else, you could use some inter process communication from updater to ask main application to quit. Something like DBus, mailslots, or even QTcpSocket.

Else, you could tell user to launch updater manually, after shut down main application.


* To get more time, you could launch updater when QCoreApplication::aboutToQuit() is emitted.

jiveaxe
18th January 2008, 17:19
Hi,
On Planet Source Code I have found this code (http://www.planet-source-code.com/vb/scripts/ShowCode.asp?txtCodeId=9617&lngWId=3). After removing some lines and added new ones to let it work with Qt this is the final working code.


#ifdef Q_OS_WIN32
void strcpy_wc(char *dest, wchar_t *src)
{
while (*src) {
*dest++ = *src++;
}
*dest = 0;
}

void MainWindow::killwinproc(const char *process_name)
{
DWORD Process_TID;
HANDLE ProcessHandle;
DWORD Reserved;
PROCESSENTRY32 proc;
HANDLE snapshot;
int gotime=0; //did we find'em?
proc.dwSize = sizeof(proc);
snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPALL,0);
Process32First(snapshot, &proc);

do {
// Find the Given Process
char dest[256];
strcpy_wc(dest, proc.szExeFile);
if(strcmp(dest,process_name)==0) {
Process_TID = proc.th32ProcessID;
gotime++;
}
} while (Process32Next(snapshot, &proc));
CloseHandle(snapshot);
// Get the Process's handle and blow it away
if(gotime>0) {
ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS | PROCESS_TERMINATE, FALSE, Process_TID);
TerminateProcess(ProcessHandle, (DWORD)0);
}
}
#endif


I wouldn't kill the application. I would find the way to make it quit gracefully.
...
Else, you could use some inter process communication from updater to ask main application to quit. Something like DBus, mailslots, or even QTcpSocket.


I agree with you that should be better closing it gracefully; the problem is that my application must run in windows too so dbus perhaps is not usable. Have you some link with simple example showing interprocess communication with QTcpSocket?

Regards

jpn
18th January 2008, 17:57
Why system(), why not QProcess?

jiveaxe
18th January 2008, 18:20
If I run the updater as a qprocess when the updater closes the main program to apply updates what appens to the updater? Will it continue running?

jpn
18th January 2008, 18:31
Well, you can always start it detached. However, I didn't read the thread very carefully. All this process handling just looked pretty complex to me...

jiveaxe
20th January 2008, 13:36
After a deep testing in windows I found my method (killwinproc(const char *process_name)) is not working very good. So as suggested I now use qprocess to start the updater. Now the problem is how send a signal from the updater to the main program to make it quit gracefully.

I don't have any code to post because I don't know which is the better way. Maybe a solution is QTcpSocket but I have never used it so a link to a simple code illustrating intercommunication process with this class should be very appreciated.

Thanks

bender86
20th January 2008, 14:11
Examples for QTcpSocket are here (http://doc.trolltech.com/4.3/network-fortuneserver.html) and here (http://doc.trolltech.com/4.3/network-fortuneclient.html).

Basically, main application will start a QTcpServer that listens on QHostAddress::LocalHost. When someone connects to this server, it emits a QTcpServer::newConnection signal. You can just quit now, or read incoming data for checking if quit request is from updater.

Client should just create a QTcpSocket, call QAbstractSocket::connectToHost. If main app checks incoming data, when connected sends quit request. When socket emits QAbstractSocket::disconnected() signal, it means that main application is quitting.

jpn
20th January 2008, 14:50
You can also use QAssistantClient as one example.

jiveaxe
20th January 2008, 16:38
Hi, I have read the suggested examples; this is the partial code.

In the main program I have added the following lines:


tcpServer = new QTcpServer(this);
if (!tcpServer->listen()) {
QMessageBox::critical(this, tr("Server"),
tr("Unable to start the server: %1.")
.arg(tcpServer->errorString()));
close();
return;
}

port = tcpServer->serverPort();

I call the updater this way:


QString program = "updater";
QStringList args;
args << port.toString();

myProcess = new QProcess(this);
myProcess->startDetached(program, args);

In the updater I added:


tcpSocket = new QTcpSocket(this);
tcpSocket->connectToHost("localhost", port.toInt());

and send the quit command this way:


void Updater::sendQuit()
{
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_0);
out << (quint16)0;
out << "quit";
out.device()->seek(0);
out << (quint16)(block.size() - sizeof(quint16));

tcpSocket->write(block);
tcpSocket->disconnectFromHost();
}

Now, how the server reads the incoming data? In the fortune client/server example the server has the following connection:


connect(tcpServer, SIGNAL(newConnection()), this, SLOT(sendFortune()));

and the function sendFortune() only sends data. Besides, the client reads data but uses as i/o device tcpSocket so perhaps I can't copy and paste its readFortune() code in main program.

Regards

bender86
20th January 2008, 17:20
You can use QDataStream or QTextStream directly with QAbstractSocket (because is a QIODevice).

void Updater::sendQuit()
{
QDataStream out(tcpSocket, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_0);
out << QString("quit");

tcpSocket->disconnectFromHost();
}

...

void MainApp::recvQuit()
{
QDataStream(int(tcpSocket,QIODevice::ReadOnly);
in.setVersion(QDataStream::Qt_4_0);
QString string;
in >> string;
if(string == QString("quit")) {
// Quit application.
QCoreApplication::instance()->quit();
}
}


Now, how the server reads the incoming data?
When you get a new connection on QTcpServer, call QIODevice::bytesAvailable on the socket: if returns > 0, simply read data, else connects its signal QIODevice::readyRead() to a slot where you read data.

jiveaxe
20th January 2008, 17:38
void MainApp::recvQuit()
{
QDataStream in(tcpSocket,QIODevice::ReadOnly);
in.setVersion(QDataStream::Qt_4_0);
QString string;
in >> string;
if(string == QString("quit")) {
// Quit application.
QCoreApplication::instance()->quit();
}
}

Is right connecting recvQuit() to QTcpServer::newConnection() signal?

In MainApp I haven't QTcpSocket object (only QTcpServer): I need to create one, right?

Thanks

bender86
20th January 2008, 19:46
Is right connecting recvQuit() to QTcpServer::newConnection() signal?
No, you should get new connection socket, and connect its readyRead signal to recvQuit.




In MainApp I haven't QTcpSocket object (only QTcpServer): I need to create one, right?
When QTcpServer::newConnection is emitted, you should do:

void MainApp::newConnectionSlot()
{
// Loop on all incoming connections.
while(tcpServer->hasPendingConnections()) {
QTcpSocket *socket = tcpServer->nextPendingConnection();
// Connect socket's signal, etc...
}
}

jiveaxe
21st January 2008, 09:13
Hi,
finally the problem is solved thanks to bender86 last post. This is the final code (if it may be useful to somebody else):


class MainApp: public QDialog
{
...
private slots:
void startUpdater();
void readIncomingData();
void newConnectionSlot();
...

private:
QProcess *myProcess;
QTcpServer *tcpServer;
QTcpSocket *tcpSocket;
...
};

MainApp::MainApp(QWidget *parent)
:QDialog(parent)
{
myProcess = 0;

...

tcpServer = new QTcpServer(this);
if (!tcpServer->listen()) {
QMessageBox::critical(this, tr("Server"),
tr("Unable to start the server: %1.")
.arg(tcpServer->errorString()));
close();
return;
}

connect(tcpServer, SIGNAL(newConnection()), this, SLOT(newConnectionSlot()));

...

connect(pushButton_1, SIGNAL(clicked()), this, SLOT(startUpdater()));

...
}

void MainApp::startUpdater()
{
QString program = "updater";
QStringList args;
args << port.toString();

...

myProcess = new QProcess(this);
myProcess->startDetached(program, args);
}

void MainApp::newConnectionSlot()
{
while(tcpServer->hasPendingConnections()) {
tcpSocket = tcpServer->nextPendingConnection();
connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(readIncomingData()));
}
}

void MainApp::readIncomingData()
{
...

QDataStream in(tcpSocket);
in.setVersion(QDataStream::Qt_4_0);
QString string;
in >> string;
if(string == QString("quit")) {
// Quit application.
QCoreApplication::instance()->quit();
}
}


class Updater: public QDialog
{
...
private slots:
void sendQuitCommand();
...

private:
QTcpSocket *tcpSocket;
...
};

Updater::Updater(char *p, QWidget *parent)
:QDialog(parent)
{
port = p;

...

tcpSocket = new QTcpSocket(this);
tcpSocket->connectToHost("localhost", port.toInt());

...
}

void Updater::sendQuitCommand()
{
QDataStream out(tcpSocket);
out.setVersion(QDataStream::Qt_4_0);
out << QString("quit");
tcpSocket->disconnectFromHost();
}

Regards