Jsn*_*dnl 129 c# c++ dll constants dllimport
事实上,我得到了一个C++(工作)DLL,我想导入我的C#项目来调用它的函数.
当我指定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);
Run Code Online (Sandbox Code Playgroud)
问题是它将是一个可安装的项目,因此用户的文件夹将不同(例如:皮埃尔,保罗,杰克,妈妈,爸爸......),这取决于计算机/会话的运行情况.
所以我希望我的代码更通用,如下所示:
/*
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);
Run Code Online (Sandbox Code Playgroud)
最重要的是"DllImport"需要DLL目录的"const string"参数.
所以我的问题是::在这种情况下可以做些什么?
Cod*_*ray 168
与其他一些答案的建议相反,使用该DllImport
属性仍然是正确的方法.
老实说,我不明白为什么你不能像世界上其他人一样做,并指定你的DLL 的相对路径.是的,应用程序安装的路径在不同人的计算机上有所不同,但这在部署时基本上是一个通用规则.该DllImport
机制的设计考虑到了这一点.
事实上,甚至DllImport
没有处理它.这是管理事物的本机Win32 DLL加载规则,无论您是否使用方便的托管包装器(P/Invoke marshaller只是调用LoadLibrary
).这些规则在这里详细列举,但重要的是在这里摘录:
在系统搜索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);
Run Code Online (Sandbox Code Playgroud)
但是,如果由于某种原因无效,并且您需要强制应用程序查找DLL的其他目录,则可以使用该SetDllDirectory
函数修改默认搜索路径.
请注意,根据文档:
调用后
SetDllDirectory
,标准的DLL搜索路径是:
- 加载应用程序的目录.
lpPathName
参数指定的目录.- 系统目录.使用该
GetSystemDirectory
函数获取此目录的路径.- 16位系统目录.没有函数可以获取此目录的路径,但会搜索它.
- Windows目录.使用该
GetWindowsDirectory
函数获取此目录的路径.PATH
环境变量中列出的目录.
因此,只要在第一次调用从DLL导入的函数之前调用此函数,就可以修改用于查找DLL的默认搜索路径.当然,好处是您可以将动态值传递给在运行时计算的此函数.对于该DllImport
属性,这是不可能的,因此您仍将使用相对路径(仅DLL的名称),并依赖新的搜索顺序为您找到它.
你必须P/Invoke这个功能.声明如下:
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool SetDllDirectory(string lpPathName);
Run Code Online (Sandbox Code Playgroud)
Mik*_*keP 35
甚至比Ran的建议使用GetProcAddress更好,只需在调用DllImport函数之前调用LoadLibrary(只有一个没有路径的文件名),他们将自动使用加载的模块.
我已经使用此方法在运行时选择是否加载32位或64位本机DLL而无需修改一堆P/Invoke-d函数.将加载代码粘贴到具有导入函数的类型的静态构造函数中,它们都可以正常工作.
Ran*_*Ran 24
如果你需要一个不在路径或应用程序位置的.dll文件,那么我认为你不能这样做,因为它DllImport
是一个属性,属性只是在类型,成员和其他上设置的元数据语言元素.
另一种可以帮助你完成我认为你正在尝试的方法是LoadLibrary
通过P/Invoke 使用native ,以便从你需要的路径加载.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);
}
}
Run Code Online (Sandbox Code Playgroud)
注意:我没有打扰使用FreeLibrary
,所以此代码不完整.在实际应用程序中,您应该注意释放已加载的模块以避免内存泄漏.
joe*_*joe 17
在所有其他好的答案中,在 .NET Core 3.0 之后,您可以使用NativeLibrary。例如,在 Linux 中,您没有这样的kernel32.dll。NativeLibrary.Load
并且Native.SetDllImportResolver
可以是要走的路:
static MyLib()
{
//Available for .NET Core 3+
NativeLibrary.SetDllImportResolver(typeof(MyLib).Assembly, ImportResolver);
}
private static IntPtr ImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
{
IntPtr libHandle = IntPtr.Zero;
if (libraryName == "MyLib")
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
libHandle = NativeLibrary.Load("xxxx.dll");
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
libHandle = NativeLibrary.Load("xxxx.so");
}
}
return libHandle;
}
[DllImport("MyLib", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr foo(string name);
Run Code Online (Sandbox Code Playgroud)
也可以看看:
只要您知道在运行时可以找到C ++库的目录,这应该很简单。我可以清楚地看到您的代码就是这种情况。您myDll.dll
将出现在myLibFolder
当前用户临时文件夹内的目录中。
string str = Path.GetTempPath() + "..\\myLibFolder\\myDLL.dll";
Run Code Online (Sandbox Code Playgroud)
现在,您可以使用const字符串继续使用DllImport语句,如下所示:
[DllImport("myDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int DLLFunction(int Number1, int Number2);
Run Code Online (Sandbox Code Playgroud)
在运行时,在调用该DLLFunction
函数(存在于C ++库中)之前,在C#代码中添加以下代码行:
string assemblyProbeDirectory = Path.GetTempPath() + "..\\myLibFolder\\myDLL.dll";
Directory.SetCurrentDirectory(assemblyProbeDirectory);
Run Code Online (Sandbox Code Playgroud)
这只是指示CLR在程序运行时获得的目录路径中查找非托管C ++库。Directory.SetCurrentDirectory
调用将应用程序的当前工作目录设置为指定目录。如果您的用户myDLL.dll
位于以path表示的assemblyProbeDirectory
路径上,则它将被加载,并通过p / invoke调用所需的函数。
归档时间: |
|
查看次数: |
128513 次 |
最近记录: |