CSharp - 如何在运行时指定[ dllimport]路径?

当我指定到DLL的完整路径时,它可以工作,如下所示:


string str ="C:UsersuserNameAppDataLocalmyLibFoldermyDLL.dll";
[DllImport(str, CallingConvention = CallingConvention.Cdecl)]
public static extern int DLLFunction(int Number1, int Number2);

我希望代码更通用一点,如下所示:


/* 
goes right to the temp folder of the user 
"C:UsersuserNameAppDataLocaltemp"
then go to parent folder
"C:UsersuserNameAppDataLocal"
and finally go to the DLL's folder
"C:UsersuserNameAppDataLocaltempmyLibFolder"
*/

string str = Path.GetTempPath() +"..myLibFoldermyDLL.dll"; 
[DllImport(str, CallingConvention = CallingConvention.Cdecl)]
public static extern int DLLFunction(int Number1, int Number2);

重要的是"dllimport"需要为DLL目录提供"const字符串"参数。

我的问题是:在这种情况下,可以做什么?

时间:

其他一些答案的建议相反,使用 DllImport 属性仍然是正确的方法。

老实说,我不明白为什么你不能像世界上其他人那样做,并为你的DLL指定一个相对平坦的路径。 是的,你的应用程序将要安装的路径在不同的人的计算机上不同,但在部署时基本上是一个通用规则。 DllImport 机制的设计思路如下。

实际上,甚至不是 DllImport 处理它。 它是本机的Win32动态链接库加载规则,不管你是否使用了方便的托管包装( /调用编组器只调用 LoadLibrary ) 。 这些规则在这里列举了非常详细的,但重要的是这里摘录:

在系统启动前的英镑。对于一个 DLL,它检查以下内容:

  • 如果已经在内存中加载具有相同模块名称的DLL,系统将使用加载的DLL,无论它在哪个目录中。 系统不搜索 DLL 。
  • 如果dll的List 认识dll的版本 Windows的应用程序运行时,系统使用已知的dll( 和已知的dlldll依赖,如果任何) 副本。 系统不搜索 DLL 。

如果启用了 SafeDllSearchMode ( 默认值), 搜索顺序如下:

  1. 应用程序加载的目录。
  2. 系统目录。使用 GetSystemDirectory 函数获取这里目录的路径。
  3. 16位 系统目录。没有获得这里目录路径的函数,但它被搜索。
  4. Windows 目录。使用 GetWindowsDirectory 函数获取这里目录的路径。
  5. 当前目录。
  6. PATH 环境变量中列出的目录。 注意,这不包括应用程序路径注册表项指定的per-application路径。 在计算DLL搜索路径时不使用应用程序路径键。

因此,除非你将DLL命名为系统 DLL ( 你显然不会在任何情况下),否则默认搜索顺序将开始在你的应用程序加载的目录中查找。 如果在安装过程中将DLL放在那里,则会发现。 如果你只使用相对路径,所有复杂的问题都会消失。

只需写:


[DllImport("MyAppDll.dll")]//relative path; just give the DLL's name
static extern bool MyGreatFunction(int myFirstParam, int mySecondParam);

但如果并不工作不管出于什么原因,和你需要迫使应用程序dll在不同的目录,你可以修改默认搜索路径使用 SetDllDirectory 函数。
请注意,按照以下文档:

调用 SetDllDirectory 之后,标准的DLL搜索路径如下:

  1. 应用程序加载的目录。
  2. lpPathName 参数指定的目录。
  3. 系统目录。使用 GetSystemDirectory 函数获取这里目录的路径。
  4. 16位 系统目录。没有获得这里目录路径的函数,但它被搜索。
  5. Windows 目录。使用 GetWindowsDirectory 函数获取这里目录的路径。
  6. PATH 环境变量中列出的目录。

只要你调用这个函数之前调用dll中的函数导入第一次,你可以修改默认搜索路径用于定位dll。 当然,好处是你可以将一个动态值传递给位于run-time的函数。 这对于 DllImport 属性是不可能的,所以你仍然可以使用相对路径( 只有DLL的名称),并依靠新的搜索来找到它。

你必须调用这里函数。 声明如下所示:


[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool SetDllDirectory(string lpPathName);

甚至比使用GetProcAddress的建议更好,只需在调用DllImport函数( 只带有不带路径的文件名) 之前调用 LoadLibrary,然后它们会自动使用加载的模块。

我使用了这个方法来在运行时选择是否加载 32位 或者 64位 原生DLL而不必修改一组p/invoke-d函数。 将加载代码粘贴到具有导入函数的类型的静态构造函数中,它将工作正常。

下面是一个代码片段,显示了我的意思。


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:windowssystem32user32.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);
 }
}

注:我没有使用FreeLibrary,所以此代码不完整,在实际应用程序中,你应该注意释放已加载的模块以避免内存泄漏。

如果没有指定完整路径,DllImport将正常工作,只要dll位于系统路径的某个位置,你可以将用户的文件夹临时添加到路径中。

如果全部失败,只需将DLL放入windowssystem32文件夹,编译器会找到它,指定要从其加载的DLL : DllImport("user32.dll"...,EntryPoint ="my_unmanaged_function"要将你想要的非托管功能导入到你的C#应用程序,请执行以下操作:


 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); 
 }
}

源代码和更多DllImport例子: http://msdn.microsoft.com/en-us/library/aa288468(v=vs.71).aspx

...