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