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

实际上,我得到了一个C ++(工作)DLL,我想将它导入到我的C#项目中以调用它的函数。

当我指定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 string”参数。

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


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

我真的不明白为什么你不能像世界上其他人那样做,并指定一个相对于你的DLL的路径。 是的,安装应用程序的路径在不同的人电脑上有所不同,但在部署时基本上是一个通用规则。 DllImport机制就是为此而设计的。

事实上,它甚至不包括DllImport来处理它。 无论您是否使用方便的托管包装器(P / Invoke编组器都只是调用LoadLibrary ),它都是管理事件的本机Win32 DLL加载规则。 这些规则在这里详细列举,但重要的规则在这里摘录:

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

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

  • 如果SafeDllSearchMode已启用(默认),搜索顺序如下所示:

  • 加载应用程序的目录。
  • 系统目录。 使用GetSystemDirectory函数获取此目录的路径。
  • 16位系统目录。 没有获得该目录路径的函数,但它被搜索。
  • Windows目录。 使用GetWindowsDirectory函数获取此目录的路径。
  • 当前目录。
  • 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搜索路径是:

  • 加载应用程序的目录。
  • lpPathName参数指定的目录。
  • 系统目录。 使用GetSystemDirectory函数获取此目录的路径。
  • 16位系统目录。 没有获得该目录路径的函数,但它被搜索。
  • Windows目录。 使用GetWindowsDirectory函数获取此目录的路径。
  • PATH环境变量中列出的目录。
  • 因此,只要您在第一次调用从DLL导入的函数之前调用此函数,就可以修改用于定位DLL的默认搜索路径。 当然,好处是您可以将动态值传递给在运行时计算的此函数。 这对于DllImport属性是不可能的,因此您仍然会在那里使用相对路径(仅限于DLL的名称),并依靠新的搜索顺序为您找到它。

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

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

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

    我使用这种方法在运行时选择是否加载32位或64位本地DLL,而无需修改一堆P / Invoke-d函数。 将加载代码粘贴到具有导入函数的类型的静态构造函数中,它将一切正常。


    如果你需要一个不在路径或应用程序位置的.dll文件,那么我认为你不能这样做,因为DllImport是一个属性,属性只是在类型,成员和其他语言元素。

    另一种可以帮助你实现我认为你正在尝试的方法是通过P / Invoke使用本地LoadLibrary ,以便从你需要的路径加载一个.dll文件,然后使用GetProcAddress来获得对函数的引用你需要从那个.dll。 然后使用这些来创建可以调用的委托。

    为了使它更易于使用,可以将此委托设置为类中的字段,以便使用它看起来像调用成员方法。

    编辑

    这是一个可以工作的代码片段,并显示了我的意思。

    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 ,所以这段代码并不完整。 在实际的应用程序中,您应该注意释放加载的模块以避免内存泄漏。

    链接地址: http://www.djcxy.com/p/44535.html

    上一篇: How can I specify a [DllImport] path at runtime?

    下一篇: BadImageFormatException when using native DLLs from ASP.NET