VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > temp > C#教程 >
  • c#中正确的调用API函数

制作者:剑锋冷月 单位:无忧统计网,www.51stat.net
 

  在C#中经常需要调用一些API函数,那么怎样才能正确的调用API函数呢,如下:

  一、调用API格式

  //引用此名称空间,简化后面的代码

usingSystem.Runtime.InteropServices;
  
...

  //使用DllImportAttribute特性来引入api函数,注意声明的是空方法,即方法体为空。

  [DllImport("user32.dll")]

publicstaticexternReturnTypeFunctionName(typearg1,typearg2,...);

  可以使用字段进一步说明特性,用逗号隔开,如:

[DllImport("kernel32",EntryPoint="GetVersionEx",SetLastError=true)]

  DllImportAttribute特性的几个公共字段如下:

  1、CallingConvention:指示向非托管实现传递方法参数时所用的CallingConvention值。

  CallingConvention.Cdecl:调用方清理堆栈。它使您能够调用具有varargs的函数。

  CallingConvention.StdCall:被调用方清理堆栈。它是从托管代码调用非托管函数的默认约定。

  2、CharSet:控制调用函数的名称版本及指示如何向方法封送String参数。

  此字段被设置为CharSet值之一。如果CharSet字段设置为Unicode,则所有字符串参数在传递到非托管实现之前都转换成Unicode字符。这还导致向DLLEntryPoint的名称中追加字母“W”。如果此字段设置为Ansi,则字符串将转换成ANSI字符串,同时向DLLEntryPoint的名称中追加字母“A”。大多数Win32API使用这种追加“W”或“A”的约定。如果CharSet设置为Auto,则这种转换就是与平台有关的(在WindowsNT上为Unicode,在Windows98上为Ansi)。CharSet的默认值为Ansi。CharSet字段也用于确定将从指定的DLL导入哪个版本的函数。CharSet.Ansi和CharSet.Unicode的名称匹配规则大不相同。对于Ansi来说,如果将EntryPoint设置为“MyMethod”且它存在的话,则返回“MyMethod”。如果DLL中没有“MyMethod”,但存在“MyMethodA”,则返回“MyMethodA”。对于Unicode来说则正好相反。如果将EntryPoint设置为“MyMethod”且它存在的话,则返回“MyMethodW”。如果DLL中不存在“MyMethodW”,但存在“MyMethod”,则返回“MyMethod”。如果使用的是Auto,则匹配规则与平台有关(在WindowsNT上为Unicode,在Windows98上为Ansi)。如果ExactSpelling设置为true,则只有当DLL中存在“MyMethod”时才返回“MyMethod”。

 

  3、EntryPoint:指示要调用的DLL入口点的名称或序号。

  如果你的方法名不想与api函数同名的话,一定要指定此参数,例如:

[DllImport("user32.dll",CharSet="CharSet.Auto",EntryPoint="MessageBox")]
  
publicstaticexternintMsgBox(IntPtrhWnd,stringtxt,stringcaption,inttype);

  4、ExactSpelling:指示是否应修改非托管DLL中的入口点的名称,以与CharSet字段中指定的CharSet值相对应。如果为true,则当DllImportAttribute.CharSet字段设置为CharSet的Ansi值时,向方法名称中追加字母A,当DllImportAttribute.CharSet字段设置为CharSet的Unicode值时,向方法的名称中追加字母W。此字段的默认值是false。

  5、PreserveSig:指示托管方法签名不应转换成返回HRESULT、并且可能有一个对应于返回值的附加[out,retval]参数的非托管签名。

  6、SetLastError:指示被调用方在从属性化方法返回之前将调用Win32APISetLastError。true指示调用方将调用SetLastError,默认为false。运行时封送拆收器将调用GetLastError并缓存返回的值,以防其被其他API调用重写。用户可通过调用GetLastWin32Error来检索错误代码。

  二、参数类型:

  1、数值型直接用对应的就可。(DWORD->int,或uint,WORD->Int16)

  2、API中字符串指针类型->.net中string,或者数组,如byte[]

  3、API中句柄(dWord)->.net中IntPtr

  4、API中结构->.net中结构或者类。注意这种情况下,要先用StructLayout特性限定声明结构或类

  公共语言运行库利用StructLayoutAttribute控制类或结构的数据字段在托管内存中的物理布局,即类或结构需要按某种方式排列。如果要将类传递给需要指定布局的非托管代码,则显式控制类布局是重要的。它的构造函数中用LayoutKind值初始化StructLayoutAttribute类的新实例。LayoutKind.Sequential用于强制将成员按其出现的顺序进行顺序布局。

 

  LayoutKind.Explicit用于控制每个数据成员的精确位置。利用Explicit,每个成员必须使用FieldOffsetAttribute指示此字段在类型中的位置。如:

[StructLayout(LayoutKind.Explicit,Size=16,CharSet=CharSet.Ansi)]
  
publicclassMySystemTime
  
{
  
[FieldOffset(0)]publicushortwYear;
  
[FieldOffset(2)]publicushortwMonth;
  
[FieldOffset(4)]publicushortwDayOfWeek;
  
[FieldOffset(6)]publicushortwDay;
  
[FieldOffset(8)]publicushortwHour;
  
[FieldOffset(10)]publicushortwMinute;
  
[FieldOffset(12)]publicushortwSecond;
  
[FieldOffset(14)]publicushortwMilliseconds;
  
}

  例如:下面是API函数CreateNamedPipe的原函数:

WINBASEAPI
  
HANDLE
  
WINAPI
  
CreateNamedPipeW(
  
LPCWSTRlpName,
  
DWORDdwOpenMode,
  
DWORDdwPipeMode,
  
DWORDnMaxInstances,
  
DWORDnOutBufferSize,
  
DWORDnInBufferSize,
  
DWORDnDefaultTimeOut,
  
LPSECURITY_ATTRIBUTESlpSecurityAttributes
  
);

  在C#中可这么调用(封装在类NamedPipeNative当中):

[DllImport("kernel32.dll",SetLastError=true)]
  
publicstaticexternIntPtrCreateNamedPipe(
  
    StringlpName,                            
  
    uintdwOpenMode,                         
  
    uintdwPipeMode,                          
  
    uintnMaxInstances,                        
  
    uintnOutBufferSize,                     
  
    uintnInBufferSize,
  
    uintnDefaultTimeOut,
  
    IntPtrpipeSecurityDescriptor    
  
);

 

  使用实例:

IntPtrm_HPipe=NamedPipeNative.CreateNamedPipe(m_PipeName,
  
NamedPipeNative.PIPE_ACCESS_DUPLEX,//数据双工通信
  
NamedPipeNative.PIPE_TYPE_MESSAGE|NamedPipeNative.PIPE_WAIT,//字节流,并且阻塞
  
100,//最大实例个数
  
128,//流出数据缓冲大小
  
128,//流入数据缓冲大小
  
150,//超时,毫秒
  
IntPtr.Zero//安全信息
  
);

  其中CreateNamedPipe函数,还有PIPE_ACCESS_DUPLEX,PIPE_TYPE_MESSAGE,PIPE_WAIT等字段都封装在类NamedPipeNative当中,以便于调用。

 

 

  3、EntryPoint:指示要调用的DLL入口点的名称或序号。

  如果你的方法名不想与api函数同名的话,一定要指定此参数,例如:

[DllImport("user32.dll",CharSet="CharSet.Auto",EntryPoint="MessageBox")]
  
publicstaticexternintMsgBox(IntPtrhWnd,stringtxt,stringcaption,inttype);

  4、ExactSpelling:指示是否应修改非托管DLL中的入口点的名称,以与CharSet字段中指定的CharSet值相对应。如果为true,则当DllImportAttribute.CharSet字段设置为CharSet的Ansi值时,向方法名称中追加字母A,当DllImportAttribute.CharSet字段设置为CharSet的Unicode值时,向方法的名称中追加字母W。此字段的默认值是false。

  5、PreserveSig:指示托管方法签名不应转换成返回HRESULT、并且可能有一个对应于返回值的附加[out,retval]参数的非托管签名。

  6、SetLastError:指示被调用方在从属性化方法返回之前将调用Win32APISetLastError。true指示调用方将调用SetLastError,默认为false。运行时封送拆收器将调用GetLastError并缓存返回的值,以防其被其他API调用重写。用户可通过调用GetLastWin32Error来检索错误代码。

  二、参数类型:

  1、数值型直接用对应的就可。(DWORD->int,或uint,WORD->Int16)

  2、API中字符串指针类型->.net中string,或者数组,如byte[]

  3、API中句柄(dWord)->.net中IntPtr

  4、API中结构->.net中结构或者类。注意这种情况下,要先用StructLayout特性限定声明结构或类

  公共语言运行库利用StructLayoutAttribute控制类或结构的数据字段在托管内存中的物理布局,即类或结构需要按某种方式排列。如果要将类传递给需要指定布局的非托管代码,则显式控制类布局是重要的。它的构造函数中用LayoutKind值初始化StructLayoutAttribute类的新实例。LayoutKind.Sequential用于强制将成员按其出现的顺序进行顺序布局。



相关教程