2010-08-11 5 views
3

Je souhaite créer une DLL qui exporte des fonctions renvoyant une chaîne. Cette DLL devrait fonctionner avec d'autres langages de programmation !! J'ai trouvé toutes sortes de mauvaises solutions/hacks pour cela, le meilleur est de rendre ma fonction retourner Pchar puis appeler une autre fonction contenue dans la même DLL (appelons-la ReleaseMemory) pour libérer la mémoire réservée pour PChar.DLL Delphi compatible avec d'autres langages de programmation

Quoi qu'il en soit, j'ai récemment découvert la bibliothèque FastShareMem. Il dit qu'il peut faire exactement ce que je veux SANS appeler le ReleaseMemory. D'un autre côté FastMM semble faire la même chose AS LONG car la DLL et l'application utilisent FastMM comme gestionnaire de mémoire. Cela tue instantanément la possibilité d'utiliser FastMM comme gestionnaire de mémoire pour ma DLL universelle. Droite?

====================

FastShareMem (http://www.codexterity.com/fastsharemem.htm), Delphi 7, Windows XP 32 bits, Windows 7 64 bits

Répondre

6

Vous confondez deux scénarios différents:

  1. applications Delphi utilisant des DLL Delphi
  2. Toute application utilisant DLL Delphi

Dans le premier scénario, à moins que vous mélangez la version Delphi ou faire quelque chose de bizarre , le gestionnaire de mémoire est le même, et le compilateur aussi. Ainsi, il existe des moyens de partager le gestionnaire de mémoire, puis le compilateur est capable de gérer correctement les allocations/désallocations. C'est le scénario dans lequel FastMM et FastShareMem fonctionnent - et seulement celui-ci. Dans le deuxième scénario, l'application et la DLL utiliseront différents gestionnaires de mémoire, peut-être très différents, et il n'y a généralement aucun moyen de les partager. Dans une telle situation, la meilleure approche est de ne jamais retourner un PChar alloué dans la DLL, même si vous fournissez une fonction de désallocation, car vous ne pouvez pas être sûr de ce que la langue appelante ferait plus tard avec votre PChar. l'appelant a une chance d'appeler la routine de désallocation appropriée avant le compilateur/interpréteur. COM fonctionne un peu comme vous l'avez dit, mais il applique l'allocation/désallocation de la mémoire via son propre gestionnaire de mémoire, ce qui le rend sûr. Vous ne pouvez pas l'appliquer avec des DLL simples, ce qui n'est pas sûr.

La meilleure approche est de laisser la langue d'appel vous transmettre un tampon suffisamment grand, et y écrire votre PChar. Bien sûr, vous devez avoir un moyen de dire à l'appelant quelle taille le tampon devrait être. C'est ainsi que Windows fonctionne, et il y a de bonnes raisons pour lesquelles ils ont fait ce choix.

+0

+1. J'espérais vraiment que je pourrais retourner PChar mais votre argument semble solide. – Ampere

+1

Vous pouvez appliquer des choses aussi bien que COM le fait. Si vous n'utilisez pas les fonctions COM pour libérer de la mémoire allouée par COM, vous obtenez des erreurs. C'est comme ça que "fait respecter" les choses. Vous pouvez appliquer l'utilisation du code de gestion de la mémoire de votre DLL de la même manière. Il n'y a aucun danger à allouer et renvoyer des pointeurs de votre DLL. Il y a un risque que l'EXE fasse un mauvais usage de la mémoire, mais il y a * toujours * un risque que d'autres codes aient des bogues. Ce n'est pas ton problème. –

+2

Le problème est que les langues qui ne connaissent pas les pointeurs peuvent encore passer des tampons et ces tampons sont automatiquement gérés. IMHO est beaucoup mieux de laisser l'appelant gérer sa mémoire et juste manipuler un tampon déjà alloué, que d'en allouer un et demander à l'appelant de le libérer, surtout si vous ne pouvez pas appliquer vos nouveaux types de données comme le fait COM. Le code peut comporter des bogues, mais certaines techniques sont plus susceptibles d'introduire des bogues que d'autres. –

8
Si

Si vous renvoyez un Delphi string, votre DLL ne fonctionnera pas avec les autres langages de programmation car aucun autre langage de programmation n'utilise le type de chaîne de Delphi. Peu importe comment vous allouez de la mémoire si les types ne sont pas les mêmes. Si vous travaillez avec du texte, suivez le modèle de l'API Windows et utilisez des pointeurs de caractères anciens.

La solution que vous avez trouvée - renvoyer un pointeur, puis fournir une autre fonction pour que votre DLL libère la mémoire - n'est pas un hack et n'est pas méchante du tout. C'est une solution parfaitement ordinaire, et personne n'utilisant votre DLL ne se mettra à l'œil quand ils le verront. La fonction API FormatMessage utilise un modèle similaire: Elle alloue une chaîne pour vous et spécifie que les chaînes qu'elle alloue doivent être libérées avec LocalFree.

Peu importe le gestionnaire de mémoire que vous utilisez tant que vous êtes cohérent et que les utilisateurs de votre DLL peuvent l'utiliser. Une approche consiste à spécifier les fonctions API Windows pour l'allocation et la libération de chaînes, telles que LocalAlloc et LocalFree ou SysAllocString et SysFreeString. Une autre méthode consiste à ne jamais allouer quoi que ce soit - si l'appelant a besoin de vous pour renvoyer une chaîne, l'appelant fournit le tampon et vous indique sa taille. Si le tampon est trop petit, vous retournez la taille requise pour que l'appelant puisse réaffecter le tampon et rappeler la fonction. Pour un exemple de cela, voir GetLongPathName. FastSharemem fournit une longue explication de how Delphi's memory manager works, puis il est dit que vous pouvez éviter tous les problèmes en utilisant simplement cette unité dans votre programme. Mais rappelez-vous ce que j'ai dit plus haut: les consommateurs de votre DLL doivent pouvoir utiliser le même gestionnaire de mémoire que vous utilisez. Lorsque le client de votre DLL n'est pas écrit en Delphi, il ne peut pas utiliser l'unité FastSharemem. FastSharemem est très bien dans un environnement Delphi homogène, mais il subit tous les mêmes pièges que n'importe quel autre gestionnaire de mémoire lorsqu'il est utilisé dans un environnement mixte.

+0

Désolé. Bien sûr, aucun autre langage de programmation n'a un tel type génial comme Delphi STRING. J'ai donc vraiment besoin de quelque chose de plus compatible. J'ai oublié de mentionner que j'ai l'intention de remplacer ma fonction de retour de chaîne par des fonctions de retour de PChar. – Ampere

+0

Il dit qu'il retourne PChar et le libère plus tard avec DllReleaseString. Donc les types sont bien, c'est juste que le gestionnaire de mémoire ne le sauvera pas du besoin d'appeler DllReleaseString si l'appelant n'utilise pas le même gestionnaire de mémoire. Puisque les gestionnaires de mémoire Delphi ne fonctionnent que dans Delphi, les applications écrites dans toutes les autres langues ne pourront même pas utiliser ces gestionnaires de mémoire. – himself

+1

Notez que les versions de Delphi peuvent également causer des problèmes. Le D2009 + ansistring est différent du D2007-one, bien que je ne sache pas si cela cause des problèmes avec les interfaces binaires. –

2

Ce qui se passe est fondamentalement ceci. Chaque morceau de code compilé séparément (DLL ou EXE) contient son propre code qui alloue la mémoire du système et la gère, appelée le gestionnaire de mémoire. En termes simples, lorsque ce morceau de code est initialisé, il alloue un gros bloc de mémoire du système. Plus tard, quand GetMem le fait ou alloue des chaînes, des tableaux et cetera, le gestionnaire de mémoire marque les parties de ce gros bloc comme utilisées. Lorsque vous utilisez FreeMem/libérez-les, ils sont marqués comme inutilisés.

Imaginez maintenant que vous avez EXE et DLL, les deux avec leurs propres gestionnaires de mémoire. EXE appelle la procédure DLL, DLL alloue une chaîne (PChar), marquant ainsi une partie de son grand bloc de mémoire tel qu'utilisé. Il renvoie ensuite le pointeur sur l'EXE, qui l'utilise et décide plus tard de libérer. EXE donne le pointeur à son propre gestionnaire de mémoire et demande à le libérer, mais ce n'est même pas du grand bloc de mémoire d'EXE!Le gestionnaire de mémoire d'EXE ne sait pas comment "libérer" la mémoire de quelqu'un d'autre. C'est pourquoi vous devez appeler DllReleaseString(), renvoyant ainsi le pointeur de mémoire empruntée à la DLL et laissant le propre gestionnaire de mémoire interne de DLL le libérer. Maintenant, ce que font les gestionnaires de mémoire partagée, ils se connectent les uns aux autres. Gestionnaire de mémoire dans votre DLL et gestionnaire de mémoire dans votre EXE savent comment se parler, et lorsque vous donnez le pointeur mémoire de DLL au gestionnaire de mémoire d'EXE, il comprend qu'il est de DLL et libère le gestionnaire de mémoire DLL. Bien sûr, cela n'est possible que lorsque les deux gestionnaires de mémoire DLL et EXE sont construits à partir du même code de gestionnaire de mémoire (ou bien ils ne se reconnaîtraient pas!). Si votre gestionnaire de mémoire DLL est un partage, et votre gestionnaire de mémoire EXE est quelque chose d'autre, gestionnaire de mémoire DLL ne sera pas en mesure de "demander" EXE pour libérer de la mémoire, et le gestionnaire de mémoire EXE ne va même pas essayer. Par conséquent, si vous voulez que votre DLL soit universelle, vous ne pouvez pas compter sur les gestionnaires de mémoire qui se parlent. Votre DLL peut être utilisée avec des fichiers EXE ou DLL qui dépendent d'un gestionnaire de mémoire différent, peut-être écrit dans une langue différente. Le partage de gestionnaires de mémoire n'est possible que lorsque vous contrôlez toutes les parties de votre projet et que vous pouvez configurer explicitement un seul et même gestionnaire partout.

+0

Mais c'est ici. Ce gestionnaire de mémoire Delphi dit que cela peut être fait (lire les deux dernières lignes): http://www.codexterity.com/memmgr.htm . Je comprends mal ou il peut vraiment le faire? – Ampere

+0

+1 pour votre réponse très complète! – Ampere

+1

Je ne vois pas où il est dit que la solution va fonctionner même si un côté (EXE ou DLL) n'utilise pas FastSharemem. – himself

3

Je suis l'auteur de FastSharemem et j'aimerais apporter ma contribution de 2 cents. Rob a raison, FastSharemem suppose que les modules seront tous écrits en Delphi. Passer des données entre des modules dans des langues différentes peut être difficile, en particulier avec des données dynamiques comme des chaînes. C'est pourquoi l'API Windows est souvent difficile à utiliser lorsqu'il s'agit de structures de données complexes, et c'est aussi une des raisons pour lesquelles COM (OLE) de Microsoft fournit ses propres fonctions de gestion de la mémoire et ses types spéciaux; le but est la compatibilité binaire entre les modules compilés à partir de sources différentes. Donc, puisque Windows l'a déjà fait auparavant, vous pouvez utiliser l'une des deux méthodes que Windows utilise pour le faire.Soit:

1) Exposez une API de style C (PChars etc) et spécifiez l'API avec beaucoup de détails. Vous pouvez exposer les routines d'allocation de mémoire que les clients doivent appeler ou demander aux clients d'effectuer l'allocation. L'API Windows effectue les deux à différents moments. Les clients peuvent également avoir besoin d'un SDK pour parler commodément à votre module, et n'oubliez pas d'utiliser uniformément la convention d'appel stdcall.

Ou,

2) Utilisez les types COM et pour transmettre des données et sortir. Delphi a un excellent support COM presque transparent. Par exemple, pour les chaînes, vous pouvez utiliser le BSTR de COM (WideString dans Delphi).

Espérons que cela aide.

Questions connexes