DllImport属性应用于方法,要求最少要提供包含入口点的dll的名称。
只有入口点的DLL名称:
[DllImport("standerMFC.dll")] public static extern int PReadUID(ref HHFC_SET stru); [StructLayout(LayoutKind.Sequential)] public struct HHFC_SET { [MarshalAs(UnmanagedType.LPStr)] public String Uid; [MarshalAs(UnmanagedType.I4)] public int code; }
DllImport的定义如下:
- [AttributeUsage(AttributeTargets.Method)]
- public class DllImportAttribute: System.Attribute
- {
- public DllImportAttribute(string dllName) {…} //定位参数为dllName
- public CallingConvention CallingConvention; //入口点调用约定
- public CharSet CharSet; //入口点采用的字符接
- public string EntryPoint; //入口点名称
- public bool ExactSpelling; //是否必须与指示的入口点拼写完全一致,默认false
- public bool PreserveSig; //方法的签名是被保留还是被转换
- public bool SetLastError; //FindLastError方法的返回值保存在这里
- public string Value { get {…} }
- }
用法示例:
- [DllImport(“kernel32”)]
- private static extern long WritePrivateProfileString(string section,string key,string val,string filePath);
以上是用来写入ini文件的一个win32api。
用此方式调用Win32API的数据类型对应:DWORD=int或uint,BOOL=bool,预定义常量=enum,结构=struct。
DllImport会按照顺序自动去寻找的地方:
1、exe所在目录
2、System32目录
3、环境变量目录所以只需要你把引用的DLL 拷贝到这三个目录下 就可以不用写路径了 或者可以这样server.MapPath(.\bin\*.dll)web中的,同时也是应用程序中的 后来发现用[DllImport(@”C:\OJ\Bin\Judge.dll”)]这样指定DLL的绝对路径就可以正常装载。
这个问题最常出现在使用第三方非托管DLL组件的时候,我的也同样是这时出的问题,Asp.Net Team的官方解决方案如下: 首先需要确认你引用了哪些组件,那些是托管的,哪些是非托管的.
托管的很好办,直接被使用的需要引用,间接使用的需要拷贝到bin目录下.
非托管的处理会比较麻烦.实际上,你拷贝到bin没有任何帮助,因为CLR会把文件拷贝到一个临时目录下,然后在那运行web,而CLR只会拷贝托管文件,这就是为什么我们明明把非托管的dll放在了bin下却依然提示不能加载模块了.
具体做法如下:
1、首先我们在服务器上随便找个地方新建一个目录,假如为C:\DLL
2、然后,在环境变量中,给Path变量添加这个目录
3、最后,把所有的非托管文件都拷贝到C:\DLL中. 或者更干脆的把DLL放到system32目录 对于可以自己部署的应用程序,这样未偿不是一个解决办法,然而,如果我们用的是虚拟空间,我们是没办法把注册PATH变量或者把我们自己的DLL拷到system32目录的。同时我们也不一定知道我们的Dll的物理路径。 DllImport里面只能用字符串常量,而不能够用Server.MapPath(@”~/Bin/Judge.dll”)来确定物理路径。ASP.NET中要使用DllImport的,必须在先“using System.Runtime.InteropServices;”不过,我发现,调用这种”非托管Dll”相当的慢,可能是因为我的方法需要远程验证吧,但是实在是太慢了。经过一翻研究,终于想到了一个完美的解决办法首先我们用
- [DllImport(“kernel32.dll”)]
- private extern static IntPtr LoadLibrary(String path);
- [DllImport(“kernel32.dll”)]
- private extern static IntPtr GetProcAddress(IntPtr lib, String funcName);
- [DllImport(“kernel32.dll”)]
- private extern static bool FreeLibrary(IntPtr lib);
以下自定义类的代码完成LoadLibrary的装载和函数调用
- public class DllInvoke
- {
- [DllImport(“kernel32.dll”)]
- private extern static IntPtr LoadLibrary(String path);
- [DllImport(“kernel32.dll”)]
- private extern static IntPtr GetProcAddress(IntPtr lib, String funcName);
- [DllImport(“kernel32.dll”)]
- private extern static bool FreeLibrary(IntPtr lib);
- private IntPtr hLib;
- public DllInvoke(String DLLPath)
- {
- hLib = LoadLibrary(DLLPath);
- }
- ~DllInvoke()
- {
- FreeLibrary(hLib);
- }
- //将要执行的函数转换为委托
- public Delegate Invoke(String APIName,Type t)
- {
- IntPtr api = GetProcAddress(hLib, APIName);
- return (Delegate)Marshal.GetDelegateForFunctionPointer(api,t);
- }
- }
下面代码进行调用
- public delegate int Compile(String command, StringBuilder inf);
- //编译
- DllInvoke dll = new DllInvoke(Server.MapPath(@“~/Bin/Judge.dll”));
- Compile compile = (Compile)dll.Invoke(“Compile”, typeof(Compile));
- StringBuilder inf;
- compile(@“gcc a.c -o a.exe“,inf);//这里就是调用我的DLL里定义的Compile函数
- namespace System.Runtime.InteropServices
- {
- [AttributeUsage(AttributeTargets.Method)]
- public class DllImportAttribute: System.Attribute
- {
- public DllImportAttribute(string dllName)
- {…}
- public CallingConvention CallingConvention;
- public CharSet CharSet;
- public string EntryPoint;
- public bool ExactSpelling;
- public bool PreserveSig;
- public bool SetLastError;
- public string Value { get {…} }
- }
- }
说明:
5、此外,用 DllImport 属性修饰的方法必须具有 extern 修饰符。
———————————————————————————————————————-
托管代码与非托管代码
众所周知,我们正常编程所用的高级语言,是无法被计算机识别的。需要先将高级语言翻译为机器语言,才能被机器理解和运行。
在标准C/C++中,编译过程是这样的:
源代码首先经过预处理器,对头文件以及宏进行解析,然后经过编译器,生成汇编代码,接着,经过汇编,生成机器指令,最后将所有文件连接起来。这种编译方式的优点在于,最终直接生成了机器码,可以直接被计算机识别和运行,无需任何中间运行环境,但缺点也在于,由于不同平台能够识别的机器码不同,因此程序的跨平台能力较差。
而在Java语言中,源代码并没有被直接翻译成机器码,而是编译成了一种中间代码(字节码Bytecode)。因此,运行Java程序需要一个额外的JRE(Java Runtime Enviromental)运行环境,在JRE中存在着JVM(Java Virtual Mechinal,Java虚拟机),在程序运行的时候,会将中间代码进一步解释为机器码,并在机器上运行。
使用中间代码的好处在于,程序的跨平台性比较好,一次编译,可以在不同的设备上运行。
托管/非托管是微软的.net framework中特有的概念,其中,
非托管
代码也叫本地(native)代码。与Java中的机制类似,也是先将源代码编译成中间代码(MSIL,Microsoft Intermediate Language),然后再由.net中的CLR将中间代码编译成机器代码。
而C#与Java的区别在于,Java是先编译后解释,C#是两次编译。
托管
的方式除了拥有跨平台的优点之外,对程序的性能也产生一定的影响。但程序性能不在本文讨论的范围,这里不在赘述。
此外,在.net中,C++也可以进行托管扩展,从而使C++代码也依赖于.net和CLR运行,获得托管代码的优势。
托管资源与非托管资源
在上一节中,我们讲到,托管代码与非托管代码相比,有下列不同:
- 编译运行过程不同
- 跨平台能力不同
- 程序性能不同
本节中,我们会涉及到托管和非托管的另一个区别:
- 释放资源的方式不同
托管与非托管的混合编程
#include #include void DisplayHelloFromDLL() { printf ("Hello from DLL !\n"); } void CallHelloFromDLL(char* cp) { printf (cp); printf ("\n"); *cp='a'; cp++; printf (cp); printf ("\n"); }
在C#中:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace TestConsole { using System; using System.Runtime.InteropServices; // DLL support class Program { [DllImport(@"TestLib.dll")] public static extern void DisplayHelloFromDLL(); [DllImport(@"TestLib.dll", CallingConvention = CallingConvention.Cdecl)] public static extern void CallHelloFromDLL(StringBuilder s); static void Main() { Console.WriteLine("This is C# program"); DisplayHelloFromDLL(); StringBuilder sb = new StringBuilder(100); CallHelloFromDLL(sb); Console.WriteLine(sb); } }
在混合编程中,涉及了几个要点。
- 如何在DLL中将函数接口暴露出来?
有两种方式,一种是采用__declspec(dllexport)的声明,另一种是编写额外的def文件,如;导出DLL函数 LIBRARY testLib EXPORTS DisplayHelloFromDLL CallHelloFromDLL - DLL与C#之间如何进行数据传送?
这个问题其实很复杂,像int,double这种基本的数据类型,是很好传递的。到了byte和char,就有点复杂了,更复杂的还有string和stringBuilder,以及结构体的传递等。
若传递的是指针,有两种方法,一种是采用托管的方式,使用Intptr存储指针,并使用ref获得地址(&);另一种是在C#中编写非托管的代码,用unsafe声明:unsafe { //非托管代码 } - extern “C”、CallingConvention =CallingConvention.Cdecl)等必要声明。
这里面也牵涉到复杂的语言机制,本文不再赘述。
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/209194.html原文链接:https://javaforall.net
