I. Introduction▲
Une nouveauté du célèbre MSN Messenger 7 est qu'il permet d'afficher un message personnel. Un petit message qui ne se retrouve pas exactement dans votre pseudo puisque celui-ci n'est pas repris lorsque vous « tchatchez ». Pour ceux qui ne voient toujours pas de quoi je veux parler, voici une capture d'écran :
Cette nouvelle zone peut également être utilisée pour afficher ce que vous êtes en train d'écouter avec Windows Media PLayer,Winamp…
II. Développement du programme▲
II-A. Présentation▲
Pour cet article, je vais réaliser un programme permettant de modifier cette zone personnelle. Deux paramètres peuvent être modifiés :
1. L'icône affichée (Icone jeux, office ou musique) ;
2. Le texte affiché.
Afin de rendre l'article plus intéressant, je vais rajouter une fonctionnalité importante, le texte affiché sera en fait un compte à rebours. Et voici finalement ce que permet le programme :
J'ai décidé d'y afficher un compte à rebours, mais vous pouvez programmez tout ce que vous voulez afficher (la température du cpu, votre vitesse de download, le programme que vous êtes en train d'utiliser…) il n'y a pas de limite.
II-B. Communication avec MSN Messenger 7▲
Afin de communiquer, en fait d'envoyer le message personnel, nous devons appeler une fonction propre à MSN 7. Avant d'appeler cette fonction, nous allons importer deux dll ainsi que tous les objets nécessaires. Nous n'avons pas besoin d'ajouter manuellement des références.
Si vous souhaitez comprendre le mécanisme qui permet d'appeler des fonctions dont le code est implémenté dans des DLL natives, je vous incite à lire cet article : Les DLL natives en .NET (Morpheus)
using
System.
Runtime.
InteropServices;
[DllImport(
"user32"
, EntryPoint=
"SendMessageA"
)]
private
static
extern
int
SendMessage
(
int
Hwnd,
int
wMsg,
int
wParam,
int
lParam);
[DllImport(
"user32"
, EntryPoint=
"FindWindowExA"
)]
private
static
extern
int
FindWindowEx
(
int
hWnd1,
int
hWnd2,
string
lpsz1,
string
lpsz2);
private
const
short
WM_COPYDATA =
74
;
public
struct
COPYDATASTRUCT
{
public
int
dwData;
public
int
cbData;
public
int
lpData;
}
public
COPYDATASTRUCT data;
Nous avons tous les objets nécessaires, nous pouvons maintenant faire appel à la méthode de MSN (SendMessage). Mais avant, il faut construire un string reprenant l'ensemble des informations (icône+message).
public
int
VarPtr
(
object
e)
{
GCHandle GC =
GCHandle.
Alloc
(
e,
GCHandleType.
Pinned);
int
gc =
GC.
AddrOfPinnedObject
(
).
ToInt32
(
);
GC.
Free
(
);
return
gc;
}
private
void
SendMSNMessage
(
bool
enable,
string
category,
string
message)
{
string
buffer =
"
\\
0"
+
category +
"
\\
0"
+
(
enable ?
"1"
:
"0"
) +
"
\\
0{0}
\\
0"
+
message +
"
\\
0
\\
0
\\
0
\\
0
\0
"
;
int
handle =
0
;
data.
dwData =
0x0547
;
data.
lpData =
VarPtr
(
buffer);
data.
cbData =
buffer.
Length *
2
;
handle =
FindWindowEx
(
0
,
handle,
"MsnMsgrUIManager"
,
null
);
if
(
handle >
0
)
SendMessage
(
handle,
WM_COPYDATA,
0
,
VarPtr
(
data));
}
Tout est en place et nous pouvons envoyer le message personnel en appelant la méthode SendMSNMessage de cette façon :
SendMSNMessage
(
true
,
"Office"
,
"travaille sous Word"
);
- Le premier paramètre (bool enable), indique s'il faut afficher le message que l'on envoie…la réponse est oui, enfin true ;)
Par contre, on peut imaginer mettre ce paramètre à false lorsque l'on quitte l'application. Ceci aura pour effet de remettre le précédent message personnel.
- Le second paramètre représente l'icône à afficher, on a le choix entre trois : « Office », « Games » ou « Music »
- Le dernier paramètre est le texte à afficher.
II-C. Création du compte à rebours▲
Vous pourrez retrouver le code source de ce programme dans le fond de l'article. Voici à quoi ressemble l'application en elle-même.
Le principe est simple, l'utilisateur choisit l'icône à afficher puis choisit une date/heure à décompter (Date time to countdown) ou directement une durée pour le décompte (Countdown).
Pour finir, il ne reste plus qu'à spécifier le texte à afficher (Text to display) ainsi que le taux de rafraichissement du décompte dans MSN (Refresh rate).
Je ne vais pas vous expliquer en détail chaque ligne, mais uniquement les gros concepts utilisés. Le code source n'est pas compliqué à comprendre. Pour résumer; beaucoup d'évènements, peu de traitements.
La première étape consiste à créer un projet WinForm et à placer tous les contrôles, pour ça pas besoin d'aide :p.
Nous aurons besoin d'un objet DateTime pour retenir la date et l'heure de la zone Date time to countdown ainsi qu'un objet TimeSpan pour retenir la durée du compte à rebours.
System.
DateTime dtcountdown =
System.
DateTime.
Now;
System.
TimeSpan dtthecd;
Maintenant pour chaque modification de la date et/ou de l'heure de la zone Date time to countdown nous devrons mettre dtcountdown et dtthecd à jour. Je ne vous montre qu'un évènement, mais c'est à chaque fois pareil.
private
void
numUpDownTimeH_ValueChanged
(
object
sender,
System.
EventArgs e)
{
try
{
dtcountdown =
dtcountdown.
AddHours
((
double
)this
.
numUpDownTimeH.
Value-
dtcountdown.
Hour);
MajcountDown
(
);
}
catch
(
Exception err)
{
Form FrmDebug =
new
FrmDebug
(
err.
ToString
(
));
FrmDebug.
ShowDialog
(
);
}
}
private
void
MajcountDown
(
)
{
try
{
dtthecd =
dtcountdown.
Subtract
(
System.
DateTime.
Now);
if
(
dtthecd.
TotalSeconds <=
0
)
{
this
.
numUpDownCDD.
Value=
0
;
this
.
numUpDownCDH.
Value=
0
;
this
.
numUpDownCDM.
Value=
0
;
this
.
numUpDownCDS.
Value=
0
;
}
else
{
this
.
numUpDownCDD.
Value=(
decimal
)dtthecd.
Days;
this
.
numUpDownCDH.
Value=(
decimal
)dtthecd.
Hours;
this
.
numUpDownCDM.
Value=(
decimal
)dtthecd.
Minutes;
this
.
numUpDownCDS.
Value=(
decimal
)dtthecd.
Seconds;
}
}
catch
(
Exception err)
{
Form FrmDebug =
new
FrmDebug
(
err.
ToString
(
));
FrmDebug.
ShowDialog
(
);
}
}
De même, lors de la modification du décompte (zone Countdown), il faut mettre à jour l'objet dtthecd (TimeSpan) qui se cache derrière.
private
void
numUpDownCDD_ValueChanged
(
object
sender,
System.
EventArgs e)
{
try
{
dtthecd =
dtthecd.
Add
(
System.
TimeSpan.
FromDays
((
double
)this
.
numUpDownCDD.
Value -
dtthecd.
Days));
}
catch
(
Exception err)
{
Form FrmDebug =
new
FrmDebug
(
err.
ToString
(
));
FrmDebug.
ShowDialog
(
);
}
}
Tout est en place, nous pouvons lancer le compte à rebours. Le compte à rebours est lancé lorsque l'utilisateur appuie sur le premier bouton tout en bas.
Le compte à rebours est implémenté à l'aide d'un Timer.
private
void
btnStart_Click
(
object
sender,
System.
EventArgs e)
{
try
{
this
.
timer.
Interval=(
int
)numUpDownRefRate.
Value*
1000
;
this
.
timer.
Start
(
);
this
.
timer.
Tick+=
new
EventHandler
(
timer_Tick);
...
}
catch
(
Exception err)
{
Form FrmDebug =
new
FrmDebug
(
err.
ToString
(
));
FrmDebug.
ShowDialog
(
);
}
}
Grâce au timer, la méthode timer_Tick sera lancée à intervalle de temps régulier. L'intervalle de temps (représenté en ms) est en fait le nombre de secondes spécifié dans Refresh rate.
La méthode timer_Tick représente le plus gros du traitement : la mise à jour du compteur, du texte et de l'icône à envoyer à MSN, et pour finir l'appel de la méthode SendMSNMessage.
private
void
timer_Tick
(
object
sender,
EventArgs e)
{
try
{
string
ico,
message;
if
(
dtthecd.
TotalSeconds>
0
)
{
if
(
this
.
radioOffice.
Checked==
true
)
{
ico=
"Office"
;
}
else
{
if
(
this
.
radioGames.
Checked==
true
)
{
ico=
"Games"
;
}
else
{
ico=
"Music"
;
}
}
message=
this
.
txtBxToDisp.
Text;
dtthecd =
dtthecd.
Subtract
(
System.
TimeSpan.
FromSeconds
((
double
)numUpDownRefRate.
Value));
numUpDownCDD.
Value =
(
decimal
)dtthecd.
Days;
numUpDownCDH.
Value =
(
decimal
)dtthecd.
Hours;
numUpDownCDM.
Value =
(
decimal
)dtthecd.
Minutes;
numUpDownCDS.
Value =
(
decimal
)dtthecd.
Seconds;
message =
message.
Replace
(
"[D]"
,((
decimal
)dtthecd.
Days).
ToString
(
));
if
(
dtthecd.
Days>
1
)
{
message =
message.
Replace
(
"[ds]"
,
"s"
);
}
else
{
message =
message.
Replace
(
"[ds]"
,
""
);
}
message =
message.
Replace
(
"[H]"
,((
decimal
)dtthecd.
Hours).
ToString
(
));
if
(
dtthecd.
Hours>
1
)
{
message =
message.
Replace
(
"[hs]"
,
"s"
);
}
else
{
message =
message.
Replace
(
"[hs]"
,
""
);
}
message =
message.
Replace
(
"[M]"
,((
decimal
)dtthecd.
Minutes).
ToString
(
));
if
(
dtthecd.
Minutes>
1
)
{
message =
message.
Replace
(
"[ms]"
,
"s"
);
}
else
{
message =
message.
Replace
(
"[ms]"
,
""
);
}
message =
message.
Replace
(
"[S]"
,((
decimal
)dtthecd.
Seconds).
ToString
(
));
if
(
dtthecd.
Seconds>
1
)
{
message =
message.
Replace
(
"[ss]"
,
"s"
);
}
else
{
message =
message.
Replace
(
"[ss]"
,
""
);
}
SendMSNMessage
(
true
,
ico,
message);
}
else
{
if
(
this
.
radioOffice.
Checked==
true
)
{
ico=
"Office"
;
}
else
{
if
(
this
.
radioGames.
Checked==
true
)
{
ico=
"Games"
;
}
else
{
ico=
"Music"
;
}
}
SendMSNMessage
(
true
,
ico,
this
.
txtBxAfter.
Text);
}
}
catch
(
Exception err)
{
Form FrmDebug =
new
FrmDebug
(
err.
ToString
(
));
FrmDebug.
ShowDialog
(
);
}
}
Le 2e bouton (bouton Stop) arrête le compteur et remet l'ancien message personnel :
private
void
btnStop_Click
(
object
sender,
System.
EventArgs e)
{
try
{
this
.
timer.
Tick-=
new
EventHandler
(
timer_Tick);
this
.
timer.
Stop
(
);
SendMSNMessage
(
false
,
"Office"
,
""
);
...
}
catch
(
Exception err)
{
Form FrmDebug =
new
FrmDebug
(
err.
ToString
(
));
FrmDebug.
ShowDialog
(
);
}
}
La partie compte à rebours est maintenant terminée, il me reste à vous parler de deux trois aspects du programme.
II-D. Lecture/Écriture des paramètres dans un fichier XML▲
Il y a ici très peu de paramètres personnels, en fait juste le texte à afficher durant et après le décompte. Ces informations sont retenues dans un fichier XML en sortie de programme et sont lues en entrée (pour remplir les textbox).
private
void
ReadXML
(
)
{
try
{
XmlTextReader xReader;
XmlDocument xDoc;
XmlNode node;
xDoc =
new
XmlDocument
(
);
xReader =
new
XmlTextReader
(
"TxtToDisp.xml"
);
xDoc.
Load
(
xReader);
xReader.
Close
(
);
node =
xDoc.
SelectSingleNode
(
"TxtToDisp/UserDuring"
);
this
.
txtBxToDisp.
Text =
node.
InnerText;
node =
xDoc.
SelectSingleNode
(
"TxtToDisp/UserAfter"
);
this
.
txtBxAfter.
Text =
node.
InnerText;
}
catch
(
Exception err)
{
Form FrmDebug =
new
FrmDebug
(
err.
ToString
(
));
FrmDebug.
ShowDialog
(
);
}
}
private
void
WriteXML
(
)
{
try
{
XmlDocument xDoc;
XmlTextWriter xWriter;
XmlNode xNode;
xDoc =
new
XmlDataDocument
(
);
xDoc.
AppendChild
(
xDoc.
CreateXmlDeclaration
(
"1.0"
,
"utf-8"
,
null
));
xNode =
AddNode
(
xDoc,
null
,
"TxtToDisp"
,
""
);
AddNode
(
xDoc,
xNode,
"UserDuring"
,
this
.
txtBxToDisp.
Text);
AddNode
(
xDoc,
xNode,
"UserAfter"
,
this
.
txtBxAfter.
Text);
xWriter =
new
XmlTextWriter
(
"TxtToDisp.xml"
,
System.
Text.
Encoding.
GetEncoding
(
"utf-8"
));
xDoc.
WriteTo
(
xWriter);
xWriter.
Close
(
);
}
catch
(
Exception err)
{
Form FrmDebug =
new
FrmDebug
(
err.
ToString
(
));
FrmDebug.
ShowDialog
(
);
}
}
private
XmlNode AddNode
(
XmlDocument xDoc,
XmlNode xNodeParent,
string
name,
string
innerValue)
{
try
{
XmlNode xNode;
xNode =
xDoc.
CreateNode
(
XmlNodeType.
Element,
name,
""
);
xNode.
InnerText =
innerValue;
if
(
xNodeParent==
null
)
{
xDoc.
AppendChild
(
xNode);
}
else
{
xNodeParent.
AppendChild
(
xNode);
}
return
xNode;
}
catch
(
Exception err)
{
Form FrmDebug =
new
FrmDebug
(
err.
ToString
(
));
FrmDebug.
ShowDialog
(
);
return
null
;
}
}
II-E. Réduction en icône de notification▲
Ce genre d'application reste en général longtemps en fonctionnement, et dans de tels cas, il vaut mieux aller se loger dans la zone de notification (à gauche de l'horloge). Pour ne pas surcharger la barre de tâches de windows. Un autre avantage est que lorsque l'on réduit l'application, elle utilise beaucoup moins de mémoire (24Mo->1.5Mo).
Au passage, je vous montre le code de la méthode FrmMsnCntDown_Closing déclenchée lors de la fermeture de la fenêtre.
int
quit=
0
;
...
private
void
FrmMsnCntDown_Closing
(
object
sender,
System.
ComponentModel.
CancelEventArgs e)
{
SendMSNMessage
(
false
,
"Office"
,
""
);
quit=
1
;
}
private
void
FrmMsnCntDown_Deactivate
(
object
sender,
System.
EventArgs e)
{
try
{
if
(
quit==
0
&&
this
.
WindowState==
FormWindowState.
Minimized)
{
this
.
notifyIcon1.
Visible=
true
;
this
.
Visible=
false
;
}
}
catch
(
Exception err)
{
Form FrmDebug =
new
FrmDebug
(
err.
ToString
(
));
FrmDebug.
ShowDialog
(
);
}
}
private
void
notifyIcon1_Click
(
object
sender,
System.
EventArgs e)
{
this
.
Visible=
true
;
this
.
WindowState =
FormWindowState.
Normal;
this
.
notifyIcon1.
Visible=
false
;
}
III. Conclusion▲
Si l'actualisation du compteur sur votre MSN se fait sans problème chaque seconde, il faut cependant savoir que les autres ne verront pas changer cette zone chaque seconde… Ça serait trop beau. En fait cette zone est actualisée en général toutes les 5-10 secondes.
Contrairement au pseudo qui lui est retenu sur un serveur MSN, cette zone de texte, tout comme votre image perso ne sont pas envoyées à MSN. C'est vous qui partagez ces données. Peut-être qu'en achetant une ligne T4 vous arriverez à actualiser ces données chaque seconde ;)
J'allais oublier le plus important! Pour que ce procédé fonctionne, il faut choisir dans MSN « Activer ce que j'écoute ».
Pour toute remarque ou question sur l'article, n'hésitez pas à me contacter par MP ou posez votre question sur le forum Général Dotnet.
IV. Téléchargements▲
- Code source (Miroir) [330Ko]
- Exécutable (Miroir) [60Ko]