如何在运行时指定[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之前,它会检查以下内容:
如果SafeDllSearchMode
已启用(默认),搜索顺序如下所示:
GetSystemDirectory
函数获取此目录的路径。 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
函数获取此目录的路径。 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
,所以这段代码并不完整。 在实际的应用程序中,您应该注意释放加载的模块以避免内存泄漏。
上一篇: How can I specify a [DllImport] path at runtime?
下一篇: BadImageFormatException when using native DLLs from ASP.NET