LE CRIME PARFAIT

Caribensila Messages postés 2527 Date d'inscription jeudi 15 janvier 2004 Statut Membre Dernière intervention 16 octobre 2019 - 3 mai 2010 à 23:45
cs_Forman Messages postés 600 Date d'inscription samedi 8 juin 2002 Statut Membre Dernière intervention 6 avril 2010 - 14 mai 2010 à 17:11
Cette discussion concerne un article du site. Pour la consulter dans son contexte d'origine, cliquez sur le lien ci-dessous.

https://codes-sources.commentcamarche.net/source/51723-le-crime-parfait

cs_Forman Messages postés 600 Date d'inscription samedi 8 juin 2002 Statut Membre Dernière intervention 6 avril 2010 1
14 mai 2010 à 17:11
Caribensila: il me semble qu'ici les PING sont ici exécutés séquentiellement, pas en parallèle (c'est même le but de la manœuvre!). Il n'y en a donc jamais plus d'un à la fois, ce qui ne devrait pas trop gêner le scheduler.
cs_Forman Messages postés 600 Date d'inscription samedi 8 juin 2002 Statut Membre Dernière intervention 6 avril 2010 1
14 mai 2010 à 17:09
MAURICIO: Peut-être que TASKLIST ne fonctionne pas pareil alors. Théoriquement, lorsque le processus n'existe pas, la commande renvoie 1 seule ligne de texte. Sinon, elle en renvoie plusieurs (3 il me semble). Mon fichier BAT compte le nombre de sauts de lignes renvoyés par TASKLIST: s'il y en a plus de zéro (processus "running"), il réessaie jusqu'à ce qu'il n'en reste plus (processus "stopped").
Caribensila Messages postés 2527 Date d'inscription jeudi 15 janvier 2004 Statut Membre Dernière intervention 16 octobre 2019 18
14 mai 2010 à 16:12
Salut,


Testé sous XP :

l:=TStringList.Create;
try
l.Add('@echo off');
l.Add(':loop');
l.Add('del "%cd%\' + ExtractFileName(Application.Exename) + '"');
l.Add('if exist "%cd%\' + ExtractFileName(Application.Exename) + '" goto :loop');
l.Add('del "%cd%\MrPropre.bat"');
l.SaveToFile('MrPropre.bat');
ShellExecute(0,nil,'MrPropre.bat',0,nil,0);
finally
l.Destroy;

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

Pour ce qui est de la temporisation avec le PING, je me demande si ce ne serait pas une fausse bonne idée, finalement.
Car, sauf erreur, un PING crée un process. Et on ne fait que remplacer une suite de messages système par une suite de création de process.
Et je ne suis pas sûr que le Scheduler Windows apprécie tant que ça.

La meilleure solution, je crois, c'est de créer le .bat au tout dernier moment.
cs_MAURICIO Messages postés 2106 Date d'inscription mardi 10 décembre 2002 Statut Modérateur Dernière intervention 15 décembre 2014 5
14 mai 2010 à 14:36
Le code du fichier bat tourne en boucle :)
Cirec Messages postés 3833 Date d'inscription vendredi 23 juillet 2004 Statut Modérateur Dernière intervention 18 septembre 2022 50
14 mai 2010 à 14:27
euh ...
{$EXTERNALSYM SW_HIDE}
SW_HIDE = 0;
{$EXTERNALSYM SW_SHOWNORMAL}
SW_SHOWNORMAL = 1;

;)
cs_Forman Messages postés 600 Date d'inscription samedi 8 juin 2002 Statut Membre Dernière intervention 6 avril 2010 1
14 mai 2010 à 14:05
Merci d'avoir regardé. Peut-être que TASKLIST fonctionne différemment sous Vista...

En remplaçant SW_HIDE par 0, la fenêtre de console devrait être affichée et donner plus de détails, si tu as le temps.
cs_MAURICIO Messages postés 2106 Date d'inscription mardi 10 décembre 2002 Statut Modérateur Dernière intervention 15 décembre 2014 5
14 mai 2010 à 13:11
Sous Windows Vista Ultimate et "en dehors" de Delphi:

le fichier *.bat est bien crée au démarrage de l' appli.
Cependant, celui-ci ainsi que l' exe ne s' éliminent pas en quittant l' appli.
Si je clique sur le bat, uniquement celui-ci est éliminé.

Après avoir testé l' execution du code en fin d' execution de l' appli, j' obtients les mêmes résultats.

Maurício
cs_Forman Messages postés 600 Date d'inscription samedi 8 juin 2002 Statut Membre Dernière intervention 6 avril 2010 1
14 mai 2010 à 13:00
Au fait si quelqu'un peut tester mon code plus haut sous d'autres versions de Windows que XP, ça m'intéresse.
cs_Forman Messages postés 600 Date d'inscription samedi 8 juin 2002 Statut Membre Dernière intervention 6 avril 2010 1
13 mai 2010 à 05:54
Je me permets d'ajouter ma solution. Ne laisse aucune trace et fait briller, quel que soit le temps mis par le processus pour se terminer (testé sous WinXP). MonsieurPropre peut être lancé n'importe quand (par exemple au démarrage du programme, ou dans l'événement OnCreate d'une fiche).

Le PING n'est pas vraiment nécessaire, il sert uniquement à éviter de trop gâcher du temps machine.

procedure MonsieurPropre;
var
l:TStringList;
begin
l:=TStringList.Create;
try
l.Add(':LOOP');
l.Add('PING 127.0.0.1 -n 2 -w 1000');
l.Add('SET N=0');
l.Add('FOR /F "delims=" %%i IN (''TASKLIST /FI "IMAGENAME EQ %1"'') DO SET /a N+=1');
l.Add('IF %N% NEQ 0 (GOTO LOOP)');
l.Add('DEL %1');
l.Add('DEL %0');
l.SaveToFile('MrPropre.bat');
ShellExecute(0,nil,'MrPropre.bat',PChar(ExtractFileName(Application.Exename)+#0),nil,SW_HIDE);
finally
l.Destroy;
end;
end;
cs_Jean_Jean Messages postés 615 Date d'inscription dimanche 13 août 2006 Statut Membre Dernière intervention 13 décembre 2018 3
6 mai 2010 à 09:09
Génial, merci à tous
Cirec Messages postés 3833 Date d'inscription vendredi 23 juillet 2004 Statut Modérateur Dernière intervention 18 septembre 2022 50
4 mai 2010 à 19:00
Salut Mauricio,

tu ajoutes une section "finalization" à ta unit3.pas avec un "Sleep(5000);" et tu testes à nouveau ;)

tu verras que l'application résiste ... cf. mon commentaire du 04/05/2010 18:27:26
cs_MAURICIO Messages postés 2106 Date d'inscription mardi 10 décembre 2002 Statut Modérateur Dernière intervention 15 décembre 2014 5
4 mai 2010 à 18:50
Salut à tous!

Qui a dit que je ne me pointe que quand il y a du sang? C' est pas vrai d' ailleurs !!!

Mes sentiments après consommation:
Très légère, mince et sans rien vêtue (même pas une petite form), elle n' attends rien et ne reçoit rien non plus, en voilà une qui va direct à ces instincts basiques!
Elle ne fait que s' éclipser sans laisser de trâce (ce qui n' est pas pour déplaire) mais laisse entrevoir tout son potentiel pour une utilisation plus adéquat si elle veut bien se laisser faire ... et elle laisse!
Donc, pour les plus grosses:
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.Title := 'Cindy WOL';
Application.CreateForm(TForm3, Form3);
Application.Run;

ShellExecute(0, // Le crime parfait d'après Cirec. Thanks to him ! ;)
nil,
'CMD' ,
'/K "del wol.exe&&Exit"',
nil ,
0);

Maurício
Cirec Messages postés 3833 Date d'inscription vendredi 23 juillet 2004 Statut Modérateur Dernière intervention 18 septembre 2022 50
4 mai 2010 à 18:38
oups
la fenêtre DOS devait être visible à cause du switch /C au lieu de /K ... problème résolu :
ShellExecute(0, // Le crime parfait d'après Cirec. Thanks to him ! ;)
nil,
'CMD' ,
PChar('/K "for /L %I IN (1 1 10000) DO if EXIST '+GetCommandLine+' (del '+GetCommandLine+') ELSE exit"'),
nil ,
0);
Cirec Messages postés 3833 Date d'inscription vendredi 23 juillet 2004 Statut Modérateur Dernière intervention 18 septembre 2022 50
4 mai 2010 à 18:27
Hello,

cette pirouette est prévue, à la base, pour supprimer un petit prog (- 100ko) qui fait le ménage où toute autre action. Dans ces conditions ça fonctionne à tous les coups ... maintenant sur une application plus "grosse" si l'instruction est bien placée elle a 80% de chances de réussir (au bas mot)
mais l'important c'est pas la taille de l'application mais la façon de programmer ... effectivement si l'on place des instructions "gourmandes" dans les sections "finalization" ça peut/va poser un problème ... il faut donc en tenir compte ... et si on ne peut pas on peut encore modifier l'instruction sur les bases de la proposition de RT15 :

ShellExecute(0, // Le crime parfait d'après Cirec. Thanks to him ! ;)
nil,
'CMD' ,
PChar('/C "for /L %I IN (1 1 10000) DO if EXIST '+GetCommandLine+' (del '+GetCommandLine+') ELSE exit"'),
nil ,
SW_SHOWNORMAL);

inconvénient:
il faut que la fenêtre dos soit visible sinon la boucle n'est pas exécuté ;)
Caribensila Messages postés 2527 Date d'inscription jeudi 15 janvier 2004 Statut Membre Dernière intervention 16 octobre 2019 18
4 mai 2010 à 18:08
Effectivement, j'avais mal compris ton code :$
J'ai pas l'habitude. Excuse moi.

Ben... c'est nickel, alors ! :)

Je te remercie beaucoup pour tout le temps que tu as passé sur ce truc.
cs_rt15 Messages postés 3874 Date d'inscription mardi 8 mars 2005 Statut Modérateur Dernière intervention 7 novembre 2014 13
4 mai 2010 à 17:44
Bin ton .bat semble marcher à part ça. Un goto :loop serait peut être un rien plus esthétique mais à part ça...

Je crois que tu as lu mon code comme ça en fait :

for i:=0 to 1000000 do
begin
Sleep(500);
end;
if FileExists('Project1.exe') then
DeleteFile('Project1.exe')
else
Application.Terminate;

Mais non non, je n'ai pas été sale à ce point là !
cs_rt15 Messages postés 3874 Date d'inscription mardi 8 mars 2005 Statut Modérateur Dernière intervention 7 novembre 2014 13
4 mai 2010 à 17:26
Bin mon code est une boucle. Qui s'arrête au bout d'un certain temps (Quelque chose comme 138 heures), effectivement, mais une boucle !

En gros c'est :

for i:=0 to 1000000 do
begin
Sleep(500);
if FileExists('Project1.exe') then
DeleteFile('Project1.exe')
else
Application.Terminate;
end;

Je vais jetter un oeil sur ton .bat, mais déjà c'est exist et non existS (Le DOS et ses méandres...). Mais c'est dommage de faire un .bat !
Caribensila Messages postés 2527 Date d'inscription jeudi 15 janvier 2004 Statut Membre Dernière intervention 16 octobre 2019 18
4 mai 2010 à 17:13
Oué. Si je comprends bien, c'est plus sûr en effet, mais c'est pas beaucoup plus rigoureux, je pense.
Plutôt qu'une temporisation, je préférerais un truc du genre boucle.
Comme dans le .bat que j'avais essayé de mettre au point, en vain :

@echo off
:loop
del "%cd%\Project1.exe"
if exists "%cd%\Project1.exe" goto loop
del "%cd%\suicide.bat" <-- Ne s'exécute pas ! ! !

"Project1.exe" est bien détruit à sa fermeture, mais le "suicide.bat" s'accroche à la vie :)
Je ne comprends pas pourquoi la dernière commande ne s'exécute pas...
cs_rt15 Messages postés 3874 Date d'inscription mardi 8 mars 2005 Statut Modérateur Dernière intervention 7 novembre 2014 13
4 mai 2010 à 16:25
Si on trace dans le "end." on passe dans des fonctions genre :
FinalizeUnits
Finalization
Close
UninitAllocator
UnregisterModule
NotifyModuleUnload
UnsetExceptionHandler

Avec ton code, en Delphi6, on totalise 362 instructions (Comptées avec la commande wt de ntsd, http://msdn.microsoft.com/en-us/library/ff561497(VS.85).aspx) entre l'instruction suivant ShellExecute et l'appel à ExitProcess (Fonction d'arrêt du processus de Windows qui ne revient jamais à l'appelant).

Avec une section de finalization, ce score tombe à 314 instructions. Bon ça doit être plus important sur une grosse appli, mais ça reste un chiffre magnifiquement petit (A comparer 16350 instructions + 3 appels systèmes pour un Hello world en C + runtime fournie avec gcc).

Donc en fin de compte, la partie Delphi est tout à fait négligeable par rapport à ExitProcess qui doit se chiffrer en millions d'instructions au bas mot.

Donc ce n'est en effet pas très rigoureux : qui de ExitProcess ou de cmd sera le plus rapide ?

Que pense tu de faire attendre un peu cmd dans le cas ou un premier del échouerait (Comme sur l'exemple du batch de mon lien) ?

Avec le code suivant, le fichier est supprimé même avec un gros Sleep juste après le ShellExecute.
====================
ShellExecute(0, // Le crime parfait d'après Cirec. Thanks to him ! ;)
nil,
'CMD' ,
'/K "for /L %I IN (1 1 1000000) DO ping -n 2 127.0.0.1 > NUL && if EXIST Project1.exe (del Project1.exe) ELSE exit"',
nil ,
0);
====================
Caribensila Messages postés 2527 Date d'inscription jeudi 15 janvier 2004 Statut Membre Dernière intervention 16 octobre 2019 18
4 mai 2010 à 14:52
Merci JiHelB :)

Bonjour RT15,
Je te remercie pour ces observations très intéressantes et je vais regarder de près le lien que tu nous proposes.

Cependant, une petite réflexion a priori :
si nous avons affaire à un gros bouffi, on peut imaginer qu'il dispose des sections "Initialization" et "Finalization".
Et dans ce cas, on pourrait très bien mettre la ligne du code de Cirec à la fin ce cette section "Finalization".
L'action du ShellExecvute étant effectuée après la libération de toutes les ressources (sauf erreur), le del interviendra toujours quand le .exe ne sera plus en cours d'utilisation.
Non ?

Il reste que ce n'est quand même pas très rigoureux cette histoire... :$
jihelb Messages postés 49 Date d'inscription lundi 27 janvier 2003 Statut Membre Dernière intervention 24 mars 2017
4 mai 2010 à 10:52
FABULEUX !!! Exactement ce qu'il nous fallait. :)
Du coup, j'ai modifié mon code en conséquence.
Je met 10/10.
Cordialement,
JiHelB
cs_rt15 Messages postés 3874 Date d'inscription mardi 8 mars 2005 Statut Modérateur Dernière intervention 7 novembre 2014 13
4 mai 2010 à 10:05
Salut Cari,

Cette méthode fonctionne bien sur un petit processus chétif. En revanche un gros processus bouffi a des chances de s'en sortir. Il y a en effet une "race condition".

ShellExecute est appelé et va lancé cmd en lui demandant d'exécuter une commande. ShellExecute n'est pas bloquant et ne va pas attendre la fin du processus lancé pour rendre la main à l'appelant. Si ShellExecute était bloquant, cela ne fonctionnerait pas du tout : del essaierait de supprimer le .exe alors qu'il est en cours d'utilisation.

Mais qui a-t-il après ShellExecute ? "end." Et le temps d'exécution de ce petit "end." n'est pas forcément ridicule, car c'est la que Delphi et Windows vont libérer un certain nombre de ressources. Et j'imagine que sur un gros processus, ce "end." prend un certain temps.

Et le problème est là : s'il prend plus de temps que le temps que met cmd.exe à se lancer, alors le .exe est toujours en cours d'utilisation au moment de l'appel de del. Donc le .exe survit.

Ca se vérifie avec ce genre de code ou on allonge artificiellement la durée de "end.".
===========================================
program Project1;

uses
ShellAPI, Windows;

begin
ShellAbout (0,
'cette petite application de 14Ko',
'Elle ne sert strictement à rien ( pffff... )' + #13#10 +
'Donc, judicieusement, elle disparaîtra ! ;) Caribensila',
0);

ShellExecute(0, // Le crime parfait d'après Cirec. Thanks to him ! ;)
nil,
'CMD' ,
'/K "del Project1.exe&&Exit"',
nil ,
0);
Sleep(5000);
end.
===========================================

Pour plus d'info sur l'auto suppression, il y a par exemple cette page :
http://www.catch22.net/tuts/selfdel

Mais aucune des solutions proposées ne semble parfaite...

La solution du batch n'est pas trop mal. Pour limiter la consommation CPU, on peut tenter de mettre ça dans la boucle :
ping -n 2 127.0.0.1 > NUL

La méthode avec COMSPEC est la même que la tienne, sauf qu'il récupère COMSPEC qui contient cmd ou command.com sur les antiquité.

Le DELETE_ON_CLOSE a l'air pas mal.

Les codes en assembleur ne sont pas très propres. Ils ne libèrent pas les ressources de la runtime C. Cela revient à appeler ExitProcess ou Halt dans du Delphi, sans passer par le "end." final ou par Terminate. Ces méthode fonctionnerait par contre convenablement sur des .exe sans runtime C (C'est à dire entre autre sans le "main" classique). L'utilisation de rundll32 est cependant une belle trouvaille.

Quand au méthode par injection de code, ça reste quand même très sale, pas le genre de truc que l'on peut embarqué dans une application sérieuse...
cs_Alain Proviste Messages postés 908 Date d'inscription jeudi 26 juillet 2001 Statut Modérateur Dernière intervention 1 février 2015 2
4 mai 2010 à 09:52
J'accuse le colonel moutarde dans la salle de bain avec le chandelier.
Rejoignez-nous