C# 如何在运行时指定[DllImport]路径? 事实上,我得到了一个C++(工作)DLL,我想导入到我的C项目中调用它的函数。

C# 如何在运行时指定[DllImport]路径? 事实上,我得到了一个C++(工作)DLL,我想导入到我的C项目中调用它的函数。,c#,c++,dll,constants,dllimport,C#,C++,Dll,Constants,Dllimport,当我指定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); 问题是它将是一个可安装的项目,因此用户的

当我指定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、妈妈、爸爸等),这取决于运行它的计算机/会话

因此,我希望我的代码更加通用,如下所示:

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

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

只要dll位于系统路径上的某个位置,DllImport在没有指定完整路径的情况下就可以正常工作。您可以临时将用户文件夹添加到路径。

如果您需要一个不在路径或应用程序位置的.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:\windows\system32\user32.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);
    }
}
类程序
{
静态void Main(字符串[]参数)
{
var a=新的MyClass();
var result=a.ShowMessage();
}
}
类函数加载器
{
[DllImport(“Kernel32.dll”)]
私有静态外部IntPtr加载库(字符串路径);
[DllImport(“Kernel32.dll”)]
私有静态外部IntPtr GetProcAddress(IntPtr hModule,字符串procName);
公共静态委托加载函数(字符串dllPath、字符串functionName)
{
var hModule=LoadLibrary(dllPath);
var functionAddress=GetProcAddress(hModule,functionalname);
返回Marshal.GetDelegateForFunctionPointer(functionAddress,typeof(T));
}
}
公共类MyClass
{
静态MyClass()
{
//加载函数并将其设置为代理
//这只是一个示例-您可以从任何路径加载.dll,
//您甚至可以在运行时确定文件位置。
MessageBox=(MessageBoxDelegate)
FunctionLoader.LoadFunction(
@“c:\windows\system32\user32.dll”、“MessageBoxA”);
}
私有委托int MessageBoxDelegate(
IntPtr hwnd、字符串标题、字符串消息、int按钮);
/// 
///这是动态P/Invoke替代方案
/// 
静态私有MessageBox委托MessageBox;
/// 
///使用“动态P/Invoke”的方法示例
/// 
公共int ShowMessage()
{
//3表示“是/否/取消”按钮,只是为了表明它可以工作。。。
返回消息框(IntPtr.Zero,“Hello world”,“动态加载”,3);
}
}

注意:我没有费心使用
FreeLibrary
,因此此代码不完整。在实际应用程序中,应注意释放加载的模块以避免内存泄漏。

如果所有操作都失败,只需将DLL放入
windows\system32
文件夹中即可。编译器将找到它。 使用以下命令指定要从中加载的DLL:
DllImport(“user32.DLL”…
,设置
EntryPoint=“my_unmanaged_function”
,将所需的非托管函数导入C#应用程序:


源代码和更多的
dlliport
示例:

甚至比Ran建议使用
GetProcAddress
更好,只需在调用
dlliport
函数之前调用
LoadLibrary
,它们就会自动使用加载的模块


我使用此方法在运行时选择是加载32位还是64位本机DLL,而不必修改一堆p/Invoke-d函数。将加载代码粘贴到具有导入函数的类型的静态构造函数中,它会正常工作。

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

我真的不明白为什么你不能像世界上其他人一样指定DLL的相对路径。是的,在不同的人的计算机上安装应用程序的路径是不同的,但这基本上是部署时的通用规则。
DllImport
机制就是用这种方法设计的这是我的想法

事实上,处理它的甚至不是
DllImport
。不管您是否使用方便的托管包装器(p/Invoke marshaller只是调用),都是本机Win32 DLL加载规则控制事情。这些规则非常详细,但这里摘录了重要的规则:

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

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

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

  • 从中加载应用程序的目录
  • 系统目录。使用
    GetSystemDirectory
    函数获取
     using System;
    using System.Runtime.InteropServices;
    
    class Example
    {
       // Use DllImport to import the Win32 MessageBox function.
    
       [DllImport ("user32.dll", CharSet = CharSet.Auto)]
       public static extern int MessageBox 
          (IntPtr hWnd, String text, String caption, uint type);
    
       static void Main()
       {
          // Call the MessageBox function using platform invoke.
          MessageBox (new IntPtr(0), "Hello, World!", "Hello Dialog", 0);    
       }
    }
    
    [DllImport("MyAppDll.dll")] // relative path; just give the DLL's name
    static extern bool MyGreatFunction(int myFirstParam, int mySecondParam);
    
    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    static extern bool SetDllDirectory(string lpPathName);
    
    string str = Path.GetTempPath() + "..\\myLibFolder\\myDLL.dll"; 
    
    [DllImport("myDLL.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int DLLFunction(int Number1, int Number2);
    
    string assemblyProbeDirectory = Path.GetTempPath() + "..\\myLibFolder\\myDLL.dll"; 
    Directory.SetCurrentDirectory(assemblyProbeDirectory);
    
    <add key="dllPath" value="C:\Users\UserName\YourApp\myLibFolder\myDLL.dll" />
    
    string dllPath= ConfigurationManager.AppSettings["dllPath"];    
       string appDirectory = Path.GetDirectoryName(dllPath);
       Directory.SetCurrentDirectory(appDirectory);
    
     [DllImport("myDLL.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int DLLFunction(int Number1, int Number2);