Créer son application Cocoa: 3ème partie

( 4 Votes )


Une autre Information qui pourrait servir, est de savoir si le Kext a été chargé par le système ou non au démarrage.

En effet, si votre extensions n’est pas chargé, cela peut dire qu’elle a été mal installée ou inutile.


Classes Abordées dans cette partie:

NSTask

NSPipe

NSFileHandle

NSImage

NSBundle

NSData

NSString

NSArray


Pour connaître les extensions chargées, il existe une commande terminal très simple:


kextstat


Si vous souhaitez savoir si une extension en particulier est chargée ou non, il fut indiquer à kextstat l’Identifier du kext, de cette manière:


kextstat -b org.netkas.HPETDevice


Vous pouvez faire le test avec un kext que vous possédez, le résultat devrait s’afficher sous cette forme:


Index Refs Address Size Wired Name (Version) <Linked Against>

34 0 0x46855000 0x2000 0x1000 org.netkas.HPETDevice (1) <13 7 4 3>



Vous allez me dire: et alors, qu’est -ce le terminal à a voir avec Objective-C?

Et bien messieurs, sachez qu’il est possible de lancer ce genre de commande dans Objective-C, grâce à la classe NSTask

Si lancer cette commande est une chose, il faut également être capable de la lire, et d’en traiter les résultats.


Pour cela, nous avons à disposition les classes NSPipe et NSFileHandle, qui respectivement permettent de créer un «tuyau» de communication entre kextstat et notre application, et de lire le résultat de la commande avec votre application.


Voilà, les bases sont posées, voyons maintenant comment nous allons procéder pour communiquer avec kextstat.

Comme vous savez maintenant le faire, il faudra créer une fonction dans MyController.m, et la déclarer dans MyController.h.

Voici la fonction:


- (void) ifLoaded

{

//initialisation

NSTask *kextStat=[[NSTask alloc] init];

NSPipe *pipe=[[NSPipe alloc] init];

// configuration

[kextStat setLaunchPath:@"/usr/sbin/kextstat"];

[kextStat setArguments:[NSArray arrayWithObjects:@"-l", @"-b", self.theIdentifier, nil]];

[kextStat setStandardOutput:pipe];

NSFileHandle *handle = [pipe fileHandleForReading];

[kextStat launch];


// notre code va ici


}


Initialisation:

Comme nous l’avons déjà vu, ici nous créons nos objets, afin de pouvoir les utiliser par la suite.


Configuration:

Ici, nous indiquons 3 choses à NSTask.


1) [kextStat setLaunchPath:@"/usr/sbin/kextstat"];

Le chemin vers l’application kextstat.


2) [kextStat setArguments:[NSArray arrayWithObjects:@"-l", @"-b", self.theIdentifier, nil]];

les arguments qui viennent après kextstat. Ici nous utilisons un Array, dans lequel nous stockons à la suite les instructions à donner à kextstat. Notez qu’un array que vous définissez dans votre code doit toujours se terminer par nil, qui permet de dire que l’Array se termine. Si nous devions écrire ces deux premiers points dans un terminal, cela ressemblerait à ça:


kextstat -l -b identifier-du-kext


Le -l nous permet de supprimer la ligne Index Refs Address..etc pour ne garder que le résultat du kextstat. Vous allez comprendre pourquoi un peu plus loin.



1) [kextStat setStandardOutput:pipe];

Nous disons à NSTask d'utiliser notre «tuyau» pour faire circuler les informations.


2) NSFileHandle *handle = [pipe fileHandleForReading];

Là, nous disons à NSFileHandler de lire les informations qui circulent dans notre tuyau.


3) [kextStat launch];

On exécute notre NSTask



Kextstat ne se préoccupe que des kexts chargés. Si vous lancez kextstat avec un identifier de kext qui n’est pas chargé par le système, alors rien ne s’affiche. Je vous invite à essayer.


Pour les possesseurs d’un carte graphique Nvidia, ceci ne retournera rien (on cherche un pilote ATI):

kextstat -l -b com.apple.kext.ATIFramebuffer


Pour les possesseurs d’une carte graphique ATI (on cherche un pilote Nvidia):

kextstat -l -b com.apple.NVDAResman



Bon, vous devez me voir venir avec mes gros sabots: maintenant que nous savons repérer si un kext est chargé ou non, il ne reste plus qu’à afficher dans notre application «kext chargé» si quelque chose est trouvé, ou «kext non chargé» si rien n’est trouvé.


Pour faire ça, nous allons faire ce que nous savons faire, c’est à dire créer NSString (que l’on nomme kextLoad) que l’on va «binder» à un Text Field dans l’interface.

Vous savez comment faire ça maintenant, alors voici la fonction dans sa totalité:


- (void) ifLoaded

{

//initialisation

NSTask *kextStat=[[NSTask alloc] init];

NSPipe *pipe=[[NSPipe alloc] init];

// configuration

[kextStat setLaunchPath:@"/usr/sbin/kextstat"];

[kextStat setArguments:[NSArray arrayWithObjects:@"-l",@"-b", self.theIdentifier,nil]];

[kextStat setStandardOutput:pipe];

NSFileHandle *handle = [pipe fileHandleForReading];

[kextStat launch];

// on stocke ce qui sort

NSData *outPut = [handle readDataToEndOfFile];

// on affiche selon le résultat

if ([outPut length]<1) {

self.kextLoad = @"kext non chargé";

}

else {

self.kextLoad = @"kext chargé";

}

}



Nous avons deux choses un peu nouvelles ici:


1) NSData *outPut = [handle readDataToEndOfFile];

Le résultat est sous la forme de data, donc on stocke ce résultat dans un NSData pour effectuer notre vérification par la suite



2)if ([outPut length] < 1) {

Nous avons vu qu’avec notre commande, si le kext n’est pas chargé, rien ne s’affiche. Alors nous vérifions avec notre if si outPut contient quelque chose, lenght correspondant aux nombres de données stockés. On utilise le même comparateur que dans la partie 2 du ce tutoriel, qui revient à dire «si la longueur de outPut est inférieure à 1, alors...»


Maintenant il faut appeler cette fonction dans la bonne partie du code de MyController.m

Vous avez deux possiblités:


- soit faire comme avec - (void) getInformations et appeler - (void) ifLoaded aux mêmes endroits,

- soit appeler - (void) ifLoaded directement dans - (void) getInformations


Je penche pour la deuxième solution, car on économise une ligne de code... On l'appellera ici:


self.theCount = [NSString stringWithFormat:@"%i %@", [filenames count], pluriel];

[self ifLoaded];



Voilà, vous n’avez plus qu’a créer votre Text Field dans Interface Builder et e binded avec self.kextload, et compiler pour tester le résultat!



Bon, je ne sais pas pour vous, mais je trouve qu’uniquement du texte, c'est pas très User-Friendly...

Je verrais bien des images qui illustrent le fait qu’un kext soit chargé ou non... alors je vous invite à faire de même :)

récupérez les images en pièces jointes. Se sont celles que j’utilise, mais vous pouvez utiliser celles de votre choix bien sur.



Pour ajouter des images à votre application, faites glissez vos images dans le dossier «Resources» de la fenêtre de votre projet Xcode, et cochez la case «copy items into destination group’s folder» de la boite de dialogue:



Retournez à votre fonction - (void) ifLoaded

Dans «Initialisation», ajoutez deux lignes comme ceci:

//initialisation

NSTask *kextStat=[[NSTask alloc] init];

NSPipe *pipe=[[NSPipe alloc] init];

NSString *loaded = [[NSBundle mainBundle] pathForResource:@"info" ofType:@"png"];

NSString *notLoaded = [[NSBundle mainBundle] pathForResource:@"warning" ofType:@"png"];



On a crée deux strings qui contiennent les chemins vers vos images.

Maintenant, vous devez créer un NSImage qui sera «bindé». La technique est exactement la même que celle utilisé pour les NSString que nous avons fait précédemment, sauf qu’il faut remplacer NSString par NSImage.

il vous faudra donc ces 3 éléments à leurs places respectives:


NSImage *kextState;

@property (copy, nonatomic) NSImage *kextState;

@synthesize kextState;


Toujours dans la fonction - (void) ifLoaded, vous modifiez votre if / else comme ceci:


if ([outPut length]<1) {

self.kextLoad = @"kext non chargé";

self.kextState = [[NSImage alloc] initWithContentsOfFile:notLoaded];

}

else {

self.kextLoad = @"kext chargé";

self.kextState = [[NSImage alloc] initWithContentsOfFile:loaded];

}



Dernière étape, ajouter de quoi afficher l’image dans l’interface.


A partir de la Librarie d’Interface Builder, faites glisser une «Image Well» dans la fenêtre de l’application.

Ensuite, cliquez dessus et configurez l’onglet binding de l’Inspecteur de cette manière:



Voilà, sauvegardez le tout et compilez, votre application est terminée!! Enfin presque...


TP: Faire disparaitre le texte et l’image quand la liste est Vide


Et oui, nous sommes confrontés au même souci que dans la deuxième partie. Mais comme la solution a déjà été donnée, donc je vous laisse trouver tout seul modifier votre code.


Conclusion du Tutoriel:


Voilà, j’espère que vous aurez réussi à faire toutes les parties de ce tutoriel. Sinon, n’hésitez pas à relire plusieurs fois, fouiller un peu dans la documentation, expérimenter... le but n’était pas de vous apprendre Objective--C, mais plutôt de mettre un pied à l'étrier et voir si ça vous plait...

Mais vous avez du faire quand même de sacrés progrès: regardez votre code maintenant, et dite vous qu’avant d’avoir suivit ce tutoriel, tout ceci était du Chinois :)


Ainsi, considérez cette petite application que nous avons fait ensemble comme un point de départ: en effet, vous pouvez y ajouter de nombreuses fonctionnalités, améliorations, comme par exemple créer un indicateur d’activité, afficher si le kext est compatible 64bit, supprimer de la liste les kexts qui ne sont pas chargés, voir installer/ désinstaller des kexts qui se trouvent dans la liste... etc.


Pour ceux qui auront apporté des améliorations, je vous invite les poster, cela pourrait intéresser d’autres personnes, et pourquoi pas au final une application collective verra le jour....

Télécharger les icônes Télécharger le projet
 

Commentaires 

 
0 #1 30-11-2009 16:41
Un grand merci pour ce tutoriel.
Citer
 
 
0 #2 01-12-2009 09:09
Connaissant très mal MacosX, je vais peut-être dire une bétise mais il semblerait néanmoins que certains extensions ne restent pas chargés mais sont néanmoins indispensables au bon fonctionnement du système.

C'est le cas par exemple du kext suivant :

/Extra/Extensions/ElliottForceLeg acyRTC.kext

Ce Kext n'apparait pas dans kextstat et est donc considéré comme non chargé par l'application MyosxKexts.

En retirant ce kext du répertoire /Extra/Extensions, je me retrouve de nouveau avec un clear cmos de ma carte mère Gigabyte EP31-DS3L au reboot du système.

J'ai remis le kext /Extra/Extensions/ElliottForceLeg acyRTC.kext et tout fonctionne.
Ce Kext est donc indispensable mais ne reste pas chargé.
Citer
 
 
0 #3 sonotone 01-12-2009 12:43
On va dire que c'est l'exception qui confirme la règle.
Un bonne occasion d'ailleurs de créer une condition pour ce kext pourquoi pas :)
Citer
 
 
0 #4 eric_s 07-04-2012 18:24
Très bon tutoriel que j'ai mis en oeuvre avec Xcode 4.3. Tout fonctionne correctement sauf que la tableview ne se met pas à jour : au lancement de l'application aucune ligne n'est affichée (ni même les lignes "alternating"). Pour voir un des chemins je dois double-cliquer la ligne.
J'ai téléchargé le zip (qui lui fonctionne) et comparé les attributs de la tableview (ainsi que ses descendants) mais je ne vois pas différence.
Si quelqu'un a une idée.

Encore une fois bravo pour ce tutoriel.
Citer