Bonjour à tous,

J'explique la chose ;-) Nous avons créés plusieurs applications en Delphi qui se basent toutes sur une DLL (API) écrite en C++ Builder.

Après un petit audit de nos codes sources, nous avons constatés qu'il y avait un léger soucis avec les TList que nous employons et qui transitaient entre les applications et la DLL. Le fait est que nous pensions qu'un TList.Clear libérait la mémoire, mais ce n'est pas le cas.

Donc, dans la DLL C++, j'ai commencé à écrire une classe héritant de TList et surchargeant la méthode Clear de base.

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
 
template <class T> class TListDs : public TList
{
    public:
        void __fastcall Clear (void) {
            for (int i = 0; i < Count; ++i) {
                delete (T*) Items[i];
            }
            TList::Clear ();
        }
};
Via ce code, il est bien plus simple de changer toutes nos déclarations de TList dans la DLL par TListDs<TYPE_CLASSE> et ainsi, nous sommes sûr que tout est bien libéré lorsque nous faisons appel au desctructeur de la classe.

Le problème est dû à l'héritage qui ne semble pas fonctionnel entre du Delphi et du C++.
Donc, afin de pallier la chose,

J'ai déclaré dans ma DLL différentes fonctions retournant un pointeur de type TListDs<....>, jusque l'à tout va bien :-)

Ce qui donne ceci.

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
 
    TList * EXPORTCALL NewListPerson (void) {
        return new TListDs <TPerson>;
    }
    TList * EXPORTCALL NewListInteger (void) {
        return new TListDs <int>;
    }
    void EXPORTCALL FreeList (TList *pList) {
        delete pList;
    }
Au niveau du mapping du côté Delphi, voici ce que j'ai fais.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
 
function NewListInteger: TList; stdcall;
function NewListPerson: TList; stdcall;
 
procedure FreeList (vList : TList); stdcall;
J'ai essayé et cela fonctionne nickel, les variables créées à l'aide de NewListInteger et NewListPerson sont bien de type TList ce qui permet de garder l'interface d'une TList et lorsque je libère via FreeList, la DLL utilise bien la classe Template que j'ai écris. Ce qui dont est le but de base.

Le problème est que cela ne peut pas aller si on doit modifier toutes les applications rien que pour les TList.

Si j'ai bien compris une instance d'une TList est créée à l'aide de TList.Create

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
 
var vList : TList = nil;
begin
  vList := TList.Create;
  vList.Free;
end;
Afin de minimiser les changements dans le code Delphi, il n'est pas intéressant de remplacer les TList.Create par des NewListPerson, etc...

Alors je me suis dis, "essayons de créer une TListDsPerson" qui utiliserait une fonction Create ( et non le constructeur ), afin d'initialiser la TList et de lui retourner un pointeur correspond au bon type de TList; en l'occurence une NewListPerson;


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
 
type
  TListDsPerson = class
    private
      Ptr : TList; 
    public
      function Create : TList;
      procedure Clear; 
  end;
 
implementation
 
function TListDsPerson.Create : TList;
begin
  WriteLn ('TListDsPerson.Create');
  self.Ptr := NewListPerson;
  Result := self.Ptr;
end;
 
procedure TListDsPerson.Clear;
begin
  FreeList (self);
  WriteLn('TListDsPerson.Clear');
end;
Et maintenant voici le code que j'utilise pour essayer de tester mon système.
Le problème est que j'arrive à une erreur me disant ceci lors de la compilation.

[Erreur] LoadDLL.dpr(45): Forme d'appel de méthode autorisée seulement pour les méthodes de classes


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
 
var
  vListPerson : TList = nil;
  vPerson : Pointer;
begin
  try
    vListPerson := TListDsPerson.Create;
    GetList(vListPerson);   
 
    for Cpt := 0 to vListPerson.Count - 1 do
    begin
      vPerson := vListPerson.Items[Cpt];
      WriteLn ('Id : ' + IntToStr (Cpt));
      WriteLn ('FirstName : ' + GetPersonFirstName (vPerson));
      WriteLn ('LastName : ' + GetPersonLastName (vPerson));
    end;
    vListPerson.Clear;
  except
    WriteLn ('Une erreur est survenue...');
  end;
end.
Donc ma grande question, est-il possible de réaliser ce que j'essaie, c'est à dire retourner quelque chose lors du Create, afin de tromper Delphi, ce qui me permettrait de limiter les changements au sein du code existant.

Si oui, comment ;-)

Sinon, :'(


Merci,

Stéphane