用Visual C#调用Windows API函数

Api函数是构筑Windws应用程序的基石,每一种Windows应用程序开发工具,它提供的底层函数都间接或直接地调用了Windows  API函数,同时为了实现功能扩展,一般也都提供了调用WindowsAPI函数的接口,  也就是说具备调用动态连接库的能力。Visual  C#和其它开发工具一样也能够调用动态链接库的API函数。.NET框架本身提供了这样一种服务,允许受管辖的代码调用动态链接库中实现的非受管辖函数,包括操作系统提供的Windows  API函数。它能够定位和调用输出函数,根据需要,组织其各个参数(整型、字符串类型、数组、和结构等等)跨越互操作边界。  

 

下面以c#为例简单介绍调用api的基本过程:    
动态链接库函数的声明    
 动态链接库函数使用前必须声明,相对于VB,C#函数声明显得更加罗嗦,前者通过  Api  Viewer粘贴以后,可以直接使用,而后者则需要对参数作些额外的变化工作。  

 动态链接库函数声明部分一般由下列两部分组成,一是函数名或索引号,二是动态链接库的文件名。    
   譬如,你想调用User32.DLL中的MessageBox函数,我们必须指明函数的名字MessageBoxA或MessageBoxW,以及库名字User32.dll,我们知道Win32  API对每一个涉及字符串和字符的函数一般都存在两个版本,单字节字符的ANSI版本和双字节字符的UNICODE版本。  

 下面是一个调用API函数的例子:    
[DllImport("KERNEL32.DLL",  EntryPoint="MoveFileW",  SetLastError=true,    
CharSet=CharSet.Unicode,  ExactSpelling=true,    
CallingConvention=CallingConvention.StdCall)]    
public  static  extern  bool  MoveFile(String  src,  String  dst);    

 其中入口点EntryPoint标识函数在动态链接库的入口位置,在一个受管辖的工程中,目标函数的原始名字和序号入口点不仅标识一个跨越互操作界限的函数。而且,你还可以把这个入口点映射为一个不同的名字,也就是对函数进行重命名。重命名可以给调用函数带来种种便利,通过重命名,一方面我们不用为函数的大小写伤透脑筋,同时它也可以保证与已有的命名规则保持一致,允许带有不同参数类型的函数共存,更重要的是它简化了对ANSI和Unicode版本的调用。CharSet用于标识函数调用所采用的是Unicode或是ANSI版本,ExactSpelling=false将告诉编译器,让编译器决定使用Unicode或者是Ansi版本。其它的参数请参考MSDN在线帮助.  

 在C#中,你可以在EntryPoint域通过名字和序号声明一个动态链接库函数,如果在方法定义中使用的函数名与DLL入口点相同,你不需要在EntryPoint域显示声明函数。否则,你必须使用下列属性格式指示一个名字和序号。  

[dllimport("dllname",  entrypoint="functionname")]    
[DllImport("dllname",  EntryPoint="#123")]    
值得注意的是,你必须在数字序号前加“#”    
下面是一个用MsgBox替换MessageBox名字的例子:    
[C#]    
using  System.Runtime.InteropServices;    

public  class  win32  {    
[DllImport("user32.dll",  EntryPoint="MessageBox")]    
public  static  extern  int  MsgBox(int  hWnd,  String  text,  String  caption,  uint  type);    
}    
许多受管辖的动态链接库函数期望你能够传递一个复杂的参数类型给函数,譬如一个用户定义的结构类型成员或者受管辖代码定义的一个类成员,这时你必须提供额外的信息格式化这个类型,以保持参数原有的布局和对齐。  

c#提供了一个structlayoutattribute类,通过它你可以定义自己的格式化类型,在受管辖代码中,格式化类型是一个用structlayoutattribute说明的结构或类成员,通过它能够保证其内部成员预期的布局信息。布局的选项共有三种:  

布局选项    
描述    
LayoutKind.Automatic    
为了提高效率允许运行态对类型成员重新排序。    
注意:永远不要使用这个选项来调用不受管辖的动态链接库函数。    
LayoutKind.Explicit    
对每个域按照FieldOffset属性对类型成员排序    
LayoutKind.Sequential    
对出现在受管辖类型定义地方的不受管辖内存中的类型成员进行排序。    
传递结构成员    
下面的例子说明如何在受管辖代码中定义一个点和矩形类型,并作为一个参数传递给User32.dll库中的PtInRect函数,    
函数的不受管辖原型声明如下:    
BOOL  PtInRect(const  RECT  *lprc,  POINT  pt);    
注意你必须通过引用传递Rect结构参数,因为函数需要一个Rect的结构指针。    
[C#]    
using  System.Runtime.InteropServices;    

[structlayout(layoutkind.sequential)]    
public  struct  Point  {    
public  int  x;    
public  int  y;    
}    

[structlayout(layoutkind.explicit]    
public  struct  Rect  {    
[FieldOffset(0)]  public  int  left;    
[FieldOffset(4)]  public  int  top;    
[FieldOffset(8)]  public  int  right;    
[FieldOffset(12)]  public  int  bottom;    
}    

class  win32api  {    
[DllImport("User32.dll")]    
public  static  extern  Bool  PtInRect(ref  Rect  r,  Point  p);    
}    
类似你可以调用GetSystemInfo函数获得系统信息:    
?  using  System.Runtime.InteropServices;    
[StructLayout(LayoutKind.Sequential)]    
public  struct  SYSTEM_INFO  {    
public  uint  dwOemId;    
public  uint  dwPageSize;    
public  uint  lpMinimumApplicationAddress;    
public  uint  lpMaximumApplicationAddress;    
public  uint  dwActiveProcessorMask;    
public  uint  dwNumberOfProcessors;    
public  uint  dwProcessorType;    
public  uint  dwAllocationGranularity;    
public  uint  dwProcessorLevel;    
public  uint  dwProcessorRevision;    
}    

 


[DllImport("kernel32")]    
static  extern  void  GetSystemInfo(ref  SYSTEM_INFO  pSI);    

system_info  psi  =  new  system_info();    
GetSystemInfo(ref  pSI);    

类成员的传递    
同样只要类具有一个固定的类成员布局,你也可以传递一个类成员给一个不受管辖的动态链接库函数,下面的例子主要说明如何传递一个sequential顺序定义的MySystemTime类给User32.dll的GetSystemTime函数,  函数用C/C++调用规范如下:  

void  getsystemtime(systemtime*  systemtime);    
不像传值类型,类总是通过引用传递参数.    
[C#]    
[StructLayout(LayoutKind.Sequential)]    
public  class  MySystemTime  {    
public  ushort  wYear;    
public  ushort  wMonth;    
public  ushort  wDayOfWeek;    
public  ushort  wDay;    
public  ushort  wHour;    
public  ushort  wMinute;    
public  ushort  wSecond;    
public  ushort  wMilliseconds;    
}    
class  Win32API  {    
[DllImport("User32.dll")]    
public  static  extern  void  GetSystemTime(MySystemTime  st);    
}    
回调函数的传递:    
从受管辖的代码中调用大多数动态链接库函数,你只需创建一个受管辖的函数定义,然后调用它即可,这个过程非常直接。    
如果一个动态链接库函数需要一个函数指针作为参数,你还需要做以下几步:    
首先,你必须参考有关这个函数的文档,确定这个函数是否需要一个回调;第二,你必须在受管辖代码中创建一个回调函数;最后,你可以把指向这个函数的指针作为一个参数创递给DLL函数,.  

回调函数及其实现:    
回调函数经常用在任务需要重复执行的场合,譬如用于枚举函数,譬如Win32  API  中的EnumFontFamilies(字体枚举),  EnumPrinters(打印机),  EnumWindows  (窗口枚举)函数.  下面以窗口枚举为例,谈谈如何通过调用EnumWindow  函数遍历系统中存在的所有窗口  

分下面几个步骤:    
1.  在实现调用前先参考函数的声明    
BOOL  EnumWindows(WNDENUMPROC  lpEnumFunc,  LPARMAM  IParam)    
显然这个函数需要一个回调函数地址作为参数.    
2.  创建一个受管辖的回调函数,这个例子声明为代表类型(delegate),也就是我们所说的回调,它带有两个参数hwnd和lparam,第一个参数是一个窗口句柄,第二个参数由应用程序定义,两个参数均为整形。 当这个回调函数返回一个非零值时,标示执行成功,零则暗示失败,这个例子总是返回True值,以便持续枚举。    
3.  最后创建以代表对象(delegate),并把它作为一个参数传递给EnumWindows  函数,平台会自动地  把代表转化成函数能够识别的回调格式

Re:用Visual C#调用Windows API函数
[C#]    
using  System;    
using  System.Runtime.InteropServices;    

 

public  delegate  bool  callback(int  hwnd,  int  lparam);    

public  class  enumreportapp  {    

[dllimport("user32")]    
public  static  extern  int  EnumWindows(CallBack  x,  int  y);    

public  static  void  main()    
{    
CallBack  myCallBack  =  new  CallBack(EnumReportApp.Report);    
EnumWindows(myCallBack,  0);    
}    

public  static  bool  report(int  hwnd,  int  lparam)  {    
Console.Write("窗口句柄为");    
Console.WriteLine(hwnd);    
return  true;    
}    
}    

 

指针类型参数传递:    
 在Windows  API函数调用时,大部分函数采用指针传递参数,对一个结构变量指针,我们除了使用上面的类和结构方法传递参数之外,我们有时还可以采用数组传递参数。  

 下面这个函数通过调用GetUserName获得用户名    
BOOL  GetUserName(    
LPTSTR  lpBuffer,  //  用户名缓冲区    
LPDWORD  nSize  //  存放缓冲区大小的地址指针    
);    
     
[DllImport("Advapi32.dll",    
EntryPoint="GetComputerName",    
ExactSpelling=false,    
SetLastError=true)]    
static  extern  bool  GetComputerName  (    
[MarshalAs(UnmanagedType.LPArray)]  byte[]  lpBuffer,    
   [MarshalAs(UnmanagedType.LPArray)]  Int32[]  nSize  );    
 这个函数接受两个参数,char  *  和int  *,因为你必须分配一个字符串缓冲区以接受字符串指针,你可以使用String类代替这个参数类型,当然你还可以声明一个字节数组传递ANSI字符串,同样你也可以声明一个只有一个元素的长整型数组,使用数组名作为第二个参数。上面的函数可以调用如下:  

byte[]  str=new  byte[20];    
Int32[]  len=new  Int32[1];    
len[0]=20;    
GetComputerName  (str,len);    
MessageBox.Show(System.Text.Encoding.ASCII.GetString(str));    
 最后需要提醒的是,每一种方法使用前必须在文件头加上:    
 using  System.Runtime.InteropServices;    

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Win10 VS2013 ************************************************ * BOOL GetDiskSpaceInfo(LPCSTR pszDrive * 功能 根据输入的驱动器,获取磁盘总容量 * 空闲空间、簇数量等磁盘信息 * 参数 驱动器根路径,比如“D:\”。 ************************************************ * void GetTime() * 功能 获取并显示本地与系统时间 ************************************************ * void GetLang() * 功能 获取并显示系统的默认语言 ************************************************ * void ShowVersionInfo() * 功能 获取并显示系统版本信息 ************************************************ * void ShowSystemInfo() * 功能 获取并显示硬件相关信息 ************************************************ *void GetFolders() * 功能 获取系统目录等信息 ************************************************ *void WINAPI EnumProcess1() * 功能 调用EnumProcess遍历进程 * 并调用ListProcessModules1函数和 * ListProcessThreads函数列举模块 * 和线程 ************************************************ * VOID ListProcessModules1( DWORD dwPID ) * 功能 调用EnumProcessModules函数 * 列举和显示进程加载的模块 * * 参数 DWORD dwPID 进程PID ************************************************ * VOID ListProcessThreads( DWORD dwPID ) * 功能 调用Thread32First和Thread32Next * 显示一个进程的线程 * * 参数 DWORD dwPID 进程PID ************************************************ * VOID PrintError( LPTSTR msg ) * 功能 打印出错信息 * * 参数 LPTSTR msg ************************************************ * void GetDriveInfo( LPCTSTR lpRootPath) * 功能 返 回 指 定 驱 动 器 的 类 型。 * * 参数 lpRootPathName ************************************************ * void GetSystemMetric(int nIndex) * 功能 返 回 显 示 器 分 辨 率。 * * 参数 ************************************************ * void GetKeyboardInfo() * 功能 返回键盘类型。 * * 参数 *************************************************
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值