C#调用Win32API 的高级用法:结构体内存布局 回调函数 指针参数以及数据类型映射

结构体内存布局:

许多受管辖的动态链接库函数期望你能够传递一个复杂的参数类型给函数,譬如一个用户定义的结构类型成员或者受管辖代码定义的一个类成员,这时你必须提供额外的信息格式化这个类型,以保持参数原有的布局和对齐。 

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

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

[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);  

 

回调函数的传递:  
从受管辖的代码中调用大多数动态链接库函数,你只需创建一个受管辖的函数定义,然后调用它即可,这个过程非常直接。  
如果一个动态链接库函数需要一个函数指针作为参数,你还需要做以下几步:  
首先,你必须参考有关这个函数的文档,确定这个函数是否需要一个回调;第二,你必须在受管辖代码中创建一个回调函数;最后,你可以把指向这个函数的指针作为一个参数创递给DLL函数,. 

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

分下面几个步骤:  
1. 在实现调用前先参考函数的声明  


BOOL EnumWindows(WNDENUMPROC lpEnumFunc, LPARMAM IParam)  


显然这个函数需要一个回调函数地址作为参数.  
2. 创建一个受管辖的回调函数,这个例子声明为代表类型(delegate),也就是我们所说的回调,它带有两个参数hwnd和lparam,第一个参数是一个窗口句柄,第二个参数由应用程序定义,两个参数均为整形。 

当这个回调函数返回一个非零值时,标示执行成功,零则暗示失败,这个例子总是返回True值,以便持续枚举。


3. 最后创建以代表对象(delegate),并把它作为一个参数传递给EnumWindows 函数,平台会自动地 把代表转化成函数能够识别的回调格式。 

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

 

Win32函数和.NET类库的对应关系

另外,还有很多的API函数,其实已经在.NET的类库中完成了封装,可以直接使用,对应函数的映射关系如下:

Microsoft Win32 to Microsoft .NET Framework API Map

 

 

 C#与C++之间类型的对应

Windows Data Type

.NET Data Type

BOOL, BOOLEAN

Boolean or Int32

BSTR

String

BYTE

Byte

CHAR

Char

DOUBLE

Double

DWORD

Int32 or UInt32

FLOAT

Single

HANDLE (and all other handle types, such as HFONT and HMENU)

IntPtr, UintPtr or HandleRef

HRESULT

Int32 or UInt32

INT

Int32

LANGID

Int16 or UInt16

LCID

Int32 or UInt32

LONG

Int32

LPARAM

IntPtr, UintPtr or Object

LPCSTR

String

LPCTSTR

String

LPCWSTR

String

LPSTR

String or StringBuilder*

LPTSTR

String or StringBuilder

LPWSTR

String or StringBuilder

LPVOID

IntPtr, UintPtr or Object

LRESULT

IntPtr

SAFEARRAY

.NET array type

SHORT

Int16

TCHAR

Char

UCHAR

SByte

UINT

Int32 or UInt32

ULONG

Int32 or UInt32

VARIANT

Object

VARIANT_BOOL

Boolean

WCHAR

Char

WORD

Int16 or UInt16

WPARAM

IntPtr, UintPtr or Object

 

 

另: 在进行string转换时,需要加入前缀[MarshalAs(UnmanagedType.LPStr)]

LPDWORD   对应于  ref int

C/C++

C#

HANDLE, LPDWORD, LPVOID, void*

IntPtr

LPCTSTR, LPCTSTR, LPSTR, char*, const char*, Wchar_t*, LPWSTR

String [in], StringBuilder [in, out]

DWORD, unsigned long, Ulong

UInt32, [MarshalAs(UnmanagedType.U4)]

bool

bool

LP<struct>

[In] ref <struct>

SIZE_T

uint

LPDWORD

out uint

LPTSTR

[Out] StringBuilder

PULARGE_INTEGER

out ulong

WORD

uInt16

Byte, unsigned char

byte

Short

Int16

Long, int

Int32

float

single

double

double

NULL pointer

IntPtr.Zero

Uint

Uint32

 

Wtypes.h 中的非托管类型

非托管 C 语言类型

托管类名

说明

HANDLE

void*

System.IntPtr

在 32 位 Windows 操作系统上为 32 位,在 64 位 Windows 操作系统上为 64 位。

BYTE

unsigned char

System.Byte

8

SHORT

short

System.Int16

16

WORD

unsigned short

System.UInt16

16

INT

int

System.Int32

32

UINT

unsigned int

System.UInt32

32

LONG

long

System.Int32

32

BOOL

long

System.Int32

32

DWORD

unsigned long

System.UInt32

32

ULONG

unsigned long

System.UInt32

32

CHAR

char

System.Char

用 ANSI 修饰。

LPSTR

char*

System.String 或 System.Text.StringBuilder

用 ANSI 修饰。

LPCSTR

Const char*

System.String 或 System.Text.StringBuilder

用 ANSI 修饰。

LPWSTR

wchar_t*

System.String 或 System.Text.StringBuilder

用 Unicode 修饰。

LPCWSTR

Const wchar_t*

System.String 或 System.Text.StringBuilder

用 Unicode 修饰。

FLOAT

Float

System.Single

32

DOUBLE

Double

System.Double

64

 

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值