使用非静态路径从DLL调用函数

时间:2022-09-01 18:36:51

I have a DLL that I need to access methods from.

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

In most cases like this I just use [DllImport] to access methods from unmanaged assemblies, but the problem with that in this situation is that it requires the path to the DLL at instantiation time, so a constant string.

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

This particular DLL is one that gets installed with my application and I can't guarantee where it will be after the program is installed (I'd rather not put it somewhere static like %SystemRoot%).

这个特殊的DLL是随我的应用程序安装的那个,我不能保证在程序安装后它会在哪里(我宁愿不把它放在像%SystemRoot%这样的静态)。

So is there a way in C# that I can declare and use a method from a DLL at runtime with a variable path?

那么在C#中是否有一种方法可以在运行时使用变量路径声明并使用DLL中的方法?

Any ideas or suggestions would be greatly appreciated!

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

3 个解决方案

#1


This is a bit of hack, but since you say that you can find the path to the dll at runtime, why not copy it to your current working directory before you use any of the functions? That way, the dll will exist next to your exe and will be found by LoadLibrary. No need for any additional path in your DllImport.

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

The only other way to use a method from a dynamic path is to do this:
1) Do the necessary P/Invoke signatures for LoadLibrary & GetProcAddress
2) Load the library from the desired path (LoadLibrary)
3) Find the desired function (GetProcAddress)
4) Cast the pointer to a delegate Marshal.GetDelegateForFunctionPointer
5) Invoke it.

从动态路径使用方法的唯一方法是:1)为LoadLibrary和GetProcAddress执行必要的P / Invoke签名2)从所需路径加载库(LoadLibrary)3)找到所需的函数(GetProcAddress) )4)将指针转换为委托Marshal.GetDelegateForFunctionPointer 5)调用它。

Of course, you will need to declare a delegate for each function you want to "import" in this way since you have to cast the pointer to a delegate.

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

#2


Don't use a path at all. Windows uses a default method of searching for DLLs when trying to dynamically or statically load a function from it.

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

The exact search logic is documented at MSDN in the docs for LoadLibrary - basically, if the DLL is just used by your app, put in the same folder as your application during the install and don't worry about it. If it's a commonly used DLL, put it somewhere in the folder structure searched by LoadLibrary() and it'll get found.

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

#3


I had a similar situation. I use DLLs from a SDK that is installed on the machine. I get the directory location of the DLLs from that SDKs registry key. I set the DLL location on the executing users PATH variable (only temporary modification). Basically it allows you to set a dynamic path for the DLL you want to invoke, so it don't have to be from registry. Mind that the PATH var is the last place Windows looks for DLLs. But on the other hand, it does not change the other places Windows looks for DLLs.

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

Example:

API i want to call, on the DLL:

我想在DLL上调用API:

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

Get the registry key (you need using Microsoft.Win32;):

获取注册表项(您需要使用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;
     }

Add the path of the DLL on the PATH var (Concat() is found in Linq):

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

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

Start Using the API call:

开始使用API​​调用:

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

//Start using
ApiCall(int numberOfEyes);

#1


This is a bit of hack, but since you say that you can find the path to the dll at runtime, why not copy it to your current working directory before you use any of the functions? That way, the dll will exist next to your exe and will be found by LoadLibrary. No need for any additional path in your DllImport.

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

The only other way to use a method from a dynamic path is to do this:
1) Do the necessary P/Invoke signatures for LoadLibrary & GetProcAddress
2) Load the library from the desired path (LoadLibrary)
3) Find the desired function (GetProcAddress)
4) Cast the pointer to a delegate Marshal.GetDelegateForFunctionPointer
5) Invoke it.

从动态路径使用方法的唯一方法是:1)为LoadLibrary和GetProcAddress执行必要的P / Invoke签名2)从所需路径加载库(LoadLibrary)3)找到所需的函数(GetProcAddress) )4)将指针转换为委托Marshal.GetDelegateForFunctionPointer 5)调用它。

Of course, you will need to declare a delegate for each function you want to "import" in this way since you have to cast the pointer to a delegate.

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

#2


Don't use a path at all. Windows uses a default method of searching for DLLs when trying to dynamically or statically load a function from it.

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

The exact search logic is documented at MSDN in the docs for LoadLibrary - basically, if the DLL is just used by your app, put in the same folder as your application during the install and don't worry about it. If it's a commonly used DLL, put it somewhere in the folder structure searched by LoadLibrary() and it'll get found.

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

#3


I had a similar situation. I use DLLs from a SDK that is installed on the machine. I get the directory location of the DLLs from that SDKs registry key. I set the DLL location on the executing users PATH variable (only temporary modification). Basically it allows you to set a dynamic path for the DLL you want to invoke, so it don't have to be from registry. Mind that the PATH var is the last place Windows looks for DLLs. But on the other hand, it does not change the other places Windows looks for DLLs.

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

Example:

API i want to call, on the DLL:

我想在DLL上调用API:

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

Get the registry key (you need using Microsoft.Win32;):

获取注册表项(您需要使用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;
     }

Add the path of the DLL on the PATH var (Concat() is found in Linq):

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

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

Start Using the API call:

开始使用API​​调用:

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

//Start using
ApiCall(int numberOfEyes);