12
votes

C # équivalent de DLLMain en C (Winapi)

J'ai une application plus ancienne (Ca. 2005) qui accepte les plugins DLL. L'application a été conçue à l'origine pour les plug-ins Win32 C, mais j'ai un modèle de DLL C # de travail. Mon problème: Je dois faire une initialisation ponctuelle, qui dans une DLL Win32 C serait effectuée dans DLLMAIN:

public static bool DllMain(int hModule, int reason, IntPtr lpReserved) {
  [one time stuff here...]
}


4 commentaires

initialisation ponctuelle de quoi? Depuis que DllMain est procédural, et c # est orienté objet, je me demande quelle initialisation vous devez effectuer à l'extérieur d'une classe qui ne pouvait pas être appelée de l'intérieur?


Cela dépend également du type d'application que vous construisez, console, Web, Windows Client, Windows Service, etc.


Vous ne devriez pas faire une initialisation significative dans dllmain .


@Ritch Melton pense à des choses comme Plugin Chargement lorsque l'application d'hébergement ne sait pas exactement ce qui est dans la DLL. Dans ce cas, vous voudrez peut-être que votre plugin enregistre avec l'application afin de pouvoir être trouvé en cas de problème sans attacher l'hôte au plugin.


4 Réponses :


17
votes

Donnez votre constructeur statique de classe A et faites votre initialisation là-bas. Il exécutera la première fois que n'importe qui appelle une méthode statique ou une propriété de votre classe ou construit une instance de votre classe.


6 commentaires

Cela fonctionne pour des trucs que vous faites dans l'affaire DLL_PROCESS_ATCH, mais qu'en est-il des autres cas (dll_process_detach, dll_thread_attach, dll_thread_attach)?


@Thomaslevesque qui n'était pas le problème de l'OP. "Je dois faire une initialisation unique." C # constructeurs statiques accomplit une initialisation unique.


Ouais, je sais que ce n'est pas ce que l'OP demandait ... mais c'est ce que Je suis Demander;)


@Thomaslevesque alors posez une nouvelle question. Ne détournez pas une question existante.


@Raymondchen sa question est pertinente alors pourquoi créer un nouveau fil s'il pouvait obtenir une réponse ici.


Parce que quelqu'un cherche la réponse à la deuxième question ne le trouvera pas. Ceci est un site de questions / réponses non un site de discussion.



6
votes

Pas facile à faire de C # Vous pouvez avoir un par Initializers de module

Les modules peuvent contenir des méthodes spéciales appelées initialisateurs de module pour initialiser le module lui-même. Tous les modules peuvent avoir un initialiseur de module. Cette méthode doit être statique, un membre du module, ne prend aucun paramètre, ne renvoie aucune valeur, être marquée avec RTSpecialName et SpecialName, et être nommé .CCTOR. Il n'y a pas de limitations sur quel code est autorisé dans un initialiseur de module. Les initialisateurs de module sont autorisés à exécuter et à appeler le code géré et non géré.


5 commentaires

Comment allez-vous utiliser cela?


Je pense que vous devrez peut-être désassembler et insérer directement le code IL.


Parapura est correct, il n'y a aucun moyen de le faire sans modifier directement l'IL ... j'espère que cela se fixe un jour.


Voir Stackoverflow.com/a/9739535/240845 (Utilisez INJECTMODULINITIALISANT INJECTMODULEITIALISANT EGILSSON)


Les initialisateurs de modules ne sont pas toujours un bon substitut de la fonctionnalité DLLMain car ils ne sont pas appelés jusqu'à ce qu'une méthode soit appelée dans la DLL. Si vous avez une situation dans laquelle une application héritée charge simplement une DLL et s'attend à ce que cela fasse quelque chose, les initialisateurs de module vous échoueront (comme ils m'a fait). Voir Stackoverflow.com/a/9745422/240845 ailleurs dans cette question pour une solution à ce problème.



2
votes

même si c # ne prend pas directement en charge l'initialisation du module, nous pouvons la mettre en œuvre à l'aide de réflexions et de constructeurs statiques. Pour ce faire, nous pouvons définir un attribut personnalisé et utiliser les classes qui doivent être initialisées sur le chargement du module: xxx pré>

sur les classes que nous devons initialiser immédiatement, nous ajoutons ce code à leur constructeur statique (qui sera exécuté une fois même si la propriété Getter est accessible plusieurs fois) et ajoutez l'attribut personnalisé que nous avons ajouté pour exposer cette fonctionnalité. P>

[InitOnLoad]
class foo
{
    private static bool loaded { get { return true; } }
    static foo() 
    {
        int i = 42;
    }
}


3 commentaires

Cela ne répond pas à la question que l'OP recherche un moyen d'interagir avec une application héritée.


@ Tritchmelton Je ne suis pas suivi. L'OP tente de faire une initialisation unique sur les DLL gérées par une application gérée ou d'une application non gérée qui héberge le CLR. Dans le premier cas, vous ajouteriez le code dans le constructeur de MyMainClass près du démarrage et dans la seconde, vous le mettriez probablement probablement dans la méthode que vous passez initialement à ExecuteInfaulAppDomaine . Dans les deux cas, vous obtenez la fonctionnalité que vous recherchez.


@Ritchmelton Il essaie de reproduire la fonctionnalité de DllMain dans une "DLL C #" Je ne vois toujours pas comment il est pertinent si le processus d'hébergement est géré ou non.



12
votes

J'ai dû interagir avec une application héritée probablement dans la même situation que vous. J'ai trouvé un chemin hacky pour obtenir une fonctionnalité DllMain dans une assemblée CLR. Heureusement, ce n'est pas trop difficile. Il nécessite une DLL supplémentaire, mais elle ne vous oblige pas à déployer em> une DLL supplémentaire afin que vous puissiez toujours avoir le "Mettez une DLL dans ce répertoire et l'application le chargera" paradigme.

premier , vous faites une simple dll C ++ régulière qui ressemble aux éléments suivants: p>

dllmain.cpp: p> xxx pré>

notez la création de fil. Il s'agit de garder Windows heureux car l'appelant code géré dans un point d'entrée DLL est un NO-NON. P>

Ensuite, vous devez créer cette fonction LaunchDll le code ci-dessus. Références. Cela va dans un fichier séparé car il sera compilé comme une unité de code gérée C ++. Pour ce faire, créez d'abord le fichier .CPP (je l'ai appelé launchdll.cpp). Ensuite, cliquez avec le bouton droit de la souris sur ce fichier dans votre projet et dans Propriétés de la configuration em> strong> -> c / c ++ em> strong> -> Général em> strong> Changez le support EM> EM> Strong> Entrée sur Prise en charge de la langue courante (/ CLR ) em> strong>. Vous ne pouvez pas avoir d'exceptions, de reconstruction minimale, de vérifications d'exécution et probablement d'autres choses que j'ai oubliée, mais le compilateur vous dira. Lorsque le compilateur se plaint, suivez les paramètres de la valeur par défaut et changez-les sur le fichier launchdll.cpp seulement em>. P>

launchdll.cpp: p>

IDR_DLLENCLOSED DLL "C:\\Path\\to\\Inner.dll"


4 commentaires

Ou courez une étape post-construction pour modifier votre assembly C #: Stackoverflow.com/questions/1915506


@MHEYMAN: Mais le nouveau "wrapper.dll" ne peut pas être utilisé comme le Inner.dll aurait été utilisé par l'application héritée. L'INTERN.DLL est AC # Assembly, mon application Legacy appelle Assembly.getTypes () sur le Inner.dll et si j'utilise le wrapper.dll à la place, il retournera certaines choses C ++ / CLR au lieu de l'inner.dll les types. C'est un gros problème pour moi, comment puis-je exposer les types de Inner.dll?


@Blud avez-vous trouvé une solution à ce problème?


L'assemblage est chargé, vous pouvez donc le trouver en itérant tous les assemblages chargés. Mais, en utilisant FODY Nuget Package semble la solution la plus propre que si vous devez appeler un non géré. Dll.