PDA

View Full Version : [C] Terminare processo dopo tot tempo


Tarrion
03-06-2010, 23:48
salve a tutti, sto sviluppando un piccolo .exe che svolga questa funzione:

-se un dato processo è in esecuzione da più di tot minuti lo termina;

sono riuscito a scrivere questo (per esempio: termina notepad.exe se è aperto da più di 10 min):


#include <stdio.h>
#include <stdlib.h>

int main(void)
{
system("taskkill /fi \"cputime gt 00:10:00\" /im notepad.exe /f");
return(0);
}


come potete notare ho usato il comando di dos taskkill.
Tutto viene compilato ed eseguito senza errori, ma ho dei dubbi su come venga calcolato quel cputime perché ho provato a fare una verifica settando il tempo a 10 secondi (per evitare di aspettare 10 minuti ogni volta):

-apro notepad.exe
-aspetto più di 10 secondi
-eseguo il programmino di cui sopra

il problema è che notepad non viene chiuso!
L'unica risposta che so darmi è che il cputime non coincide con il tempo trascorso dall'esecuzione di notepad.exe.
A questo punto che posso fare? Come risolvo/aggiro il problema?
Grazie per ogni eventuale aiuto.

fero86
04-06-2010, 00:11
partiamo dal presupposto che il tuo avatar é bellissimo!! :D :D

detto questo, ho provato sul mio PC ad eseguire quel comando taskkill nel prompt dei comandi e non funziona; credo di aver capito il motivo: quello che lui chiama "CPU time" non é il tempo per cui il programma é rimasto in esecuzione, bensi il tempo per cui il programma ha usato la CPU, che nel caso di Blocco Note é quasi zero (o comunque meno di un secondo) nella stragrande maggioranza dei casi visto quanto é semplice.

infatti poi ho provato a mettergli "CPUTIME ge 00:00:00" e ha funzionato.

non credo che il comando taskkill ti permetta di risolvere, io piuttosto userei le PSAPI in aggiunta a questa: http://msdn.microsoft.com/en-us/library/ms683223(VS.85).aspx

qui hai il reference delle PSAPI: http://msdn.microsoft.com/en-us/library/ms684894(v=VS.85).aspx

in particolare, per enumerare i processi usa questa: http://msdn.microsoft.com/en-us/library/ms682629(v=VS.85).aspx

e per conoscere il nome del modulo eseguibile usato per creare il processo (il file .exe, approssimativamente parlando) usa invece questa passando NULL al secondo parametro: http://msdn.microsoft.com/en-us/library/ms683196(v=VS.85).aspx

Tarrion
04-06-2010, 00:21
Grazie (anche per il complimento sull'avatar)! Domattina farò dei tentativi e posterò i risultati.

Tarrion
04-06-2010, 09:01
Molto bene, ho dato una bella spulciata a queste PSAPI e mi sembrano adatte all'occasione.
Unico problema: non le so usare!
La mia conoscenza di C si limita al primo corso di informatica alla facoltà di matematica, quindi non so maneggiare oggetti del genere.
Ho capito comunque che c'è un modo per far si che una di queste PSAPI restituisca un puntatore ad una struttura che contiene il valore del tempo di esecuzione in "user mode"; quello che resta da fare è implementarla nel mio codice in questo modo:

-trova il tempo di esecuzione del processo "nomeprocesso.exe";
-se è maggiore di 10 minuti terminalo;

come scrivo il punto 1? (io conosco già il nome dell'eseguibile, e solo quello)

edit: spulciando ancora ho trovato la funzione Process.GetProcessesByName(), può tornare utile?

Tarrion
05-06-2010, 09:06
Allora, dopo qualche altro giorno di ricerca ho capito che devo riuscire a ottenere l'handler del processo che mi interessa tramite il suo nome; ma francamente non ho idea di quale sia il codice da inserire per far eseguire questa funzione al mio programma.
Penso che non sia una cosa così difficile, ma vi giuro non so proprio da dove partire, per questo chiedo se gentilmete qualcuno può darmi una mano nella scrittura codice. Grazie.

clockover
05-06-2010, 09:36
Puoi mandare in esecuzione un alarm(secondi) e gestire il segnale in modo opportuno!
Es
{
signal(SIGALRM, sig_handler);//funzione del gestore del segnale
alarm(10);//dopo 10 secondi invio di un segnale SIGALRM
//altra roba.....
}

void sig_handler(){
kill(getpid(), SIGINT);
}

//e ovviamente puoi mettere un gestore per SIGINT

DanieleC88
05-06-2010, 11:13
Puoi mandare in esecuzione un alarm(secondi) e gestire il segnale in modo opportuno!
Es
{
signal(SIGALRM, sig_handler);//funzione del gestore del segnale
alarm(10);//dopo 10 secondi invio di un segnale SIGALRM
//altra roba.....
}

void sig_handler(){
kill(getpid(), SIGINT);
}

//e ovviamente puoi mettere un gestore per SIGINT

La signal() su Windows?

clockover
05-06-2010, 11:16
La signal() su Windows?

:stordita: :stordita: ehmm... sorry... piccolo rincoglionimento...

DanieleC88
05-06-2010, 11:29
Ho notato. :D

Tarrion
05-06-2010, 11:53
A me basterebbe anche scrivere una cosa così:

-ottieni handler per notepad (o-quello-che-è)
-passa handler alla funzione PSAPI GetProcessTimes per ottenere l'user time
-se l'user time è maggiore di 10 minuti (o-quello-che-è) [ammesso che l'user time rappresenti quello che mi serve], termina notepad
-fine

a questo punto penso che una volta ottenuto l'handler io sia in grado di scrivere il resto del codice (dovrei ancora ricordarmi come gestire i puntatori), ma il problema grosso è proprio, ripeto, il primo punto!

fero86
05-06-2010, 13:06
a questo punto penso che una volta ottenuto l'handler io sia in grado di scrivere il resto del codice una nota: per terminare il processo ci sono diverse maniere che ti elenco in ordine di brutalitá (l'ultima é la piu brutale e funziona per forza se il sistema di sicurezza lo concede):
1. prova ad inviare WM_CLOSE alla finestra principale
2. provare ad inviare WM_QUIT al thread primario
3. TerminateProcess



ma il problema grosso è proprio, ripeto, il primo punto! per trovare l'HANDLE del processo di notepad devi:
- enumerare i PID di tutti i processi usando EnumProcesses
- per ciascun PID devi invocare OpenProcess (che per alcuni PID potrebbe fallire)
- usare l'HANDLE eventualmente restituito da OpenProcess con GetModuleBaseName
- confrontare in maniera case-insensitive la stringa restituita da GetModuleBaseName con la stringa "notepad.exe" (occhio all'Unicode, usa la macro _T)

e alla fine chiudi l'HANDLE del processo con CloseHandle.

ovviamente potrebbero esserci piu istanze di notepad.exe in esecuzione, nel qual caso troveresti molteplici HANDLEs.

una nota importante: no, lo user time non é quello che intendi tu; quello é lo stesso tempo considerato dal comando taskkill, cioé il tempo per il quale i thread del processo hanno usato la CPU in modalitá utente; per la stragrande maggioranza del tempo che resta in esecuzione Blocco Note resta bloccato in attesa di input, quindi i suoi thread (anzi, il suo thread, perché ho proprio idea che ne abbia uno solo) non usano la CPU.

il tempo che devi considerare é il tempo di creazione del processo: devi sottrarlo alla data corrente e vedere se é maggiore della soglia da te stabilita. per trovare la data corrente puoi usare GetSystemTimeAsFileTime, che ti restituisce una data UTC in una struttura FILETIME (stesso formato usato dalla GetProcessTimes).

di seguito ti indico le pagine di riferimento delle funzioni che avevo tralasciato nel post precedente.
TerminateProcess: http://msdn.microsoft.com/en-us/library/ms686714(VS.85).aspx
CloseHandle: http://msdn.microsoft.com/en-us/library/ms724211(VS.85).aspx
OpenProcess: http://msdn.microsoft.com/en-us/library/ms684320(VS.85).aspx
GetSystemTimeAsFileTime: http://msdn.microsoft.com/en-us/library/ms724397(v=VS.85).aspx

dovrebbe essere tutto, non ti resta che provare...

fero86
05-06-2010, 13:09
(anzi, il suo thread, perché ho proprio idea che ne abbia uno solo) verificato ora con Spy++: ne ha uno solo :D

Tarrion
05-06-2010, 13:23
Ok, ora inizio i tentativi, ma avviso i gentili forumspettatori che mi addentro in mondi di C a me sconosciuti, quindi penso che a breve dovrò abusare ancora della vostra infinita pazienza e disposizione per altri aiuti.

Ad ogni modo grazie per tutto quello che mi hai detto finora, sei stato illuminante.

Tarrion
06-06-2010, 00:45
No, sentite, più mi perdo dentro queste PSAPI e più mi rendo conto che sono oggetti fuori dalla mia portata! Quando mi sono proposto di fare questo programmino non pensavo fosse necessaria una tale conoscenza di C (speravo di poter fare tutto passando dei comandi al prompt di DOS).

Ma è possibile che sia così difficile da dos ottenere almeno l'ora di creazione di un processo?

Mi dispiace veramente, ma non ho proprio il tempo di imparare ad usare le PSAPI; solo che questo programmino mi tornerebbe davvero utile e vorrei poter trovare una soluzione.

Tarrion
06-06-2010, 12:29
dopo qualche ora di sforzi, sono riuscito a sfornare una bozza di codice che dovrebbe fare quello di cui ho bisogno (il nome del processo da terminare è client.exe, il tempo minimo di esecuzione è 9 min e ho anche aggiunto un for nel caso ce ne siano due aperti):


#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
#include <ATLComTime.h>
#include <psapi.h>

int main(void)
{
int i=0;
HANDLE hProcess;
FILETIME ftCreation, ftExit, ftKernel, ftUser;
DWORD SetOfPID[2];
COleDateTime timeNow = GetCurrentTime(), timeMin(0,0,0,0,9,0);
COleDateTimeSpan timeDiff;

GetProcessID("client",SetOfPID);

for (i;i < 2; i++)
{
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, SetOfPID[i]);

GetProcessTimes(hProcess, &ftCreation, &ftExit, &ftKernel, &ftUser);

timeDiff = timeNow - timeCreation;


if(timeDiff > timeMin)
{
system("taskkill /im client.exe /f");
}


CloseHandle(hProcess);
}

return(0);
}


solo che il compilatore mi da un unico orribile errore:

-COleDateTime Undecleared

io ho incluso all'inizio ATLComTime.h, ma ho controllato delle directory di DEV e questo header non c'è... dove lo trovo?

Per il resto il codice vi sembra decente? Cosa dovrei correggere?

Tarrion
06-06-2010, 15:43
Ho risolto! (in realtà ho aggirato il problema)

Ho imparato in un'oretta qualche comando di base di Python e in 5 righe ho scritto il programma:

import psutil
import time
for pid in psutil.get_pid_list():
p = psutil.Process(pid)
if p.name == 'notepad.exe':
if time.time() - p.create_time >= 540:
p.kill()



Mi dispiace di non esserci riuscito con C, ma alla fine è il risultato che conta!

Grazie di tutto

fero86
06-06-2010, 15:47
per quale recondito motivo stai usando COleDateTime? :mbe:
quella é una classe, non la puoi usare in C. l'header ci sta, altrimenti il compilatore non te lo troverebbe e ti darebbe l'errore sull'#include anziché su COleDateTime; il motivo per cui ti dice che l'identificatore non é dichiarato é che evidentemente quell'header é portabile, cioé funziona anche in C, solo che in C non ti permette di importare le classi e quindi non ti fornisce nessuna COleDateTime.

che problema c'era a usare GetSystemTimeAsFileTime?

e quell'altra funzione GetProcessID é tua?

fero86
06-06-2010, 15:49
Ho risolto! (in realtà ho aggirato il problema)

Ho imparato in un'oretta qualche comando di base di Python e in 5 righe ho scritto il programma: ah... ok... :stordita:

calcola che questo thread é un win per cdimauro :asd:

Tarrion
06-06-2010, 15:56
Guarda, quel codice in c è nato da una specie di collage di pezzi trovati nella rete, non so bene cosa ho scritto, ma alla fine ho "risolto" così.
Dato che avevo abbastanza premura di completare questo programma (devo preparare due esami), sono contento di aver sistemato la faccenda anche se magari con un linguaggio brutto e meno potente, ma davvero non ho tempo per combattere con queste cose.
Ad ogni modo ti ringrazio per il tempo che mi hai dedicato e spero che questo topic possa essere utile anche a qualcunaltro più volenteroso di me!

fero86
06-06-2010, 16:48
non c'é di che, solo una cosa peró:

sono contento di aver sistemato la faccenda anche se magari con un linguaggio brutto e meno potente, ma davvero non ho tempo per combattere con queste cose. hai capito male, se c'é un linguaggio brutto e poco potente quello é proprio C. non sei l'unico a cui preme la produttivitá, anzi, preme a tutte le persone dotate di un minimo di cervello ;)