2010-07-29 22 views
4

Au cours des dernières années, je me suis demandé de temps en temps quel était l'équivalent du (in) célèbre DLL_PROCESS_ATTACH disponible dans le monde .NET. Toute documentation que j'ai dit, légèrement simplifiée, que le premier point d'entrée à une classe est le constructeur statique (cctor), mais vous ne pouvez pas influencer when it is called, ni vous pouvez définir un cctor qui est garanti d'être appelé avant tout autre cctor ou initiateur de champ , pirater, il ne peut même pas être appelé du tout si la classe n'est jamais utilisée. Donc, si vous voulez garantir quelque chose d'initialisé avant toute méthode de votre assembly est appelée et vous ne voulez pas avoir à ajouter un cctor à chaque classe de votre assembly, quelle approche pouvez-vous prendre? Ou y a-t-il une solution facile et gérée dans .NET que j'ai manqué toutes ces années?Quel est le premier point d'entrée que le CLR appelle avant d'appeler une méthode dans un assembly?

+2

Pourquoi? Qu'essayez-vous de faire? – SLaks

+1

@SLaks: Pourquoi? Dans une bibliothèque d'utilitaire statique avec plusieurs classes, vous ne voulez pas que chaque méthode ou cctor dans chaque classe appelle un initialiseur global, violant DRY. Aussi, comparez le 'DllMain' existant, il était là pour un but (et avait ses inconvénients). Si vous voulez accrocher ou détourner des méthodes ou voulez utiliser votre propre [AssemblyResolver] (http://msdn.microsoft.com/en-us/library/system.appdomain.assemblyresolve%28VS.71%29.aspx) ou faire d'autres tâches liées à l'assemblage. Quelques exemples dans la nature sont le module System.Data et [msvcm80.dll] (http://blogs.msdn.com/b/junfeng/archive/2005/11/19/494914.aspx) – Abel

Répondre

4

Normalement, je ne répondrais pas à ma propre question, mais pendant ce temps, j'ai trouvé une réponse qui n'est pas venue ici auparavant, alors je vais y aller.

Après quelques recherches, je me trouvais sur this post by Microsoft, ce qui explique les problèmes de mélange géré et le code non géré à l'intérieur DllMain et la solution, ce qui est arrivé avec la version 2 de la CLI, initializers module. Quote:

Ce initialiseur court juste après la DllMain native (en d'autres termes, en dehors de verrouillage du chargeur), mais avant tout code managé est géré ou géré des données est accessible à partir ce module. La sémantique du module.cctor sont très similaires à ceux de la classe .cctors et sont définies dans les normes ECMA C# et Common Language Infrastructure .

Alors que je ne pouvais trouver le terme Module initialiseur dans la spécification ECMA actuelle, il en découle logiquement de initialiseur de type et la <Module> mondiale classe spéciale (voir la section 22.26 sur MethodDef, sous point 40). Cette fonctionnalité a été implémentée après .NET 1.1 (c'est-à-dire à partir de la version 2.0). Voir aussi this semi-official description. Cette question ne portait pas sur C#, mais parce que c'est la lingua franca de .NET: C# ne connaît pas les méthodes globales, et vous ne pouvez pas créer un <Module>, et encore moins son cctor. Cependant, Einar Egilsson a recognized this apparent deficiency et a créé InjectModuleInitializer.exe qui vous permet de le faire en tant qu'étape de publication/compilation à partir de Visual Studio. En C++. NET, l'utilisation de cette méthode est pratique triviale et recommandée à la place de DllMain. Voir aussi this SO answer by Ben Voigt (pas la réponse acceptée) et ceci SO answer by yoyoyoyosef. En bref, l'initialiseur de module est la première méthode qui est appelée après le chargement du module (pas nécessairement lors du chargement de l'assemblage!) Et avant d'appeler une méthode de classe ou d'instance. Il ne prend aucun paramètre, ne renvoie aucune valeur, mais peut contenir n'importe quel code managé dans son corps.

+0

http://stackoverflow.com/questions/1915506 – user423430

1

Ceci est par nature: il minimise le couplage entre les constructeurs statiques. Vous savez que votre cctor sera invoqué avant l'initialisation de tout élément de votre classe et après les ccteurs de toutes les classes utilisées par votre classe. Mais il n'y a aucune garantie sur quand il sera exécuté par rapport à des classes non apparentées dans la même application.

Si vous voulez vous assurer que votre code s'exécute avant le point d'entrée, pensez à écrire un wrapper pour l'application principale. Un moyen simple serait de le mettre dans un exécutable séparé.

Une façon plus autonome à faire peut-être à:

  1. Exécuter tout le code de démarrage est nécessaire, dans l'ordre. Ne référencez aucun type dans les assemblys qui ne doivent pas être initialisés.
  2. Créer votre propre application domaine
  3. Exécutez le véritable point d'entrée dans ce deuxième application domaine
+1

Ceci est probablement le chemin prendre, j'avais peur de cette réponse surgir. Un exécutable distinct, btw, sera peu utile lorsque votre assembly n'est pas une application en soi, ou est utilisé dans des environnements hébergés (comme ASP.NET). – Abel

2

En fait, il est pas tout à fait vrai que cctor est appelé en premier. Si vous avez un champ statique initialisé par une méthode statique, cette méthode statique sera appelée.

Jetez un oeil à ce code:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace CallSequence 
{ 
    internal class Test 
    { 
     internal Test() 
     { 
      Console.WriteLine("non-static constructor"); 
     } 

     static Test() 
     { 
      Console.WriteLine("static constructor"); 
     } 

     static int myField = InitMyField(); 

     static int InitMyField() 
     { 
      Console.WriteLine("static method : (InitMyField)"); 
      return 0; 
     } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      Test t = new Test(); 
     } 
    } 
} 

EDIT: Pensez également à utiliser Factory pattern qui vous aidera à faire toute l'initialisation nécessaire avant de retourner l'objet créé.

+0

Excuses, ma note * "(simplifiée)" *, que j'avais dans ma première version, a été enlevée par moi par accident. Je sais que les champs statiques sont initialisés avant le cctor statique et que leur ordre n'est pas défini. Cela ne fait que renforcer les arguments pour qu'une méthode soit appelée avant tout cela. Note: J'ai édité mon q. pour refléter cette ambiguïté apparente, merci de mentionner ;-) – Abel

+0

Toute réponse sur la façon de définir * quoi * le premier entrypoint est? – Abel

+0

Les premiers points d'entrée sont des initialiseurs statiques et un constructeur Je ne suis pas sûr que ce soit ce dont vous avez besoin, mais il est possible que le modèle Factory puisse être utile si vous devez faire quelque chose comme preinit. – Incognito