GetIconInfo fuite de ressources

Résolu
Aerdanel Messages postés 4 Date d'inscription mercredi 15 novembre 2006 Statut Membre Dernière intervention 24 mars 2009 - 24 mars 2009 à 09:21
Aerdanel Messages postés 4 Date d'inscription mercredi 15 novembre 2006 Statut Membre Dernière intervention 24 mars 2009 - 24 mars 2009 à 19:45
Bonjour tout le monde. Je suis désolé de vous embêter avec mes histoires, mais sur ce coup ci j'ai besoin d'aide..

Que je vous explique : je suis en train de développer une application Winform en C# 3.5 qui contient un panel dans lequel on peut faire un drag & drop d'images, qui lui sont ajoutées dynamiquement dans des PictureBox.
J'ai mis en place un déplacement des PictureBox, avec un effet "Windows Like" : lorsque l'on commence un déplacement, une version transparente de l'image va suivre le curseur.

Pour ça, j'ai utilisé les api windows, notamment GetIconInfo et CreateIconIndirect afin de créer un nouveau curseur (représentant l'image à déplacer et le pointeur de la souris).
Je me suis vite rendu compte qu'il y avait un problème. Après quelques recherches, il en est ressortit que la fonction GetIconInfo génère une fuite de ressources, en effet, cette fonction va créer 2 objets Bitmap qui ne seront pas détruit automatiquement.

Par la suite, j'ai trouvé une autre fonction de l'api windows, DeleteObject, qui s'occuperait de détruire les objets GDI. Cependant je ne sais pas si je l'utilise mal ou quoi, mais le nombre d'objets GDI continue d'augmenter bien que je fais un appel systématique à cette fonction sur les pointeurs IconInfo.hbmColor et IconInfo.hbmMask.

J'ai bien essayé des solutions de contournement, comme déplacer une PictureBox directement, mais le résultat est décevant : ça lag affreusement, et il y a un effet de clipping monstrueux..

Quelqu'un aurait une idée ou même un exemple qui fonctionne de l'implémentation de la fonction DeleteObject ?

Merci

2 réponses

Aerdanel Messages postés 4 Date d'inscription mercredi 15 novembre 2006 Statut Membre Dernière intervention 24 mars 2009
24 mars 2009 à 19:45
Voilà ce que je faisais au début :

public struct IconInfo
{
public bool fIcon;
public int xHotspot;
public int yHotspot;
public IntPtr hbmMask;
public IntPtr hbmColor;
}


[DllImport("user32.dll")]
public static extern IntPtr CreateIconIndirect(ref IconInfo icon);

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetIconInfo(IntPtr hIcon, ref IconInfo pIconInfo);

public static Cursor CreateCursor(Bitmap bmp, int xHotSpot, int yHotSpot)
{
IconInfo tmp = new IconInfo();
GetIconInfo(bmp.GetHicon(), ref tmp);
tmp.xHotspot = xHotSpot;
tmp.yHotspot = yHotSpot;
tmp.fIcon = false;
return new Cursor(CreateIconIndirect(ref tmp));
}

void pb_MouseDown(object sender, MouseEventArgs e)
{
Cursor.Current = CreateCursor(new Bitmap((PictureBox)sender.Image), e.X, e.Y);
}

Mais au bout d'un certain nombre d'exécution, le programme plantait. J'ai fait quelques recherches, et j'ai trouvé que l'appel à la fonction GetIconInfo engendrait des fuites de mémoires car elle ne s'occupait pas de détruire les objets qui n'étaient plus utilisés. D'où l'utilisation de la méthode DeleteObject de l'api windows user32, qui va détruire l'objet graphique dont on passe le pointeur.

Par contre, les fuites n'étaient toujours pas réparées.. J'ai d'abord pensé que DeleteObject ne remplissait pas son office. C'est en regardant précisément le nombre d'objets GDI grâce au gestionnaire des tâches, et en exécutant le programme pas à pas que je me suis rendu compte que non seulement les DeleteObject fonctionnaient bien, mais qu'en plus les fonctions GetHicon et CreateIconIndirect créaient eux aussi des objets GDI non détruis.

En fouillant un peu, j'ai trouvé une fonction (DestroyIcon) qui permettrait de supprimer un objet Icon créé par une API windows, et en modifiant un peu le code, voilà ce que ça donne :


//la structure qui contiendra les informations du curseur
public struct IconInfo
{
public bool fIcon;
public int xHotspot;
public int yHotspot;
public IntPtr hbmMask;
public IntPtr hbmColor;
}

//import des différentes API windows
[DllImport("user32.dll")]
public static extern IntPtr CreateIconIndirect(ref IconInfo icon);

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetIconInfo(IntPtr hIcon, ref IconInfo pIconInfo);

[DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr handle);

[DllImport("user32.dll", CharSet = CharSet.Auto)]
extern static bool DestroyIcon(IntPtr handle);

//pointe sur la structure IconInfo contenant les informations du curseur
public static IntPtr pointeurCurseur;

public static Cursor CreateCursor(Bitmap bmp, int xHotSpot, int yHotSpot)
{
//si le pointeur pointe sur une adresse mémoire, on la vide
if (pointeurCurseur != IntPtr.Zero) DestroyIcon(pointeurCurseur);

IntPtr ptr = bmp.GetHicon();
IconInfo tmp = new IconInfo();
GetIconInfo(ptr, ref tmp);
tmp.xHotspot = xHotSpot;
tmp.yHotspot = yHotSpot;
tmp.fIcon = false;
pointeurCurseur = CreateIconIndirect(ref tmp);

//on supprime les Bitmap créées par l'API
if (tmp.hbmColor != IntPtr.Zero) DeleteObject(tmp.hbmColor);
if (tmp.hbmMask != IntPtr.Zero) DeleteObject(tmp.hbmMask);
//on supprime la structure temporaire
if (ptr != IntPtr.Zero) DestroyIcon(ptr);

//on renvoie le nouveau curseur
return new Cursor(pointeurCurseur);
}

void pb_MouseDown(object sender, MouseEventArgs e)
{
Cursor.Current = CreateCursor(new Bitmap((PictureBox)sender.Image), e.X, e.Y);
}



Pour tester cette méthode, j'ai bouclé 10000 fois sur l'appel de la fonction, et il n'y a eu aucune erreur, aucun plantage, et le nombre d'objets GDI à la fin du traitement est sensiblement le même (à 3 4 près, étant donné qu'on instancie des objets qu'on utilise encore).

Voilà, en espérant que ça serve à d'autres !
3
Aerdanel Messages postés 4 Date d'inscription mercredi 15 novembre 2006 Statut Membre Dernière intervention 24 mars 2009
24 mars 2009 à 16:23
J'AI TROUVÉ UNE SOLUTION !!! \o/

Je mettrai le résultat dans mon prochain post avec le code annoté !
0
Rejoignez-nous