您的位置:首页 > 编程语言 > C#

.NET(C#) 平台调用:不依赖平台的GetWindowLongPtr和SetWindowLongPtr API

2012-10-19 13:19 453 查看
http://www.cnblogs.com/mgen/archive/2012/04/09/2439149.html

首先在方法声明上,由于在32位Windows上GetWindowLongPtr和SetWindowLongPtr仅仅是宏定义,不是具体函数,所以只能去使用GetWindowLong和SetWindowLongPtr函数。因此我们需要定义两份这样的函数。其次是参数在不同环境下的变化。比如GetWindowLang的函数原型:

LONG WINAPI GetWindowLong(

__in HWND hWnd,

__in int nIndex

);

它是返回LONG的。而GetWindowLongPtr的函数原型:

LONG_PTR WINAPI GetWindowLongPtr(

__in HWND hWnd,

__in int nIndex

);

它是返回LONG_PTR的。这个LONG_PTR联通其他的类型比如INT_PTR、UINT_PTR、DWORD_PTR……都是用来使定义好的类型不需要改变就可以轻松在32位和64位上的API正确运行。在32位下,它们保持自己的默认大小。在64位下,它们会被扩展成64为下的大小。而注意在.NET下,int永远是32位的(int仅仅是System.Int32类型的别名),而long(System.Int64类型)永远是64位的,因此我们只能用依赖平台大小的IntPtr来表示上述数据类型。

那么首先把这四个API都声明一下:

[DllImport("user32.dll", EntryPoint
= "GetWindowLong")]

static extern
IntPtr GetWindowLong32(IntPtr hWnd,
int nIndex);

[DllImport("user32.dll", EntryPoint
= "GetWindowLongPtr")]

static extern
IntPtr GetWindowLong64(IntPtr hWnd,
int nIndex);

[DllImport("user32.dll", EntryPoint
= "SetWindowLong")]

static extern
IntPtr SetWindowLong32(IntPtr hWnd,
int nIndex,
IntPtr dwNewLong);

[DllImport("user32.dll", EntryPoint
= "SetWindowLongPtr")]

static extern
IntPtr SetWindowLong64(IntPtr hWnd,
int nIndex,
IntPtr dwNewLong);

接着用专门的方法判断是32位还是64位执行环境,然后根据环境调用相应的本地API。

//WindowLongFlags的定义可以参考:http://www.pinvoke.net/default.aspx/Enums/WindowLongFlags.html

public static
IntPtr GetWindowLongPtr(IntPtr hWnd,
WindowLongFlags nIndex)

{

if (IntPtr.Size
== 8)

return GetWindowLong64(hWnd, (int)nIndex);

else

return GetWindowLong32(hWnd, (int)nIndex);

}

public static
IntPtr SetWindowLongPtr(IntPtr hWnd,
WindowLongFlags nIndex,
IntPtr dwNewLong)

{

if (IntPtr.Size
== 8)

return SetWindowLong64(hWnd, (int)nIndex, dwNewLong);

else

return SetWindowLong32(hWnd, (int)nIndex, dwNewLong);

}

方法定义好后,就可以调用它执行了,不过还有一个问题就是枚举值的设置,由于对应枚举对象是依赖平台的,所以把它定义成IntPtr,可是IntPtr对于枚举值的设置可能会遇到麻烦,可以使用如下方法。

首先我们使用这篇文章(.NET(C#):负数位域和正数位域)中的EnumHelper类型,接着定义一个针对基于IntPtr枚举值的有好封装类型:IntPtrEnumHelper如下代码:

static class
IntPtrEnumHelper

{

//判读是否包含指定标志位

public
static
bool HasFlags(IntPtr val,
object flag)

{

return
EnumHelper.HasFlag(val.ToInt64(), (long)flag);

}

//设置标志位

public
static
IntPtr SetFlag(IntPtr val,
object flag)

{

return
new
IntPtr(EnumHelper.SetFlag(val.ToInt64(),
(long)flag));

}

//取消标志位

public
static
IntPtr UnsetFlag(IntPtr val,
object flag)

{

return
new
IntPtr(EnumHelper.UnsetFlag(val.ToInt64(),
(long)flag));

}

}

这样的话,比如我们想要通过SetWindowLongPtr设置窗体的样式。我们可以直接用上面的方法来对基于IntPtr枚举对象进行位域的设置或者取消操作。

如下封装代码:

//WindowLongFlags定义可以参考:http://www.pinvoke.net/default.aspx/Enums/WindowLongFlags.html

//设置标志位

public static
IntPtr SetWindowStyles(IntPtr hWnd,
WindowStyles ws)

{

var style
= GetWindowLongPtr(hWnd,
WindowLongFlags.GWL_STYLE);

return SetWindowLongPtr(hWnd,
WindowLongFlags.GWL_STYLE,
IntPtrEnumHelper.SetFlag(style, ws));

}

//取消标志位

public static
IntPtr UnsetWindowStyles(IntPtr hWnd,
WindowStyles ws)

{

var style
= GetWindowLongPtr(hWnd,
WindowLongFlags.GWL_STYLE);

return SetWindowLongPtr(hWnd,
WindowLongFlags.GWL_STYLE,
IntPtrEnumHelper.UnsetFlag(style, ws));

}

代码下载:

参考这篇文章:.NET(C#):Win32窗体API封装工程
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: