vendredi 14 janvier 2011

Delegates & opération inter-thread en VB.NET

Il y a quelques temps de cela, Sh0ck m'avait demandé comment faire pour partager les membres au sein d'une classe en .NET. Les exemples sont florissants sur le web mais parfois assez obscurs.

Le problème



Commencez par ouvrir Visual Studio - peu importe la version - et agencez rapidement une interface graphique, par exemple, comme ceci :


Vous pouvez vous passer du label. En fait, seule le champ "TextEdit" et le bouton nous importent.

On va faire en sorte que lorsque nous cliquons sur le bouton, un thread se créé, se lance, et mette à jour le champ TextEdit pour y afficher un "Hello world".

Oui, bon, ok, pas besoin de thread pour ça, mais c'est pour l'exemple.

Créez un champ "BackgroundWorker" en prime. Ensuite, on va dans le code de notre formulaire et on insère la méthode suivante :
Private Sub DoStuff()
' Mise à jour du TextBox
Me.TextBox1.Text = "Hello world!"
End Sub


Cette méthode sera appelée par le BackgroundWorker :

Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, _
ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Me.DoStuff()
End Sub


Et on "lancera" le BackgroundWorker après clic sur notre bouton :

Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
Me.BackgroundWorker1.RunWorkerAsync()
End Sub


On lance la fenêtre, on clique sur notre bouton et, là, c'est le drâme : une exception type InvalidOperationException est lancée. Le descriptif de celle-ci est : Opération inter-threads non valide : le contrôle 'TextBox1' a fait l'objet d'un accès à partir d'un thread autre que celui sur lequel il a été créé..

Et c'est là qu'interviennent les "Delegates". Ils permettent aux différents threads d'accéder aux propriétés communes d'une classe. Pour ceux qui connaissent les mutex - exclusions mutuelles - le principe peut sembler le même, sauf qu'ici, nous n'aurons pas verrouiller ou déverrouiller quoi que ce soit.

Tout d'abord, nous allons créer une méthode publique qui mettra à jour notre TextEdit :

Public Sub MajTextBox1(ByVal chaine As String)
Me.TextBox1.Text = chaine
End Sub


Rien de bien sorcier. Ensuite, nous déclarons dans la liste des membres notre "Delegate" qu'on instanciera et à qui on fournira l'adresse de MajTextBox1. Il s'agit, en quelques sortes, d'une référence de fonction. Le delegate doit donc avoir la même signature que notre méthode MajTextBox1, qu'importe la visibilité de cette dernière. On aura donc à écrire :

Private Delegate Sub AccessMajTextEdit(ByVal chaine As String)


Ensuite, dans notre méthode DoStuff(), appelez la méthode "Invoke", qui va servir à appeler notre Delegate et donc, par la même occasion, la méthode MajTextBox1, les conflits en moins :

Private Sub DoStuff()

' Déclaration et instanciation du Delegate

Dim MyDelegate As New AccessMajTextEdit(AddressOf MajTextBox1)

' La propriété InvokeRequired indique si l'on a
' besoin d'appelé un délégué pour modifier une instance
' d'un autre thread

If Me.InvokeRequired Then
' Appel du delegate
Me.Invoke(MyDelegate, "Hello world !")
End If
End Sub


On relance notre petite application, on clique sur le bouton et, normalement, ...



C'était un petit article sans prétention. Je ne maîtrise véritablement pas le sujet, je ne saurais vous dire ce qu'est un Delegate en détail. On sait juste que ça marche. Vous pouvez très bien utiliser les threads au lieu des BackgroundWorker - ces derniers étant néanmoins plus faciles à utiliser, ce pourquoi je m'en suis servi.

Cette astuce m'est très utile au boulot, c'est pour ça que je l'insère ici pour ne pas l'oublier. En espérant que quelques programmeurs VB.NET puissent profiter.

Le code complet :

Public Class Form1

Private Delegate Sub AccessMajTextEdit(ByVal chaine As String)

Private Sub DoStuff()

' Instanciation du Delegate
Dim MyDelegate As New AccessMajTextEdit(AddressOf MajTextBox1)

' La propriété InvokeRequired indique si l'on a
' besoin d'appelé un délégué pour modifier une instance
' d'un autre thread

If Me.InvokeRequired Then
Me.Invoke(MyDelegate, "Hello world !")
End If
End Sub


Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
Me.BackgroundWorker1.RunWorkerAsync()
End Sub

Private Sub
BackgroundWorker1_DoWork(ByVal sender As Object, _
ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Me.DoStuff()
End Sub

Public Sub
MajTextBox1(ByVal chaine As String)
Me.TextBox1.Text = chaine
End Sub
End Class


Ge0

mardi 11 janvier 2011

Timestamp UNIX en VB.NET

Dans le cadre d'un projet sur lequel je bosse en entreprise, il m'est arrivé de devoir manipuler les timestamp UNIX en VB.NET. Or les sources à ce sujet sont peu généreuses sur la toile.

On fournit notamment une fonction pour passer d'un timestamp à une date, comme ceci :

Public Function DateFromTimestamp(ByVal timestamp As Long) As DateTime
Dim debut As New DateTime(1970, 1, 1, 0, 0, 0)
Return debut.AddSeconds(timestamp)
End Function


Pour preuve :
P:\>php -r "echo time();"
1294733118


MessageBox.Show(DateFromTimestamp(1294733118).ToString) ' Résultat : "11/01/2011 08:05:18"


Pour récupérer le timestamp d'une date, c'est un poil plus délicat mais pas impossible. On dispose de la propriété Ticks pour une instance DateTime, qui renvoie le nombre de "Ticks" correspondant à une date donnée par 100 nanosecondes. Seulement, on ne sait pas depuis quand. Cependant, on sait qu'un timestamp correspond au nombre de secondes écoulées depuis le premier Janvier 1970 à minuit - le début de l'ère UNIX - et une date lambda. Il suffit donc de récupérer, pour chacune de ces deux dates, le nombre de Ticks, d'en faire la différence et de multiplier le tout par 10-7 pour convertir nos ticks en secondes.

Public Function TimeStamp(Optional ByVal laDate As DateTime = Nothing) As Long
If laDate = Nothing Then
laDate = DateTime.Now
End If
Dim TicksDebut As Long = New DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Local).Ticks
Dim TicksActuels As Long = laDate.Ticks

Return Math.Floor((TicksActuels - TicksDebut) * Math.Pow(10, -7))
End Function


Puisse cette petite astuce servir à quelques développeurs...

Ge0

mercredi 10 novembre 2010

Comprendre ce à quoi servent __cdecl et __stdcall en langage C, ainsi que leurs différences

Qu'on développe en C ou qu'on lise du code (pour le fun), on tombe souvent sur des signatures type :
int __cdecl foobar(const char* pt_name, int anotherArg);


Ou même encore :

void __stdcall barfoo(void* ptr, int arg);


Les prototypes sont choisis au hasard. On ne sait pas ce que font ces fonctions à l'intérieur et encore moins à quoi elles servent.

D'après le titre de l'article, vous comprendrez que nous nous intéresserons aux notations __cdecl et __stdcall.

Tout d'abord, il faut savoir que ça n'est pas un souci majeur de savoir si on doit mettre telle ou telle directive. En effet, cela change peu de choses. Si l'on ne met aucune de ces deux directives - et c'est souvent le cas quand on écrit du C, parce qu'on en n'a strictement rien à cirer - alors ce sera la directive __cdecl qui sera utilisée.

Allez, j'en viens aux faits... Explications !

Comme vous pouvez le voir, on utilise ces directives lors de la définition de fonctions. En effet, pour ceux qui ne savent pas forcément comment se passe l'appel d'une fonction - d'un point de vue binaire, donc quand on exécute des instructions "natives" successivement - voici, sans entrer dans des explications barbares, comment ça se passe :

La routine appelante, qui peut aussi bien être votre fonction main ou même une autre fonction, va déposer les arguments de la fonction à appeler sur la pile d'exécution. Les informaticiens emploient couramment la traduction anglaise de ce terme : "execution stack", voire stack puisqu'ils sont fainéants. Cette pile d'exécution est très utile, et sert au programme pour mémoriser des données en général : variables, adresses mémoire, et j'en passe. Prenons le code suivant :

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

void __cdecl doStuff(int, int);
void __stdcall doAnotherStuff(int, int, int, int);

int main(int argc, char **argv) {
doStuff(1, 2);
doAnotherStuff(1, 2, 3, 4);
return EXIT_SUCCESS;
}

void __cdecl doStuff(int arg1, int arg2) {
printf("I'm doStuff.\n");
}

void __stdcall doAnotherStuff(int foo1, int foo2, int foo3, int foo4) {
printf("- And I'm doAnotherStuff.\n");
}


Lors de l'appel à doStuff, le code présent dans la fonction main() va simplement "empiler" - ou "déposer sur la pile d'exécution" - les arguments 2 et 1. La directive __cdecl définit l'ordre de passage des arguments de la fonction de droite à gauche. C'est-à-dire que nous allons déposer le nombre 2 sur la pile, et 1 ensuite. Au final, notre pile ressemblera à ceci :

00000001
00000002


La valeur 2 se retrouve à la base de notre pile (bien qu'en réalité, on ait d'autres valeurs en-dessous mais elles ne nous intéressent pas) car elle a été empilée en premier. 1 se retrouve au sommet car il a été empilé en second. Vous pourrez penser que c'est bizarre, mais il n'en est rien ; il est plus aisé, pour la fonction appelée, de retrouver le premier argument au sommet de la pile !

La directive __stdcall utilise aussi cette convention, elle a ça en commun avec __cedcl. De ce fait, avant d'appeler la fonction doAnotherStuff, la pile d'exécution sera :

Arg1 = 00000001
Arg2 = 00000002
Arg3 = 00000003
Arg4 = 00000004


Pour information, il existe aussi des conventions où l'on empile les arguments de gauche à droite. Mais nous ne nous y intéresserons pas.

Vous suivez jusqu'ici ? Alors continuons.

Ne tournons plus autour du pot et expliquons les choses telles qu'elles sont réellement :

  • Vous choisissez la directive __cdecl : une fois l'appel de la fonction déroulé, la pile sera intacte et ce sera logiquement à vous de la "nettoyer" ;

  • Vous choisissez la directive __stdcall : une fois l'appel de la fonction déroulé, la pile sera dépourvue des arguments que nous avons empilés.



Que ce soient pour ceux qui ne comprennent pas nécessairement le fonctionnement exact de la pile - parce que j'ai été bref - ou ceux qui savent très très bien de quoi je parle, une question commune peut se poser : "Mais quel intérêt ? Pourquoi choisir l'une ou l'autre puisque ça ne change rien d'apparent ?"

Tout d'abord, sachez que si vous utilisez __stdcall, assurez-vous - et on ne sait jamais - d'avoir une copie de vos arguments quelque part en mémoire. Logiquement, vous en avez. Mais une erreur est si vite arrivée ! Un avantage notable serait évidemment des raisons de réduction de taille de pile : en effet, si votre fonction accepte plusieurs arguments voire une structure en entrée seulement - donc copiée intégralement sur la pile - alors la directive __stdcall peut s'avérer intéressante puisque la pile est dégagée au retour à la routine appelante.

Mais __cdecl a ses avantages aussi : c'est de garder la pile telle qu'on l'a trouvée avant l'appel ; pratique pour ceux qui utilisent une fonction sans forcément l'avoir programmée - l'API Win32, par exemple - et qui veulent que les choses restent à leur place. Ben, tenez, j'ai parlé de l'API Win32. Elles sont toutes, je pense, conventionnées par __stdcall.

Peut-être dis-je des choses fausses à propos des avantages / inconvénients ; le fainéant que je suis n'a pas pris la peine de se renseigner davantage sur ces directives-là. Donc ça peut se discuter, éventuellement. Et les commentaires sont là pour ça (je ne les ai jamais soumis à approbation et ça a excellemment marché jusque là).

Référence : http://msdn.microsoft.com/en-us/library/984x0h58%28v=VS.80%29.aspx


Geo

dimanche 13 juin 2010

Comment supporter __construct() et __destruct() en PHP 4 ?

Ok, rien à voir avec la sécurité, le Reverse Code Engineering ou quoi, mais je m'en fous. C'est mon blog et j'ai bien prévenu que je pouvais publier des trucs sur les polly pockets et les figurines en castor.

Allez, plus sérieusement, j'aimerais partager une astuce - m'en souvenir aussi, en tout cas - de programmation orientée objet en php. Je signale que cette méthode n'est en rien issu d'une masturbation intellectuelle, j'ai trouvé ça dans le framework CakePHP.

Pour se situer dans le contexte



En php4, voici ce qu'on ferait pour déclarer notre définition de classe (le moule à gateau, quoi).

<?php

class Foo {
private $_param = null;

/* Constructeur de la classe */
public function Foo($arg = "") {
$this->_param = $arg;
}
}



Notez que j'ai omis le "?>" à la fin ; en effet, ça permet d'éviter d'avoir des erreurs à la con genre "header already sent by "fichier.class.php" on line [...]. On est sûr de n'avoir que du PHP derrière, pas d'echo d'HTML ni rien.

Et donc, en PHP 5, on a ça :

<?php

class Foo {
private $_param = null;

/* Constructeur de la classe */
public function __construct($arg = "") {
$this->_param = $arg;
}
}



On se sert de la méthode magique __construct(). Notez qu'on peut très bien utiliser aussi le constructeur "Foo()", mais les méthodes magiques n'ont pas été conçues pour rien !

Et pour preuve, en PHP 5, on peut faire ça :

<?php

class Foo {
private $_param = null;

/* Constructeur de la classe */
public function __construct($arg = "") {
$this->_param = $arg;
}

/* Destructeur de la classe */
public function __destruct() {
echo "Bye bye!\n";
}
}



Généralement, on utilise les destructeurs lorsque notre objet est amené à allouer des ressources dynamiquement ; il faut donc les libérer.

Prenons un exemple con : un objet va encapsuler un fichier. Le constructeur va ouvrir ce fichier (ça sera transparent) et le destructeur le fermera sans que nous ayons à écrire :

fclose($handleSurNotreFichier);


Bref, revenons à nos moutons. On est passé à PHP 5 dans la plupart des cas, mais il arrive parfois qu'on ne dispose que d'une version 4 qui ne supporte pas __construct() et __destruct() comme on le souhaiterait. En effet, ces méthodes ne seraient pas magiques et donc pas appelées automatiquement et respectivement lors de la construction et la destruction d'une instance.

Voici comment CakePHP procède : tout d'abord, il définit une class "Mère", je dirais même "Doyenne" donc toutes les autres classes hériteront. Et là, vous comprendrez vite l'intention :

<?php

class Mother {

public function Mother() {
/* On récupère les arguments passés à la fonction
via http://fr.php.net/func_get_args */
$arguments = func_get_args();

/* On vérifie si la méthode __destruct() existe
via http://fr2.php.net/method_exists */
if(method_exists($this, "__destruct")) {
/* Si c'est le cas, on l'assimile en fonction
"callback" de "shutdown, c'est-à-dire qu'elle
sera appelée en fin de vie de l'instance
(http://fr2.php.net/register_shutdown_function)
*/
register_shutdown_function( array(&$this, "__destruct") );
}
/* On appelle notre méthode __construct() avec les arguments passés
avec http://fr2.php.net/call_user_func_array */
call_user_func_array(array(&$this, "__construct"), $arguments);
}

/* Et là, on a notre constructeur qui sera surchargé par les classes
filles */
public function __construct() {
/* Le code de votre choix */
}
}


Désormais, qu'on soit en PHP 4 ou PHP 5, on pourra faire :

<?php

class MaClasse extends Mother {

/* Handle sur un fichier */
private $_handle = null;

/* Constructeur */
public function __construct() {
/* Ouverture d'un fichier */
$this->_handle = @fopen("fichier", "w");
}

/* On peut imaginer des méthodes pour écrire, tout effacer, etc... */

/* Destructeur */
public function __destruct() {
fclose($this->_handle);
}
}


Si ça se trouve j'étais le seul à pas connaître ce tour de passe-passe (& puis tant mieux) mais si y'en a à qui ça peut servir, ben tant mieux. J'ai aucun mérite sur la chose, mais je me dis que partager cette petite astuce qui se retrouve au milieu d'un bazar pointu - c'est comme auditer le code de phpBB, quoi - pourrait servir...

Geo

dimanche 6 juin 2010

Une bonne raison d'utiliser intval()

Me frappez pas. J'ai lâché l'idée de reverser winpcap car au final tout tourne autour d'un driver fait par eux-mêmes. Peut-être que quand j'aurai retrouvé l'envie, je m'y mettrai. Mais là, j'ai la tête à parler de PHP car ça faisait longtemps.

Sur le blog de mon camarade pp^, on peut voir un article fort intéressant sur le contournement de la fonction in_array(). Je ne vais pas m'attarder là-dessus, je vous redirige juste sur son article : http://ppisho.me/hack-web/in_array-probleme-avec-des-nombres/.

J'ai aussi trouvé un problème avec des nombres, hors de la fonction in_array(). M'enfin, c'était pas dur à trouver non plus.

Supposons que vous ayez des droits d'écriture sur $foo :


$foo = 3;
if($foo >= 2) {
echo "Rulez!";
} else {
echo "No...";
}


Évidemment, le code affiche Rulez. Seulement voilà, vous vous en douterez :


$foo = " 3 foobar";
if($foo >= 2) {
echo "Rulez!";
} else {
echo "No...";
}


Ce code affiche aussi "Rulez"... Et pour cause ! PHP, c'est bien, mais c'est pas un langage fortement typé comme le C ou le Java. Tout est chaînes de caractères, en PHP. Imaginez qu'à la place de "foobar" après notre "3", on injecte du code SQL, Javascript, ... Et j'en passe. Bah ça fait bobo. Notez aussi que j'ai mis un espace avant le 3 ; pourvu que ça soit un caractère blanc, donc tabulation, retour à la ligne, etc, en fait.

Bon, ok, c'est pas une découverte révolutionnaire et tout le monde sait comment éviter ça.

Avec intval(), notre variable aura toujours un nombre entier mais la condition sera quand même vérifiée :

$foo = intval(" 3 foobar");
if($foo >= 2) {
echo "Rulez! (\$foo = ".$foo.")\n";
} else {
echo ":(";
}


Résultat :

Rulez! ($foo = 3)


Ca sera tout.

Geo

samedi 20 mars 2010

Analyse de winpcap - Petite rectification

Salut,

Je profite d'un billet bref pour vous signaler que je faisais un peu fausse piste. En effet, mon code n'est pas du tout portable. Et là, encore, je ne vous parle pas de linux qui refuserait aimablement de compiler, mais je parle du fait que je passe par un driver sans m'en rendre compte : npf.sys. Si je n'ai pas ce driver, je peux ouvrir mes interfaces réseau, certes, mais ça ne sert à rien d'écrire dedans.

Mes excuses, et voici le driver pour ceux qui veulent : http://venom630.free.fr/geo/blog/pcap/npf.sys

En ce qui concerne "WinGPacket" - ma classe perso pour forger des paquets - elle continue d'être bricolée. Ca reste une bidouille perso et non un projet sérieux, mais pour ceux qui veulent les sources :
http://venom630.free.fr/?mod=cpp&submod=wingpacket

Ah, oui, vous allez atterrir sur un portfolio que je me suis amusé à faire car je me faisais chier. Ca me permet d'organiser plus proprement mon boulot que mon dépôt.

Vous verrez que WinGPacket utilise Registry et String, deux classes que j'ai aussi codées. La première regroupe des fonctions statiques pour manipuler le registre sans se casser la tête, et la classe String, ... hum, vous voyez. :)

C'est tout ce que j'ai à dire pour cette fois. Dans la part III, j'essaierai de vous expliquer comment il est possible de "sniffer" le trafic réseau à coups de DeviceIoControl() (pour foutre la carte réseau en mode promiscuous, par exemple) et ReadFile() (vous vous en doutiez, n'est-ce pas ?).

A bientôt !

Geo

mardi 9 mars 2010

Fonctionnalités intéressantes dans notepad++

Ce petit poste plus ou moins inutile pour me rappeler de quelques fonctionnalités intéressantes que j'ai trouvé au pif sur notepad++. Vous savez, quand on fait une séquence de touches sans faire exprès. Ca peut mener à des résultats rigolos.

On connait bien, pour la plupart, le raccourci Ctrl+G (G de "Go") :

Invite de notepad++ : Aller à un endroit précis


Le développeur de cet outil est très amusant !

Y'a aussi Ctrl + Q - Q étant la première lettre de "Quote". En fonction du langage que vous avez choisi pour la coloration syntaxique, sélectionnez votre bloc de code et appuyez sur ctrl + Q pour y passer en commentaires.


Invite de notepad++ : Commenter du code (cliquez pour agrandir)


Et enfin, à l'instant, je viens de taper sur ctrl + espace, et là... WOOT !


Invite de notepad++ : quelle fonction utiliser ? (cliquez pour agrandir)


On a une liste - peut-être exhaustive ? - de fonctions php à utiliser. Et c'est pas qu'en PHP qu'on nous propose ça. Je connaissais pas, c'est marrant.

Bon, ok, cet article sert un peu à rien, c'est juste pour archiver une connerie dont j'aimerais me resservir et ne pas oublier.

Cya.

Geo