首先在方法聲明上,由於在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封裝工程