En fait, j'ai une DLL C ++ (fonctionnelle) que je veux importer dans mon projet C # pour appeler ses fonctions.
Cela fonctionne lorsque je spécifie le chemin complet de la DLL, comme ceci:
string str = "C:\\Users\\userName\\AppData\\Local\\myLibFolder\\myDLL.dll";
[DllImport(str, CallingConvention = CallingConvention.Cdecl)]
public static extern int DLLFunction(int Number1, int Number2);
Le problème est que ce sera un projet installable, donc le dossier de l'utilisateur ne sera pas le même (ex: pierre, paul, jack, maman, papa, ...) en fonction de l'ordinateur / session sur lequel il sera exécuté.
Alors j'aimerais que mon code soit un peu plus générique, comme ceci:
/*
goes right to the temp folder of the user
"C:\\Users\\userName\\AppData\\Local\\temp"
then go to parent folder
"C:\\Users\\userName\\AppData\\Local"
and finally go to the DLL's folder
"C:\\Users\\userName\\AppData\\Local\\temp\\myLibFolder"
*/
string str = Path.GetTempPath() + "..\\myLibFolder\\myDLL.dll";
[DllImport(str, CallingConvention = CallingConvention.Cdecl)]
public static extern int DLLFunction(int Number1, int Number2);
Le gros problème est que "DllImport" souhaite un paramètre "const string" pour le répertoire de la DLL.
Ma question est donc: que pourrait-on faire dans ce cas?
Réponses:
Contrairement aux suggestions de certaines des autres réponses, l'utilisation de l'
DllImport
attribut reste la bonne approche.Honnêtement, je ne comprends pas pourquoi vous ne pouvez pas faire comme tout le monde dans le monde et spécifier un chemin relatif vers votre DLL. Oui, le chemin dans lequel votre application sera installée diffère sur les ordinateurs de différentes personnes, mais c'est fondamentalement une règle universelle en matière de déploiement. Le
DllImport
mécanisme est conçu dans cet esprit.En fait, ce n'est même pas cela
DllImport
qui le gère. Ce sont les règles de chargement de la DLL Win32 native qui régissent les choses, que vous utilisiez ou non les wrappers gérés pratiques (le marshaller P / Invoke appelle simplementLoadLibrary
). Ces règles sont énumérées en détail ici , mais les plus importantes sont extraites ici:Ainsi, à moins que vous ne nommiez votre DLL de la même manière qu'une DLL système (ce que vous ne devriez évidemment pas faire, jamais, en aucune circonstance), l'ordre de recherche par défaut commencera à chercher dans le répertoire à partir duquel votre application a été chargée. Si vous placez la DLL là pendant l'installation, elle sera trouvée. Tous les problèmes compliqués disparaissent si vous utilisez simplement des chemins relatifs.
Ecrivez:
Mais si cela ne fonctionne pas pour une raison quelconque et que vous devez forcer l'application à rechercher la DLL dans un autre répertoire, vous pouvez modifier le chemin de recherche par défaut à l'aide de la
SetDllDirectory
fonction .Notez que, selon la documentation:
Ainsi, tant que vous appelez cette fonction avant d'appeler la fonction importée de la DLL pour la première fois, vous pouvez modifier le chemin de recherche par défaut utilisé pour localiser les DLL. L'avantage, bien sûr, est que vous pouvez transmettre une valeur dynamique à cette fonction qui est calculée au moment de l'exécution. Ce n'est pas possible avec l'
DllImport
attribut, donc vous utiliserez toujours un chemin relatif (le nom de la DLL uniquement) là-bas, et comptez sur le nouvel ordre de recherche pour le trouver pour vous.Vous devrez P / Invoquer cette fonction. La déclaration ressemble à ceci:
la source
.dll
et d'autres systèmes ajouteront l'extension appropriée sous Mono (par exemple.so
sous Linux). Cela peut aider si la portabilité est un problème.SetDllDirectory
. Vous pouvez également simplement changerEnvironment.CurrentDirectory
et tous les chemins relatifs seront évalués à partir de ce chemin!AddDllDirectory
d'un autre côté ...DllImport
c'est plus qu'un simple wrapperLoadLibrary
. Il considèreextern
également le répertoire de l'assembly dans lequel la méthode est définie . LesDllImport
chemins de recherche peuvent être en outre limités en utilisantDefaultDllImportSearchPath
.Mieux encore que la suggestion d'utilisation de Ran
GetProcAddress
, faites simplement l'appel àLoadLibrary
avant tout appel auxDllImport
fonctions (avec seulement un nom de fichier sans chemin) et ils utiliseront automatiquement le module chargé.J'ai utilisé cette méthode pour choisir au moment de l'exécution de charger une DLL native 32 bits ou 64 bits sans avoir à modifier un tas de fonctions P / Invoke-d. Collez le code de chargement dans un constructeur statique pour le type contenant les fonctions importées et tout fonctionnera correctement.
la source
FunctionLoader
code.Si vous avez besoin d'un fichier .dll qui ne se trouve pas sur le chemin ou sur l'emplacement de l'application, je ne pense pas que vous puissiez le faire, car
DllImport
c'est un attribut et les attributs ne sont que des métadonnées définies sur les types, les membres et autres éléments de langage.Une alternative qui peut vous aider à accomplir ce que je pense que vous essayez est d'utiliser le natif
LoadLibrary
via P / Invoke, afin de charger un .dll à partir du chemin dont vous avez besoin, puis de l'utiliserGetProcAddress
pour obtenir une référence à la fonction dont vous avez besoin à partir de ce .dll. Utilisez ensuite ces derniers pour créer un délégué que vous pouvez appeler.Pour faciliter son utilisation, vous pouvez ensuite définir ce délégué sur un champ de votre classe, de sorte que son utilisation ressemble à l'appel d'une méthode membre.
ÉDITER
Voici un extrait de code qui fonctionne et montre ce que je voulais dire.
Remarque: je n'ai pas pris la peine de l'utiliser
FreeLibrary
, donc ce code n'est pas complet. Dans une application réelle, vous devez prendre soin de libérer les modules chargés pour éviter une fuite de mémoire.la source
Tant que vous connaissez le répertoire dans lequel vos bibliothèques C ++ peuvent être trouvées au moment de l'exécution, cela devrait être simple. Je vois clairement que c'est le cas dans votre code. Votre
myDll.dll
serait présent à l'intérieur dumyLibFolder
répertoire dans le dossier temporaire de l'utilisateur actuel.Vous pouvez maintenant continuer à utiliser l'instruction DllImport en utilisant une chaîne const comme indiqué ci-dessous:
Juste au moment de l'exécution avant d'appeler la
DLLFunction
fonction (présente dans la bibliothèque C ++), ajoutez cette ligne de code dans le code C #:Cela demande simplement au CLR de rechercher les bibliothèques C ++ non gérées dans le chemin du répertoire que vous avez obtenu au moment de l'exécution de votre programme.
Directory.SetCurrentDirectory
call définit le répertoire de travail actuel de l'application sur le répertoire spécifié. Si votremyDLL.dll
est présent sur le chemin représenté parassemblyProbeDirectory
chemin, il sera chargé et la fonction souhaitée sera appelée via p / invoke.la source
définir le chemin dll dans le fichier de configuration
avant d'appeler la dll dans votre application, procédez comme suit
puis appelez la dll et vous pouvez utiliser comme ci-dessous
la source
DllImport fonctionnera correctement sans le chemin complet spécifié tant que la dll se trouve quelque part sur le chemin du système. Vous pourrez peut-être ajouter temporairement le dossier de l'utilisateur au chemin.
la source
Si tout échoue, placez simplement la DLL dans le
windows\system32
dossier. Le compilateur le trouvera. Spécifiez la DLL à partir de laquelle charger avec:,DllImport("user32.dll"...
définissezEntryPoint = "my_unmanaged_function"
pour importer la fonction non gérée souhaitée dans votre application C #:Source et encore plus d'
DllImport
exemples: http://msdn.microsoft.com/en-us/library/aa288468(v=vs.71).aspxla source