Qu’est-ce que ?

De nombreuses méthodes de la bibliothèque .Net sont implémentées en code natif. Ceux qui proviennent du framework lui-même sont marqués avec [MethodImpl(MethodImplOptions.InternalCall)] . Ceux qui proviennent de DLL non gérées sont marqués avec [DllImport] (par exemple, [DllImport("kernel32.dll")] ). Jusqu’à présent, rien d’inhabituel.

Mais en écrivant la réponse à une autre question , j’ai découvert qu’il existe de nombreuses méthodes marquées avec [DllImport("QCall")] . Ils semblent être une implémentation interne de .Net (par exemple, GC._Collect() ).

Ma question est la suivante: que signifie exactement [DllImport("QCall")] ? Quelle est la différence entre [DllImport("QCall")] et [MethodImpl(MethodImplOptions.InternalCall)] ?

C’est un vieux sujet. Depuis que CoreCLR est maintenant open-source sur GitHub; si quelqu’un cherche toujours la réponse, voici la documentation officielle :

Appel du code géré au code natif

Nous avons deux techniques pour appeler le CLR à partir du code managé. FCall vous permet d’appeler directement dans le code CLR et offre une grande souplesse en termes de manipulation d’objects, bien qu’il soit facile de provoquer des trous GC en ne dépistant pas correctement les références d’objects. QCall vous permet d’appeler le CLR via le P / Invoke et est beaucoup plus difficile à utiliser accidentellement que FCall. Les FCalls sont identifiés dans le code managé comme des méthodes externes avec le bit MethodImplOptions.InternalCall défini. Les QCalls sont des méthodes externes statiques qui ressemblent aux invocations normales, mais à une bibliothèque appelée “QCall”.

Il y a une petite variante de FCall appelée HCall (pour Helper call) pour implémenter les aides JIT, pour faire des choses comme accéder à des éléments multi-dimensionnels, vérifier les plages, etc. La seule différence entre HCall et FCall dans une trace de stack d’exception.

Et puis ça continue dans les sous-titres:

  • Choisir entre FCall, QCall, P / Invoke et écrire en code managé
  • QCall comportement fonctionnel

avec des exemples:

  • Exemple QCall – Pièce gérée
  • Exemple QCall – Partie non gérée

J’ai interrogé certaines personnes de l’équipe .Net à ce sujet.

Les QCalls sont des appels aux méthodes natives dans le runtime CLR. Ils se comportent comme les autres [DllImport] , mais ils sont plus rapides car ils émettent des hypothèses spécifiques (non documentées) sur ce que font les méthodes natives, ils peuvent donc ignorer plusieurs contrôles de marshalling et de GC et d’exceptions.

InternalCall est différent; c’est pour les appels pour des choses spéciales de type reflection qui sont générées à l’exécution (ce n’était pas très clair).

En complément de @SLaks answer, MethodImplOptions.InternalCall est décrit brièvement ici: ThreadPoolPriority et MethodImplAtsortingbute .

Fondamentalement, InternalCall indique au moteur d’exécution de vérifier sa propre table de consultation interne des fonctions nommées. Cette table existe en raison d’un fichier source dans le code d’exécution qui les déclare explicitement lors de la compilation du moteur d’exécution. Il dispose d’une liste de pointeurs de fonctions pour implémenter tous les appels internes:

 static ECFunc gGuidFuncs[] = { {FCFuncElement("CompleteGuid", NULL, (LPVOID)GuidNative::CompleteGuid)}, {NULL, NULL, NULL} }; 

Cette déclaration indique à l’exécution que le corps de la méthode Guid.CompleteGuid gérée est en réalité la fonction native C ++ GuidNative :: CompleteGuid. L’article n’est pas très clair sur la façon dont fonctionne le marshaling à cet endroit, mais en général c’est clairement jusqu’à l’implémentation du runtime, car a) déclare le corps de la fonction [qui dépend du format de marshaling] et b) .