IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

API, COM et SDKs Delphi Discussion :

Problème avec la démo Virtual Listview et la libération de pointeur (suite lecture SDK)


Sujet :

API, COM et SDKs Delphi

  1. #1
    Expert éminent sénior
    Avatar de Jipété
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    10 876
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 10 876
    Points : 15 305
    Points
    15 305
    Par défaut Problème avec la démo Virtual Listview et la libération de pointeur (suite lecture SDK)
    Bonsoir à toutes et à tous.

    Dans mon D7 PE je trouve une démo fort sympathique permettant de recréer un Explorateur (non, je ne parle pas du couple ShellTreeview/ShellListview).
    Au premier lancement il affiche la liste des disques, tout ce qu'il y a de joli,
    Je vous mets ci-dessous le code du FormCreate, avec mes ajouts en commentaires, venant de mon SDK :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    procedure TForm1.FormCreate(Sender: TObject);
    var
      FileInfo: TSHFileInfo;
      ImageListHandle: THandle;
      NewPIDL: PItemIDList;
    begin
      OLECheck(SHGetDesktopFolder(FIDesktopFolder));
      FIShellFolder := FIDesktopFolder;
      FIDList := TList.Create;
     
      ImageListHandle := SHGetFileInfo('C:\',
                               0,
                               FileInfo,
                               SizeOf(FileInfo),
                               SHGFI_SYSICONINDEX or SHGFI_SMALLICON);
      SendMessage(ListView.Handle, LVM_SETIMAGELIST, LVSIL_SMALL, ImageListHandle);
     
      ImageListHandle := SHGetFileInfo('C:\',
                               0,
                               FileInfo,
                               SizeOf(FileInfo),
                               SHGFI_SYSICONINDEX or SHGFI_LARGEICON);
      SendMessage(ListView.Handle, LVM_SETIMAGELIST, LVSIL_NORMAL, ImageListHandle);
     
      OLECheck(
        SHGetSpecialFolderLocation(
          Application.Handle,
          CSIDL_DRIVES, // [in] A CSIDL value that identifies the folder of interest.
          NewPIDL)      // [out] A pointer to an item identifier list (PIDL)
      );                // specifying the folder's location relative to the root
                        // of the namespace (the desktop).
                        // The calling application is responsible for freeing this pointer
                        // with the Shell's IMalloc interface (see SHGetMalloc).
     
      SetPath(NewPIDL);
     
      CoTaskMemFree(NewPIDL); // Rajout suite lecture SDK (SHGetMalloc renvoie vers CoTask...)
     
      ActiveControl := cbPath;
      cbPath.SelStart := 0;
      cbPath.SelLength := Length(cbPath.Text);
    end;
    Et qu'est-ce que je gagne à l'exécution ? L'erreur
    Violation d'accès à l'adresse 7CE4E3C5 dans le module 'shell32.dll'. Lecture de l'adresse 00155475.
    et des icônes absentes, ou "la main" (qu'on retrouve sous les dossiers partagés), et certains textes bleus, d'autres noirs...

    Si maintenant je remplace ImageListHandle := SHGetFileInfo('C:\', par ImageListHandle := SHGetFileInfo('', je n'ai plus cette violation d'accès mais toutes les icônes affichent "la main"...

    J'aimerais bien comprendre, car cette variable NewPIDL est locale à la procédure...

    Pour info, le code de OleCheck (d'un vieux [D7 PE oblige...] ComObj.pas) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    { Raise EOleSysError exception if result code indicates an error }
     
    procedure OleCheck(Result: HResult);
    begin
      if Result < 0 then OleError(Result);
    end;
    Pas de mystère là-dedans ; mon SDK raconterait-il des blagues ? Un tour sur MSDN et je lis
    [out] A pointer to an item identifier list (PIDL) specifying the folder's location relative to the root of the namespace (the desktop). It is the responsibility of the calling application to free the returned IDList by using CoTaskMemFree.
    Bon.
    Donc j'ai raison (d'essayer) de libérer la mémoire, mais mon prog part en vrille !

    De plus je lis, toujours à propos de cette fonction, With Microsoft Windows 2000, this function is superseded by SHGetFolderLocation.
    Ça tombe bien, je suis sous 2000, donc je teste avec SHGetFolderLocation mais je gagne les mêmes résultats foireux !

    Et pourtant, Madshi, sur une page d'EE dit bien qu'il faut « freer » les Pidl's :
    when you call SHGetSpecialFolderLocation, Windows allocates a new pidl for you (in win9x even in shared memory). So you DO need to free it again. What happens, if you don't? Well, memory leak.
    Au fait, un test avec MemCheck ne montre pas de fuite mémoire si j'enlève mon ajout (code d'origine, donc).

    Voilà, désolé d'avoir été un peu long, mais si quelqu'un pouvait m'expliquer ce mystère, j'en serais vraiment ravi !
    Merci,

  2. #2
    Membre éprouvé

    Profil pro
    Inscrit en
    Mai 2003
    Messages
    582
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Mai 2003
    Messages : 582
    Points : 915
    Points
    915
    Par défaut
    J'imagine que tu fait un appèle à CoInitialize ou à OleInitialize
    quelque part....

    Citation Envoyé par MSDN
    You must initialize Component Object Model (COM) with CoInitialize or OleInitialize prior to calling SHGetFileInfo.

  3. #3
    Expert éminent sénior
    Avatar de Jipété
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    10 876
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 10 876
    Points : 15 305
    Points
    15 305
    Par défaut
    Salut Éric,

    et merci de te pencher sur ce problème où j'y perds mon latin...
    Citation Envoyé par Eric Boisvert Voir le message
    J'imagine que tu fait un appel à CoInitialize ou à OleInitialize quelque part....
    Non, le code posté vient directement de l'exemple de Delphi.

    Un truc intéressant, c'est qu'en faisant une recherche dans le forum avec le mot-clé "CoTaskMemFree", il y a une (une seule !) réponse et le code posté n'appelle pas non plus xxInitialize...

    Encore plus fort : j'ai également fait des recherches sur ShGetSpecialFolderLocation (une vingtaine de réponses), personne n'utilise CoTaskMemFree ; j'ai donc pris un ou deux code(s), les ai fait tourner sans problème, ai rajouté ensuite ce fameux CoTaskMemFree et ça n'est pas parti en vrille comme l'exemple que je cite (qui vient de Borland, je le rappelle...).
    Le plus fou c'est que, avec ou sans CoTaskMemFree, Memcheck ne me détecte pas de fuite mémoire...
    (il me reste un dernier test à faire, vérifier que mon MemCheck détecte bien une fuite si j'en force une, lol !)

  4. #4
    Membre éprouvé

    Profil pro
    Inscrit en
    Mai 2003
    Messages
    582
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Mai 2003
    Messages : 582
    Points : 915
    Points
    915
    Par défaut
    Une chose est sûre, je suis certain que Memcheck ne peu pas detecté
    des fuites mémoires qui sont faite via COM.

    Et puisque j'ai seulement Delphi5, je ne peux pas compiler ton code...

  5. #5
    Membre éprouvé

    Profil pro
    Inscrit en
    Mai 2003
    Messages
    582
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Mai 2003
    Messages : 582
    Points : 915
    Points
    915
    Par défaut
    Désolé, je viens de trouver la démo...

    en faite, tout ce passe dans
    DisposePIDL();

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    procedure DisposePIDL(ID: PItemIDList);
    var
      Malloc: IMalloc;
    begin
      if ID = nil then Exit;
      OLECheck(SHGetMalloc(Malloc));
      Malloc.Free(ID);
    end;
    Donc tu n'a pas à appeler CoTaskMemFree(NewPIDL) dans le create.

    De toute façon, tu ne doit pas faire le Free tant que le liste view les affiches.

    Ceci serait équivallent:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    procedure DisposePIDL(ID: PItemIDList);
    //var
    //  Malloc: IMalloc;
    begin
      if ID = nil then Exit;
      //OLECheck(SHGetMalloc(Malloc));
      //Malloc.Free(ID);
      CoTaskMemFree(ID) 
    end;

  6. #6
    Expert éminent sénior
    Avatar de Jipété
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    10 876
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 10 876
    Points : 15 305
    Points
    15 305
    Par défaut
    Bonsoir, Éric
    Citation Envoyé par Eric Boisvert Voir le message
    Une chose est sûre, je suis certain que Memcheck ne peut pas détecter des fuites mémoires qui sont faites via COM.
    Aïe ! Ça ne va pas arranger mes affaires... Car MemCheck détecte bien, par exemple, la création d'une TList sans libération.

    Citation Envoyé par Eric Boisvert Voir le message
    (...) Donc tu n'a pas à appeler CoTaskMemFree(NewPIDL) dans le Create.(...)
    Tu as sans doute raison, mais alors, comment expliquer ce bout de code (source) ? :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    //Here is an example to enumerate all the objects on the Desktop:
    ...
    var
      Desktop: IShellFolder;
      EnumIDLIst: IEnumIDList;
      CurPidl: PItemIDLIst;
      Fetched: ULONG;
    begin
      //if SHGetDesktopFolder(Desktop) <> NOERROR then 
      // ne fonctionne pas chez moi, dessous OK :
      if Succeeded(SHGetDesktopFolder(Desktop)) then
      begin
        if Desktop.EnumObjects(Handle, SHCONTF_FOLDERS, EnumIDList) = NOERROR then
        begin
           // Retrieve objects one at a time...
           while EnumIDList.Next(1, CurPidl, Fetched) = S_OK do
           begin
             // Add code here to do something with the objects
             // par exemple :
             memo1.Lines.Add(PIDLtoPath(CurPidl));
             CoTaskMemFree(CurPidl); // Remember to free PIDLs! <<<<<<<<<<<<<<
           end;
        end;
      end;
    end;
    Comment expliquer que Madshi dise la même chose (voir mon premier post) ? Et tiens, un autre exemple from DelphiDabbler, cité sur un forum d'Embarcadero :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    function SpecialFolderPath(CSIDL: Integer): string;
    {Returns the full path to a special file system folder specified by a CSIDL
    constant FolderID or '' if the special folder is virtual or CSIDL is not supported.}
    var
      PIDL: ShlObj.PItemIDList; // PIDL of the special folder
    begin
      Result := '';
      // Get PIDL for required folder
      if Windows.Succeeded(ShlObj.SHGetSpecialFolderLocation(0, CSIDL, PIDL)) then
      try
        // Get path from PIDL
        Result := PIDLToFolderPath(PIDL);
      finally
        // Free the PIDL using shell allocator <<<<<<<<<<<<<<<<<<<<<<<<
        FreePIDL(PIDL);
      end
    end;
    Citation Envoyé par Eric Boisvert Voir le message
    (...) De toute façon, tu ne dois pas faire le Free tant que la ListView les affiche. (...)
    Mouais, le problème doit venir de là, et pourtant la variable est locale à la procédure : la seule interaction (interférence ?) avec le reste du code, c'est l'appel à la procédure
    Citation Envoyé par Eric Boisvert Voir le message
    en fait, tout se passe dans DisposePIDL(ShellItem(I).ID);
    Moi je veux bien, mais cette procédure se trouve dans une autre, ClearIDList (appelée au FormClose), et ça travaille sur
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    type
      PShellItem = ^TShellItem;
      TShellItem = record
        FullID,
        ID: PItemIDList;
        ...
      end;
    Il est bien question de PItemIDList là-dedans, mais ma question était relative à la variable locale (oui, j'insiste, ) dans FormCreate.

    Bah, on va laisser tomber, à moins que quelqu'un ait une idée lumineuse (mais ce sujet n'a pas l'air d'inspirer grand monde, )...
    En tout cas, merci à toi de t'être penché dessus,

  7. #7
    Membre éprouvé

    Profil pro
    Inscrit en
    Mai 2003
    Messages
    582
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Mai 2003
    Messages : 582
    Points : 915
    Points
    915
    Par défaut
    La variable est local mais c'est un pointeur alloué par
    COM via quelque chose comme IMalloc par le shell et donc invisible pour Memcheck.

    Ce pointeur est passé à TForm1.SetPath(ID: PItemIDList), Attention y en a 2 setpath...

    SetPath() memorise ce pointeur dans le checkbox...(Oups Combobox)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    cbPath.Items.InsertObject(0, FPath, Pointer(FPIDL));
    Donc, faudrait modifier le FormClose
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    procedure TForm1.Form1Close(Sender: TObject; var Action: TCloseAction);
    begin
      while cbPath.Items.Count<>0 do
      begin
        CoTaskMemFree(cbPath.Items.Objects[0]);
        cbPath.Items.Delete(0);
      end;
      ClearIDList;
      FIDList.Free;
    end;

  8. #8
    Expert éminent sénior
    Avatar de Jipété
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    10 876
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 10 876
    Points : 15 305
    Points
    15 305
    Par défaut
    Citation Envoyé par Eric Boisvert Voir le message
    SetPath() mémorise ce pointeur dans le Combobox
    Bien vu !
    Et moi, après (dans le FormCreate), je détruisais ce pointeur,

    Citation Envoyé par Eric Boisvert Voir le message
    Donc, faudrait modifier le FormClose
    Testé, no problémo,

    Hélas, si on se foire sur ce coup-là, le souci c'est la fuite mémoire
    Citation Envoyé par Eric Boisvert Voir le message
    (...) et donc invisible pour Memcheck.
    Quelqu'un(e) connait un outil pour D7 PE capable de détecter ce genre de fuites ?

    J'attends deux-trois jours avant de cliquer sur « Résolu »

    Merci, Éric

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [C#]Virtual ListView (CLR 2.0) - problème MouseOver
    Par fdebuiss dans le forum Windows Forms
    Réponses: 2
    Dernier message: 01/09/2006, 08h25
  2. [VB.NET] ListView Problème avec propriété View à 'List'
    Par elkidos dans le forum Windows Forms
    Réponses: 2
    Dernier message: 25/08/2006, 17h10
  3. Problèmes avec une Listview
    Par janego dans le forum C++Builder
    Réponses: 1
    Dernier message: 10/07/2006, 19h27
  4. Problème avec Virtual Treeview
    Par bisounoursbleu dans le forum Composants VCL
    Réponses: 6
    Dernier message: 06/12/2004, 15h37
  5. [C#] Problème avec ListView
    Par yannick dans le forum Windows Forms
    Réponses: 4
    Dernier message: 03/06/2004, 17h29

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo