J'essaie d'utiliser une DLL C non managée pour charger des données d'image dans une application C#. La bibliothèque a une interface assez simple où vous passez une structure qui contient trois rappels, un pour recevoir la taille de l'image, un qui reçoit chaque ligne des pixels et enfin un appelé quand le chargement est terminé. Comme cela (C# définition gérée):Epingler un délégué dans une structure avant de passer au code non managé
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct st_ImageProtocol
{
public st_ImageProtocol_done Done;
public st_ImageProtocol_setSize SetSize;
public st_ImageProtocol_sendLine SendLine;
}
Les types de départ st_ImageProtocol sont Biographie des délégués:
public delegate int st_ImageProtocol_sendLine(System.IntPtr localData, int rowNumber, System.IntPtr pixelData);
Avec le fichier de test que j'utilise le SetSize devrait obtenir appelé une fois, le SendLine obtiendra appelé 200 fois (une fois pour chaque ligne de pixels dans l'image), enfin le rappel Done est déclenché. Qu'est-ce qui se passe réellement, c'est que le SendLine est appelé 19 fois et puis une exception AccessViolationException est levée affirmant que la bibliothèque a essayé d'accéder à la mémoire protégée. J'ai accès au code de la bibliothèque C (bien que je ne puisse pas changer la fonctionnalité) et pendant la boucle où elle appelle la méthode SendLine elle n'alloue ou ne libère aucune nouvelle mémoire, donc mon hypothèse est que le Le délégué lui-même est le problème et je dois l'épingler avant de le transmettre (je n'ai pas de code à l'intérieur du délégué, à part un compteur pour voir à quelle fréquence il est appelé, donc je doute que je casse quoi que ce soit). Le problème est que je ne sais pas comment faire cela; la méthode que j'ai utilisée pour déclarer les structures dans l'espace non géré ne fonctionne pas avec les délégués (Marshal.AllocHGlobal()) et je ne trouve aucune autre méthode appropriée. Les délégués eux-mêmes sont des champs statiques dans la classe Program donc ils ne devraient pas être collectés, mais je suppose que le runtime pourrait les déplacer.
This blog entry by Chris Brumme dit que les délégués ne doivent pas nécessairement être épinglé avant d'être passé dans le code non géré:
Il est clair que le pointeur de fonction non gérée doit se référer à une adresse fixe. Ce serait un désastre si le GC déménageait ça! Cela conduit de nombreuses applications à créer un handle d'épinglage pour le délégué. Ceci est complètement inutile. Le pointeur de fonction non géré fait référence à un bout de code natif que nous générons dynamiquement pour effectuer la transition & marshaling. Ce talon existe dans la mémoire fixe en dehors du tas GC.
Mais je ne sais pas si cela est vrai lorsque le délégué fait partie d'une structure. Cela implique qu'il est possible de les épingler manuellement, et je suis intéressé par la façon de faire ceci ou par de meilleures suggestions pour savoir pourquoi une boucle fonctionnerait 19 fois puis soudainement échouerait.
Merci.
Edité pour répondre aux questions de Johan ...
Le code qui alloue la struct est la suivante:
_sendLineFunc = new st_ImageProtocol_sendLine(protocolSendLineStub);
_imageProtocol = new st_ImageProtocol()
{
//Set some other properties...
SendLine = _sendLineFunc
};
int protocolSize = Marshal.SizeOf(_imageProtocol);
_imageProtocolPtr = Marshal.AllocHGlobal(protocolSize);
Marshal.StructureToPtr(_imageProtocol, _imageProtocolPtr, true);
Lorsque le _sendLineFunc et les variables de _imageProtocol sont les champs statiques du programme classe. Si je comprends correctement les internes, cela signifie que je passe un pointeur non géré à copier de la variable _imageProtocol dans la bibliothèque C, mais cette copie contient une référence à la _sendLineFunc statique. Cela devrait signifier que la copie n'est pas touchée par le GC - car elle n'est pas gérée - et le délégué ne sera pas collecté car il est toujours dans la portée (statique).
Le struct se fait passer à la bibliothèque en tant que valeur de retour d'un autre rappel, mais comme un pointeur:
private static IntPtr beginCallback(IntPtr localData, en_ImageType imageType)
{
return _imageProtocolPtr;
}
Fondamentalement, il existe un autre type de struct qui détient le nom du fichier d'image et le pointeur de fonction à ce rappel , la bibliothèque détermine quel type d'image est stockée dans le fichier et utilise ce rappel pour demander la structure de protocole correcte pour le type donné. Mon nom de fichier struct est déclaré et géré de la même manière que le protocole ci-dessus, donc probablement contient les mêmes erreurs, mais comme ce délégué n'est appelé qu'une seule fois et appelé rapidement, je n'ai pas encore eu de problèmes.
Edité pour mettre à jour
Merci à tous pour leurs réponses, mais après avoir passé un autre couple de jours sur le problème et de faire aucun progrès j'ai décidé de classer ce. Au cas où quelqu'un serait intéressé, j'essayais d'écrire un outil pour les utilisateurs de l'application de rendu 3D Lightwave et une fonctionnalité intéressante aurait été la possibilité de voir tous les différents formats d'image supportés par Lightwave (dont certains sont assez exotiques). Je pensais que la meilleure façon de le faire serait d'écrire un wrapper C# pour l'architecture de plugin que Lightwave utilise pour la manipulation d'image afin que je puisse utiliser leur code pour charger réellement les fichiers. Malheureusement après avoir essayé un certain nombre de plugins contre ma solution, j'ai eu une variété d'erreurs que je ne pouvais pas comprendre ou corriger et je suppose que Lightwave n'appelle pas les méthodes sur les plugins de manière standard, probablement pour améliorer la sécurité de code externe en cours d'exécution (poignarder sauvage dans le noir, je l'admets). Pour le moment, je vais laisser tomber la fonction d'image et si je décide de la rétablir, je l'aborderai différemment.
Merci encore, j'ai appris beaucoup grâce à ce processus même si je n'ai pas obtenu le résultat que je voulais.
Merci pour les questions - J'ai modifié ma question avec plus d'informations –