一尘不染

如何在运行时指定[DllImport]路径?

c#

实际上,我有一个C ++(工作中)的DLL,我想导入到我的C#项目中以调用它的功能。

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

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等),具体取决于运行该文件的计算机/会话。

所以我希望我的代码更加通用,例如:

/* 
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”参数。

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


阅读 1154

收藏
2020-05-19

共1个答案

一尘不染

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

老实说,我不明白为什么您不能像世界上其他所有人一样,并指定DLL 的 相对
路径。是的,在不同人的计算机上,应用程序的安装路径会有所不同,但这基本上是部署的通用规则。该DllImport机制的设计考虑到这一点。

实际上,它甚至DllImport无法解决问题。无论您是否使用方便的托管包装程序(P / Invoke
marshaller只是调用LoadLibrary),它都是控制事情的本机Win32
DLL加载规则。这些规则在这里详细列出,但是重要的规则在这里摘录:

在系统搜索DLL之前,它会检查以下内容:

  • 如果内存中已经加载了具有相同模块名称的DLL,则系统将使用已加载的DLL,无论它位于哪个目录中。系统都不会搜索该DLL。
    * 如果该DLL在运行该应用程序的Windows版本的已知DLL列表中,则系统使用其已知DLL的副本(以及已知DLL的从属DLL,如果有的话)。系统不搜索DLL。

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

  1. 从中加载应用程序的目录。
  2. 系统目录。使用该GetSystemDirectory函数获取此目录的路径。
  3. 16位系统目录。没有获取该目录路径的函数,但会对其进行搜索。
  4. Windows目录。使用该GetWindowsDirectory函数获取此目录的路径。
  5. 当前目录。
    6.
    PATH环境变量中列出的目录。请注意,这不包括“应用程序路径”注册表项指定的每个应用程序路径。计算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的默认搜索路径。当然,这样做的好处是您可以将 动态
值传递给在运行时计算的该函数。使用DllImport属性是不可能的,因此您仍将在此处使用相对路径(仅DLL的名称),并依靠新的搜索顺序为您找到它。

您必须P /调用此功能。声明看起来像这样:

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