11
votes

Paramétrage DLLIMPORT pour une utilisation dans une application C #

Nous avons un fournisseur qui fournit une bibliothèque d'accès à leur matériel. Malheureusement, si vous avez plusieurs périphériques, vous devez importer plusieurs fois leur bibliothèque, avec différents noms DLL. En conséquence, nous avons une tonne métrique de code dupliqué et je crains que cela devienne bientôt être un cauchemar d'entretien.

Ce que nous avons en ce moment est quelque chose comme: p> xxx

... P>

        [DllImport(DLL_NAME, EntryPoint = "_function99")]
        public static extern int Function99(int param);
    }
}


2 commentaires

À propos de votre édition récente: ma "troisième approche" peut être ajustée à tout ce que vous aimez. Créez un type de délégué pour chaque méthode (vous aurez besoin de cela de toute façon et que vous avez déjà déjà comme dllimport s), ajoutez un tableau de paramètres pour les paramètres et utilisez des génériques pour faire les appels Typeafe. Holtez, je vais essayer de régler l'exemple.


Avec l'ajout de «réflexion déléguée» (TX à Eric LIPPERT) et de génériques, il corrige votre problème pour presque tous les appels P / invoke que vous pouvez rencontrer ;-)


3 Réponses :


15
votes

Quelques considérations:

Alternative #One

Edit: Cette approche nécessite de modifier des méthodes compilées, ce qui est difficile et nécessite une injection, une modification de montage ou d'autres méthodes couramment utilisées dans AOP-Terre. Envisager une approche deux ci-dessous, ce qui est plus facile.

  1. Supprimez toutes les fonctions avec la même signature, laissez-la un de chaque
  2. Utilisez getilasbytearray pour créer une méthode dynamique de votre dllimport méthode
  3. Utilisez La technique décrite ici < / a> Pour manipuler l'IL de la fonction, vous pouvez ici modifier les attributs Dllimport, etc.
  4. créer un délégué de ces fonctions et cache vos appels
  5. retourner le délégué

    Alternative #TWO:

    Edit: Cette approche alternative semble un peu impliquée au début, mais quelqu'un a déjà fait le travail pour vous. Look up Cet excellent articleProject article et simplement télécharger et utiliser son code pour créer dynamiquement Dllimport Méthodes de style. Fondamentalement, il s'agit de:

    1. Supprimer tout Dllimport
    2. Créez votre propre wrapper DllimPort: prend un nom de DLL et un nom de fonction (en supposant que toutes les signatures sont égales)
    3. Le wrapper fait un "manuel" dllimport avec LoadLibrary ou Loadlibraryex en utilisant Les fonctions de l'API Dllimport
    4. Le wrapper crée une méthode pour vous avec Methodbuilder .
    5. retourne un délégué à cette méthode que vous pouvez utiliser comme fonction.

      Alternative #Three

      Editer: Vous cherchez plus loin, il y a une approche plus facile: utilisez simplement DefinePinvokememethod qui fait tout ce dont vous avez besoin. Le lien MSDN donne déjà un bon exemple, mais une enveloppe complète capable de créer n'importe quelle DLL natif basée sur la DLL et le nom de la fonction est fournie sur cet article de codeProject .

      1. Supprimez toutes vos signatures de style DLLIMPORT
      2. Créez une méthode d'emballage simple autour de definepinvokememethod
      3. Assurez-vous d'ajouter une mise en cache simple (dictionnaire?) Pour éviter de construire la méthode entière sur chaque appel
      4. retourne un délégué de l'emballage.

        Voici comment cette approche regarde dans le code, vous pouvez réutiliser le délégué retourné autant que vous le souhaitez, le bâtiment coûteux de la méthode dynamique ne doit être effectué qu'une fois par méthode.

        éditer : Mise à jour de l'échantillon de code pour travailler avec n'importe quel délégué et refléter automatiquement le type de retour et les types de paramètres corrects de la signature du délégué. De cette façon, nous avons découplé la mise en œuvre complètement de la signature, qui est compte tenu de votre situation actuelle, le mieux que nous puissions faire. Avantages: Vous avez des types de sécurité et de changements monocommandés, ce qui signifie: très facilement gérable. xxx

        Autres approches sont possibles, je suppose (comme l'approche de la modélisation mentionnée Par quelqu'un d'autre dans ce fil).

        Mise à jour: Ajout d'un lien vers excellent articleProject article .
        MISE À JOUR: APPROCHE FACILE ET FAIRE AJOUTÉ.
        MISE À JOUR: Ajout de code Sample
        Mise à jour: échantillon de code mis à jour pour travailler de manière transparente avec n'importe quel prototype de fonction
        MISE À JOUR: ERREUR FIXE ERREURFULAIRE: TYPEOF (FUNCTION02) doit être TYPEOF (T) Bien sûr


2 commentaires

Vous ne pouvez pas modifier les attributs au moment de l'exécution, bien que vous puissiez les rendre mutables, mais cela impliquerait un comportement indéfini.


En fait, vous pouvez, mais seulement si vous utilisez la classe MethodyBuilder pour les créer au moment de l'exécution, mais que vous ne pouvez pas les modifier pour les méthodes compilées (à moins que vous n'utilisiez l'une des nombreuses excellentes injections / Aop bibliothèques).



4
votes

Que diriez-vous d'utiliser T4 (Toolkit de transformation de textes). Créez un fichier .tt avec le contenu suivant:

using System.Runtime.InteropServices;
namespace MyNamespace {

    public static class Device01
    {
        public const string DLL_NAME = @"Device01.dll";

        [DllImport(DLL_NAME, EntryPoint = "_function1")]
        public static extern int function1(byte[] param);
        [DllImport(DLL_NAME, EntryPoint = "_function2")]
        public static extern int function2(byte[] param);
        [DllImport(DLL_NAME, EntryPoint = "_function3")]
        public static extern int function3(byte[] param);

    }

    public static class Device02
    {
        public const string DLL_NAME = @"Device02.dll";

        [DllImport(DLL_NAME, EntryPoint = "_function1")]
        public static extern int function1(byte[] param);
        [DllImport(DLL_NAME, EntryPoint = "_function2")]
        public static extern int function2(byte[] param);
        [DllImport(DLL_NAME, EntryPoint = "_function3")]
        public static extern int function3(byte[] param);

    }

    public static class Device03
    {
        public const string DLL_NAME = @"Device03.dll";

        [DllImport(DLL_NAME, EntryPoint = "_function1")]
        public static extern int function1(byte[] param);
        [DllImport(DLL_NAME, EntryPoint = "_function2")]
        public static extern int function2(byte[] param);
        [DllImport(DLL_NAME, EntryPoint = "_function3")]
        public static extern int function3(byte[] param);

    }
}


1 commentaires

En regardant la complexité impliquée avec d'autres méthodes, je pense que c'est vraiment la meilleure solution à ce problème.



3
votes

Je suggérerais également simplement d'utiliser le loadlibrary et et getProcAddress .

Avec ce dernier, vous appelez simplement marshal.getdelegatefunconceCoincer avec un type de délégué correspondant à la signature de la méthode Pinvoke.


2 commentaires

+1 pour getdelegatefunconcerer , je ne pouvais pas le trouver quand je suis allé sur ma première solution. Il sera difficile d'obtenir les trois appels d'API pour travailler avec des paramètres variables et des types de retour pour les prototypes et maintenir toujours la sécurité de type.


@Abel: Pas vraiment 'TOO' HARD :)