2008-10-03 9 views
4

Je sais que je peux obtenir ceci pour fonctionner techniquement mais je voudrais mettre en application la solution la plus propre possible. Voici la situation:Gestion des délégués gérés dans le code non managé

J'ai une bibliothèque gérée qui enveloppe une bibliothèque de style C non managée. La fonctionnalité de la bibliothèque de style C que j'emballe actuellement effectue un traitement impliquant une liste de chaînes. Le code client de la bibliothèque peut fournir un délégué, de sorte qu'au cours du traitement de la liste, si un scénario "invalide" est rencontré, la bibliothèque peut rappeler le client via ce délégué et lui permettre de choisir la stratégie à utiliser (lancer une exception, remplacer Ce que j'aimerais idéalement avoir, c'est que tout le C++ managé soit isolé dans une fonction, et ensuite être capable d'appeler une fonction séparée qui ne prend que des paramètres non gérés, de sorte que tous les éléments natifs soient isolés. Le code C++ et non managé est isolé à ce point. Fournir le mécanisme de rappel à ce code non géré s'avère être le point de friction pour moi.


#pragma managed 
public delegate string InvalidStringFilter(int lineNumber, string text); 
... 
public IList<Result> DoListProcessing(IList<string> listToProcess, InvalidStringFilter filter) 
{ 
    // Managed code goes here, translate parameters etc. 
} 

#pragma unmanaged 
// This should be the only function that actually touches the C-library directly 
std::vector<NativeResult> ProcessList(std::vector<char*> list, ?? callback); 

Dans cet extrait, je veux garder tous les accès C-bibliothèque au sein processlist, mais au cours du traitement, il devra faire callbacks, et ce rappel est fourni sous la forme du délégué InvalidStringFilter qui est transmis depuis un client de ma bibliothèque gérée.

Répondre

2

.NET peut convertir automatiquement le délégué en un pointeur pour fonctionner s'il est déclaré correct. Il y a deux mises en garde

  1. La fonction C doit être construit STDCALL
  2. Le pointeur de fonction ne compte pas comme une référence à l'objet, vous devez donc disposer d'une référence à conserver de telle sorte que l'objet sous-jacent est pas des ordures collectées

http://www.codeproject.com/KB/mcpp/FuncPtrDelegate.aspx?display=Print

+1

Merci pour le deuxième commentaire. Cela explique finalement pourquoi je continue à obtenir SEH à des moments aléatoires – galets

+0

J'ai eu de la chance d'avoir quelqu'un qui me l'a dit lors de ma première journée avec C#, donc je l'ai toujours su. Il savait que j'étais sur le point d'écrire un pont C#/C++. –

2

Si je comprends bien le problème que vous devez déclarer une fonction de rappel non géré dans votre assemblage C++/CLI qui agit comme le pont entre votre bibliothèque C et Manag délégué ED.


#pragma managed 
public delegate string InvalidStringFilter(int lineNumber, string text); 

... 
static InvalidStringFilter sFilter; 

public IList<Result> DoListProcessing(IList<string> listToProcess, InvalidStringFilter filter) 
{ 
    // Managed code goes here, translate parameters etc. 
    SFilter = filter; 
} 

#pragma unmanaged 

void StringCallback(???) 
{ 
    sFilter(????); 
} 

// This should be the only function that actually touches the C-library directly 
std::vector<NativeResult> ProcessList(std::vector<char*> list, StringCallback); 

Comme écrit, ce code n'est clairement pas sûr pour les threads. Si vous avez besoin de la sécurité des threads, un autre mécanisme est nécessaire pour rechercher le délégué géré correct dans le rappel, soit ThreadStatic, soit le callback obtient une variable fournie par l'utilisateur que vous pouvez utiliser.

+0

Donc essentiellement j'aurais un pointeur de fonction non managé appeler une fonction non managée, alors cette fonction non managée aurait appelé le délégué, qui a été stocké comme variable membre dans ma classe managée? – Reddog

+0

Essentiellement, oui. Vous devez déterminer comment la fonction StringCallback accède au délégué géré. L'extrait ci-dessus en fait juste une variable globale. StringCallback peut également gérer la traduction entre les types, par exemple std :: string et String ^ –

0

Vous voulez faire quelque chose comme ceci:

typedef void (__stdcall *w_InvalidStringFilter) (int lineNumber, string message); 

GCHandle handle = GCHandle::Alloc(InvalidStringFilter); 
w_InvalidStringFilter callback = 
    static_cast<w_InvalidStringFilter>(
    Marshal::GetFunctionPointerForDelegate(InvalidStringFilter).ToPointer() 
); 

std::vector<NativeResult> res = ProcessList(list, callback); 
Questions connexes