How can I specify a [DllImport] path at runtime?
实际上,我有一个C ++(工作中)的DLL,我想导入到我的C#项目中以调用它的功能。
当我指定DLL的完整路径时,它确实可以工作,如下所示:
1 2 3 | string str ="C:\\\\Users\\\\userName\\\\AppData\\\\Local\\\\myLibFolder\\\\myDLL.dll"; [DllImport(str, CallingConvention = CallingConvention.Cdecl)] public static extern int DLLFunction(int Number1, int Number2); |
问题在于它将是一个可安装的项目,因此用户的文件夹将是不同的(例如:pierre,paul,jack,mum,dad等),取决于要在其上运行的计算机/会话。
所以我希望我的代码更加通用,例如:
1 2 3 4 5 6 7 8 9 10 11 12 | /* 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); |
重要的是," DllImport"需要DLL目录的" const string"参数。
所以我的问题是::
在这种情况下可以做什么?
与某些其他答案的建议相反,使用
老实说,我不明白为什么您不能像世界上其他所有人一样,并指定DLL的相对路径。是的,在不同人的计算机上,应用程序的安装路径会有所不同,但这基本上是部署的通用规则。设计
实际上,甚至不是
Before the system searches for a DLL, it checks the following:
- If a DLL with the same module name is already loaded in memory, the system uses the loaded DLL, no matter which directory it is in. The system does not search for the DLL.
- If the DLL is on the list of known DLLs for the version of Windows on which the application is running, the system uses its copy of the known DLL (and the known DLL's dependent DLLs, if any). The system does not search for the DLL.
If
SafeDllSearchMode is enabled (the default), the search order is as follows:The directory from which the application loaded. The system directory. Use the GetSystemDirectory function to get the path of this directory.The 16-bit system directory. There is no function that obtains the path of this directory, but it is searched. The Windows directory. Use the GetWindowsDirectory function to get the path of this directory.The current directory. The directories that are listed in the PATH environment variable. Note that this does not include the per-application path specified by the App Paths registry key. The App Paths key is not used when computing the DLL search path.
因此,除非您为DLL命名与系统DLL相同(除非在任何情况下都不应该这样做),否则默认搜索顺序将开始在加载应用程序的目录中查找。如果在安装过程中将DLL放在此处,则会找到它。如果仅使用相对路径,所有复杂的问题都会消失。
写就好了:
1 2 | [DllImport("MyAppDll.dll")] // relative path; just give the DLL's name static extern bool MyGreatFunction(int myFirstParam, int mySecondParam); |
但是,如果由于某种原因该方法不起作用,并且您需要强制应用程序在DLL的其他目录中查找,则可以使用
请注意,根据文档:
After calling
SetDllDirectory , the standard DLL search path is:The directory from which the application loaded. The directory specified by the lpPathName parameter.The system directory. Use the GetSystemDirectory function to get the path of this directory.The 16-bit system directory. There is no function that obtains the path of this directory, but it is searched. The Windows directory. Use the GetWindowsDirectory function to get the path of this directory.The directories that are listed in the PATH environment variable.
因此,只要您在第一次调用从DLL导入的函数之前调用此函数,就可以修改用于定位DLL的默认搜索路径。当然,这样做的好处是您可以将动态值传递给在运行时计算的该函数。使用
您必须P /调用此功能。声明看起来像这样:
1 2 | [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] static extern bool SetDllDirectory(string lpPathName); |
比Ran建议使用
我使用此方法在运行时选择是加载32位还是64位本机DLL,而无需修改大量P / Invoke-d函数。将加载代码粘贴到具有导入函数的类型的静态构造函数中,即可正常工作。
如果您需要不在路径或应用程序位置上的.dll文件,那么我认为您不能这样做,因为
另一种可以帮助您完成我认为正在尝试的操作的替代方法是通过P / Invoke使用本机
为了使其易于使用,您可以将该委托设置为类中的一个字段,以便使用它就像调用成员方法一样。
编辑
这是一个有效的代码片段,并显示了我的意思。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | class Program { static void Main(string[] args) { var a = new MyClass(); var result = a.ShowMessage(); } } class FunctionLoader { [DllImport("Kernel32.dll")] private static extern IntPtr LoadLibrary(string path); [DllImport("Kernel32.dll")] private static extern IntPtr GetProcAddress(IntPtr hModule, string procName); public static Delegate LoadFunction< T >(string dllPath, string functionName) { var hModule = LoadLibrary(dllPath); var functionAddress = GetProcAddress(hModule, functionName); return Marshal.GetDelegateForFunctionPointer(functionAddress, typeof (T)); } } public class MyClass { static MyClass() { // Load functions and set them up as delegates // This is just an example - you could load the .dll from any path, // and you could even determine the file location at runtime. MessageBox = (MessageBoxDelegate) FunctionLoader.LoadFunction<MessageBoxDelegate>( @"c:\\windows\\system32\\user32.dll","MessageBoxA"); } private delegate int MessageBoxDelegate( IntPtr hwnd, string title, string message, int buttons); /// <summary> /// This is the dynamic P/Invoke alternative /// </summary> static private MessageBoxDelegate MessageBox; /// <summary> /// Example for a method that uses the"dynamic P/Invoke" /// </summary> public int ShowMessage() { // 3 means"yes/no/cancel" buttons, just to show that it works... return MessageBox(IntPtr.Zero,"Hello world","Loaded dynamically", 3); } } |
注意:我没有费心使用
只要您知道在运行时可以找到C ++库的目录,这应该很简单。我可以清楚地看到在您的代码中就是这种情况。您的
1 | string str = Path.GetTempPath() +"..\\\\myLibFolder\\\\myDLL.dll"; |
现在,您可以使用const字符串继续使用DllImport语句,如下所示:
1 2 | [DllImport("myDLL.dll", CallingConvention = CallingConvention.Cdecl)] public static extern int DLLFunction(int Number1, int Number2); |
在运行时,在调用
1 2 | string assemblyProbeDirectory = Path.GetTempPath() +"..\\\\myLibFolder\\\\myDLL.dll"; Directory.SetCurrentDirectory(assemblyProbeDirectory); |
这只是指示CLR在程序运行时获得的目录路径中查找非托管C ++库。
在配置文件中设置dll路径
1 |
在您的应用中调用dll之前,请执行以下操作
1 2 3 | string dllPath= ConfigurationManager.AppSettings["dllPath"]; string appDirectory = Path.GetDirectoryName(dllPath); Directory.SetCurrentDirectory(appDirectory); |
然后调用dll,您可以像下面这样使用
1 2 | [DllImport("myDLL.dll", CallingConvention = CallingConvention.Cdecl)] public static extern int DLLFunction(int Number1, int Number2); |
只要dll位于系统路径上的某个位置,在没有指定完整路径的情况下DllImport即可正常工作。您可能可以将用户的文件夹临时添加到路径。
如果全部失败,只需将DLL放在
指定要从中加载的DLL:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | using System; using System.Runtime.InteropServices; class Example { // Use DllImport to import the Win32 MessageBox function. [DllImport ("user32.dll", CharSet = CharSet.Auto)] public static extern int MessageBox (IntPtr hWnd, String text, String caption, uint type); static void Main() { // Call the MessageBox function using platform invoke. MessageBox (new IntPtr(0),"Hello, World!","Hello Dialog", 0); } } |
源和更多