Vous savez peut-être que Windows est un système d'exploitation assez répandu.

Et j'ai dû me poser récemment la question : comment écrire des scripts sous Windows ? Deux manières traditionnelles se complètent assez bien. Cmd d'une part, héritier direct de command.com, pour les tâches simples, voire simplistes vu sa grammaire et son vocabulaire terriblement limités ; et VBS, descendant de Visual Basic, pour accéder aux API Microsoft et véritablement programmer (mais sa syntaxe me fait vomir). On peut aussi citer la pléiade d'outils .Net ainsi que Power Shell mais ils ne sont installés que sur des versions récentes de Windows.

Il existe un autre langage de script pour Windows, JScript, dérivé de Javascript alias ECMAscript. Méconnu, peu documenté, déviant de la « norme » par de nombreux points, il s'agit pourtant d'un vrai langage, lisible, dont la microsoftisation permet d'exploiter les ressources du système sous toutes ses versions depuis XP. Il souffre de maux bizarres parfois, il faut se faire à ses spécificités, mais on arrive presque toujours à ses fins.

Par exemple, pour lire un fichier, il faut d'abord instancier un objet permettant de discuter avec le système de fichiers.

var fso = new ActiveXObject ("Scripting.FileSystemObject");

On peut ensuite ouvrir le fichier, y lire des lignes de texte et le fermer.

fi = fso.OpenTextFile (input, 1);
while (!fi.AtEndOfStream) {
  text = fi.ReadLine();
}
fi.close();

On ne peut exploiter que des données texte. Pas de binaire, ou alors par des biais horribles. Les explications d'Eric Lippert sur son blog MSDN sont tellement absurdes (cf. son deuxième commentaire) qu'on a peine à croire que des développeurs aussi capables puissent se fourvoyer à ce point. Voilà donc un des maux bizarres dont je parlais plus haut.

Consultez MSDN pour la liste des méthodes disponibles pour FileSystemObject, ainsi que pour les autres API. Parfois, les exemples ne sont fournis que pour VBScript, C++ et C#, JScript étant considéré comme un langage de deuxième ordre pour Microsoft. À l'utilisateur de chercher ces exemples sur Google.

Autre objet utile, WScript.Shell.

var shl = new ActiveXObject ("WScript.Shell");

shl.run ("msinfo32.exe /report msinfo.txt");
WScript.Echo ("%TEMP%=" + shl.ExpandEnvironmentStrings ("%TEMP%"));
WScript.Echo ("Desktop=" + shl.SpecialFolders ("Desktop"));

On peut même utiliser WMI (Windows Management Instrumentation) pour, entre autres, lire les Event Log de Windows.

var wmi = GetObject ("winmgmts:{impersonationLevel=impersonate,(Backup)}!\\\\.\\root\\CIMV2");

var evts = wmi.ExecQuery 
            ("select * from Win32_NTEventLogFile 
                      where LogFileName like 'App%' 
                         or LogFileName like 'Sys%'");
var evt = new Enumerator (evts);
for (evt.moveFirst(); !evt.atEnd(); evt.moveNext()) {
  var evtName = evt.item().LogFileName;
  var strBackupLog = evt.item().BackupEventLog (evtName+".evt")
}

Il n'y a pas de mécanisme à la #include. C'est historique : les fichiers Javascript sont généralement lus depuis une page HTML. Et puis VBS ne connaît pas non plus ce mécanisme, alors... alors on se débrouille avec eval().

eval ((new ActiveXObject 
  ("Scripting.FileSystemObject")).OpenTextFile ("lib.js",1).ReadAll ());

On peut même se payer le luxe d'une inclusion conditionnelle. Il suffit de définir un objet dans lib.js

var lib = new Object ();

et de faire précéder le eval() d'un test

if (typeof lib == "undefined")
eval ((new ActiveXObject 
  ("Scripting.FileSystemObject")).OpenTextFile ("lib.js",1).ReadAll ());

ce qui permet d'imbriquer les inclusions sans se poser de question.

Les méthodes et les attributs présents dans le fichier lib.js seront directement ajoutés au namespace courant, implicitement self, sauf si on précise un namespace différent, comme dans la déclaration

function lib.print (str) {

mais, là, on sort du domaine du scripting pour entrer dans celui de la programmation objet plus traditionnelle et JScript n'est peut-être plus le langage à employer.

On peut envoyer un mail :

var msg  = new ActiveXObject ("CDO.Message");   
var cfg  = new ActiveXObject ("CDO.Configuration");

msg.Subject = "Test";
msg.From = "me@example.com";
msg.To = "you@example.com";
msg.TextBody = "Hello world";

msg.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2;   
msg.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/smtpserver") = "smtp.example.com");
msg.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25;
msg.Configuration.Fields.Update ();   

msg.Send();

Et lire ou écrire une clef de registre :

var shl = WScript.CreateObject ("WScript.Shell");

var bKey = shl.RegRead ("HKCU\\Software\\SimonTatham\\PuTTY\\Sessions\\wfl\\BellOverloadS");

Dernier luxe par rapport à VBS et son pathétique On Error, JScript peut gérer les erreurs par un mécanisme de try/catch :

try {
  fso.CopyFile (from, to);
  WScript.Echo ("Ok");
}
catch (e) {
  WScript.Echo ("KO - " + e);
}

Aha, j'oubliais, comment lance-t-on un programme ? La commande wscript, celle qui est lancée par défaut quand on invoque directement un programme en .js, affiche les WScript.Echo() dans des petis dialogues, alors que cscript lance le script en mode console, idéal pour logger efficacement ce qui se passe ou pour rediriger les sorties console vers NUL:.

Les moyens de debug sont extrêmement limités ; restent WScript.Echo() et, pour les problèmes vraiment incompréhensibles, les Sysinternals.

NB : Les exemples de code sont mal affichés mais un copier-coller restitue le formatage d'origine.