【问题标题】:Call function from DLL with non-static path使用非静态路径从 DLL 调用函数
【发布时间】:2010-10-18 19:43:58
【问题描述】:

我有一个需要从中访问方法的 DLL。

在大多数情况下,我只是使用 [DllImport] 来访问来自非托管程序集的方法,但在这种情况下,问题在于它在实例化时需要 DLL 的路径,因此需要一个常量字符串。

这个特定的 DLL 是随我的应用程序一起安装的,我无法保证在安装程序后它会在哪里(我宁愿不把它放在像 %SystemRoot% 这样的静态位置)。

那么在 C# 中有没有一种方法可以在运行时通过变量路径声明和使用来自 DLL 的方法?

任何想法或建议将不胜感激!

【问题讨论】:

  • 安装时将路径放入注册表中,并在加载dll之前将其调出。
  • 我看不出这对我有什么帮助,在运行时获取 dll 的路径不是问题。它使用我试图弄清楚的动态路径在代码中声明函数。

标签: c# .net dll interop runtime


【解决方案1】:

我也遇到过类似的情况。我使用安装在机器上的 SDK 中的 DLL。我从该 SDK 注册表项中获取 DLL 的目录位置。我在执行用户的 PATH 变量上设置了 DLL 位置(仅临时修改)。基本上,它允许您为要调用的 DLL 设置动态路径,因此不必来自注册表。请注意 PATH 变量是 Windows 查找 DLL 的最后一个位置。但另一方面,它不会改变 Windows 查找 DLL 的其他位置。

例子:

我想在 DLL 上调用的 API:

[DllImport("My.DLL")]
private static extern IntPtr ApiCall(int param);

获取注册表项(您需要使用 Microsoft.Win32;):

private static string GetRegistryKeyPath() {
        string environmentPath = null;

        using (var rk = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\SOMENNAME"))
        {
            if (rk != null)
            {
                environmentPath = rk.GetValue("Path(or whatever your key is)").ToString();
            }
            if (string.IsNullOrEmpty(environmentPath))
            {
                Log.Warn(
                    string.Format("Path not found in Windows registry, using key: {0}. Will default to {1}",
                         @"SOFTWARE\SOMETHING", @"C:\DefaultPath"));
                environmentPath = @"C:\DefaultPath";
            }
        }
        return environmentPath;
     }

在 PATH var 上添加 DLL 的路径(Concat() 在 Linq 中找到):

void UpdatePath(IEnumerable<string> paths){
    var path = new[] { Environment.GetEnvironmentVariable("PATH") ?? "" };
    path = path.Concat(paths);
    string modified = string.Join(Path.PathSeparator.ToString(), path);
    Environment.SetEnvironmentVariable("PATH", modified);
}

开始使用 API 调用:

var sdkPathToAdd = GetRegistryKeyPath();
IList<string> paths = new List<string>
        {
            Path.Combine(sdkPathToAdd),
            Path.Combine("c:\anotherPath")
        };
UpdatePath(paths);

//Start using
ApiCall(int numberOfEyes);

【讨论】:

    【解决方案2】:

    这有点hack,但是既然你说你可以在运行时找到dll的路径,为什么不在你使用任何函数之前将它复制到你当前的工作目录呢?这样,dll 将存在于您的 exe 旁边,并且会被 LoadLibrary 找到。无需在您的 DllImport 中添加任何其他路径。

    使用动态路径中的方法的唯一其他方法是这样做:
    1) 为LoadLibrary & GetProcAddress 做必要的 P/Invoke 签名
    2) 从所需路径加载库 (LoadLibrary)
    3) 找到所需的函数 (GetProcAddress)
    4) 将指针投射到委托Marshal.GetDelegateForFunctionPointer
    5) 调用它。

    当然,您需要以这种方式为要“导入”的每个函数声明一个委托,因为您必须将指针强制转换为委托。

    【讨论】:

      【解决方案3】:

      根本不使用路径。在尝试动态或静态加载函数时,Windows 使用默认的 DLL 搜索方法。

      确切的搜索逻辑记录在 MSDN 的 LoadLibrary 文档中 - 基本上,如果 DLL 仅由您的应用程序使用,请在安装期间将其放在与您的应用程序相同的文件夹中,不用担心。如果是常用的DLL,把它放在LoadLibrary()搜索到的文件夹结构中的某个位置,就会被找到。

      【讨论】:

      • 在这个特定的实例中,我无法将 DLL 放在与我的应用程序相同的目录中,它位于我的应用程序的子目录中,我正在尝试使用相对路径来声明它.这适用于某些机器,但不一致。 =\
      • 尝试从 Kernel32 导入 SetDllDirectory 并将其命名为设置 Application.StartupPath。当我今天回家时,我将在家里用我的一个项目来尝试这个,当我测试它时我会发表评论。希望有帮助
      • 这很粗糙。我能想到的唯一两种可能性是在安装期间将 DLL 的路径添加到系统 PATH,或者编写不安全的代码以在运行时动态手动使用 LoadLibrary() 和 GetProcAddress()。不过,也许其他人有更好的想法。
      • 更好的想法,比如 Zack 的。 :-) 干得好。
      • 感谢 Ken 和 Zack 的想法,遗憾的是,经过进一步调查后,我认为我的手上可能有更大的问题,因为即使将相关 DLL 放在与我的目录相同的目录中,也不会加载应用程序(在这台特定的机器上),所以在找到解决方案之前我还需要进行一些调试。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-18
      相关资源
      最近更新 更多