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

新手上路,请多包涵

事实上,我有一个 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”参数。

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

原文由 Jsncrdnl 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.5k
2 个回答

与其他一些答案的建议相反,使用 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 环境变量中列出的目录。请注意,这不包括 App Paths 注册表项指定的每个应用程序路径。计算 DLL 搜索路径时不使用 App Paths 键。

因此,除非您将 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/Invoke 这个函数。声明如下所示:

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

原文由 Cody Gray 发布,翻译遵循 CC BY-SA 4.0 许可协议

//[?] Method Sample;
[System.Runtime.InteropServices.DllImport("ramdom_Kernel32.dll")] //[!] Error Sample
public dynamic MethodWithDllImport(){

}

partial static Main(){
  try{
    //[?] Exception Cannot Be Handled over the Attribute;
    //    handle where it is called;
    MethodWithDllImport();
  }
  catch{
   //[?] use overloaded\other name methods
  }
}

原文由 H3sDW11e 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题