c的dllimport使用方法详解

c的dllimport使用方法详解DllImport 是 System Runtime InteropServi 命名空间下的一个属性类 其功能是提供从非托管 DLL 托管 非托管是微软的 netframework 中特有的概念 其中 非托管代码也叫本地 native 代码 与 Java 中的机制类似 也是先将源代码编译成中间代码 MSIL MicrosoftInt 然后再由 net 中的 CLR 将中

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的定义如下:

代码如下:

[csharp] 
view plain  
copy

  1. [AttributeUsage(AttributeTargets.Method)]  
  2. public class DllImportAttribute: System.Attribute  
  3. {  
  4.     public DllImportAttribute(string dllName) {…} //定位参数为dllName  
  5.     public CallingConvention CallingConvention; //入口点调用约定  
  6.     public CharSet CharSet;                                   //入口点采用的字符接  
  7.     public string EntryPoint;  //入口点名称  
  8.     public bool ExactSpelling;   //是否必须与指示的入口点拼写完全一致,默认false  
  9.     public bool PreserveSig;  //方法的签名是被保留还是被转换  
  10.     public bool SetLastError;  //FindLastError方法的返回值保存在这里  
  11.     public string Value { get {…} }  
  12. }  





用法示例:

 代码如下:

[csharp] 
view plain  
copy

  1. [DllImport(“kernel32”)]  
  2. 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”相当的慢,可能是因为我的方法需要远程验证吧,但是实在是太慢了。经过一翻研究,终于想到了一个完美的解决办法首先我们用

代码如下:
[csharp] 
view plain  
copy

  1. [DllImport(“kernel32.dll”)]  
  2. private extern static IntPtr LoadLibrary(String path);  
  3. [DllImport(“kernel32.dll”)]  
  4. private extern static IntPtr GetProcAddress(IntPtr lib, String funcName);  
  5. [DllImport(“kernel32.dll”)]  
  6. private extern static bool FreeLibrary(IntPtr lib);  

以下自定义类的代码完成LoadLibrary的装载和函数调用

 代码如下:
[csharp] 
view plain  
copy

  1. public class DllInvoke   
  2.     {              
  3.         [DllImport(“kernel32.dll”)]   
  4.         private extern static IntPtr LoadLibrary(String path);  
  5.         [DllImport(“kernel32.dll”)]     
  6.         private extern static IntPtr GetProcAddress(IntPtr lib, String funcName);   
  7.         [DllImport(“kernel32.dll”)]       
  8.         private extern static bool FreeLibrary(IntPtr lib);       
  9.   
  10.         private IntPtr hLib;    
  11.         public DllInvoke(String DLLPath)     
  12.         {             
  13.             hLib = LoadLibrary(DLLPath);    
  14.         }         
  15.   
  16.         ~DllInvoke()       
  17.         {          
  18.             FreeLibrary(hLib);    
  19.         }          
  20.   
  21.         //将要执行的函数转换为委托    
  22.         public Delegate Invoke(String APIName,Type t)       
  23.         {             
  24.             IntPtr api = GetProcAddress(hLib, APIName);     
  25.             return (Delegate)Marshal.GetDelegateForFunctionPointer(api,t);       
  26.         }  
  27.     }  

下面代码进行调用

代码如下:
[csharp] 
view plain  
copy

  1. public delegate int Compile(String command, StringBuilder inf);  
  2.             //编译  
  3.             DllInvoke dll = new DllInvoke(Server.MapPath(@“~/Bin/Judge.dll”));  
  4.             Compile compile = (Compile)dll.Invoke(“Compile”typeof(Compile));  
  5.             StringBuilder inf;  
  6.             compile(@“gcc a.c -o a.exe“,inf);//这里就是调用我的DLL里定义的Compile函数  

代码如下:

[csharp] 
view plain  
copy

  1. namespace System.Runtime.InteropServices     
  2.  {      
  3.      [AttributeUsage(AttributeTargets.Method)]     
  4.      public class DllImportAttribute: System.Attribute    
  5.      {       
  6.          public DllImportAttribute(string dllName)  
  7.          {…}        
  8.   
  9.          public CallingConvention CallingConvention;     
  10.          public CharSet CharSet;      
  11.          public string EntryPoint;      
  12.          public bool ExactSpelling;       
  13.          public bool PreserveSig;        
  14.          public bool SetLastError;       
  15.          public string Value { get {…} }      
  16.      }     
  17.  }  

说明:     


5、此外,用 DllImport 属性修饰的方法必须具有 extern 修饰符。

———————————————————————————————————————-

托管与非托管



托管代码与非托管代码

众所周知,我们正常编程所用的高级语言,是无法被计算机识别的。需要先将高级语言翻译为机器语言,才能被机器理解和运行。
在标准C/C++中,编译过程是这样的:
enter description here
源代码首先经过预处理器,对头文件以及宏进行解析,然后经过编译器,生成汇编代码,接着,经过汇编,生成机器指令,最后将所有文件连接起来。这种编译方式的优点在于,最终直接生成了机器码,可以直接被计算机识别和运行,无需任何中间运行环境,但缺点也在于,由于不同平台能够识别的机器码不同,因此程序的跨平台能力较差。
而在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运行,获得托管代码的优势。

托管资源与非托管资源

在上一节中,我们讲到,托管代码与非托管代码相比,有下列不同:

  1. 编译运行过程不同
  2. 跨平台能力不同
  3. 程序性能不同

本节中,我们会涉及到托管和非托管的另一个区别:

  1. 释放资源的方式不同

托管与非托管的混合编程

#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); } } 

在混合编程中,涉及了几个要点。

  1. 如何在DLL中将函数接口暴露出来?
    有两种方式,一种是采用__declspec(dllexport)的声明,另一种是编写额外的def文件,如
    ;导出DLL函数 LIBRARY testLib EXPORTS DisplayHelloFromDLL CallHelloFromDLL 


  2. DLL与C#之间如何进行数据传送?
    这个问题其实很复杂,像int,double这种基本的数据类型,是很好传递的。到了byte和char,就有点复杂了,更复杂的还有string和stringBuilder,以及结构体的传递等。
    若传递的是指针,有两种方法,一种是采用托管的方式,使用Intptr存储指针,并使用ref获得地址(&);另一种是在C#中编写非托管的代码,用unsafe声明:




    unsafe { //非托管代码 } 
  3. extern “C”、CallingConvention =CallingConvention.Cdecl)等必要声明。
    这里面也牵涉到复杂的语言机制,本文不再赘述。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/209194.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月19日 上午9:49
下一篇 2026年3月19日 上午9:49


相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号