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

来源:互联网 发布:犬神带什么御魂 知乎 编辑:程序博客网 时间:2024/04/30 10:21

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 externIntPtr GetWindowLong32(IntPtr hWnd,int nIndex);

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

static externIntPtr GetWindowLong64(IntPtr hWnd,int nIndex);

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

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

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

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

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

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

public staticIntPtr GetWindowLongPtr(IntPtr hWnd,WindowLongFlags nIndex)

{

    if (IntPtr.Size== 8)

        return GetWindowLong64(hWnd, (int)nIndex);

    else

        return GetWindowLong32(hWnd, (int)nIndex);

}

public staticIntPtr 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 classIntPtrEnumHelper

{

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

    publicstatic bool HasFlags(IntPtr val,object flag)

    {

        returnEnumHelper.HasFlag(val.ToInt64(), (long)flag);

    }

    //设置标志位

    publicstatic IntPtr SetFlag(IntPtr val,object flag)

    {

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

    }

    //取消标志位

    publicstatic IntPtr UnsetFlag(IntPtr val,object flag)

    {

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

    }

}

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

如下封装代码:

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

//设置标志位

public staticIntPtr SetWindowStyles(IntPtr hWnd,WindowStyles ws)

{

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

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

}

//取消标志位

public staticIntPtr 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封装工程