SetWindowLongPtr

SetWindowLong function is well known among those who want to change various properties of already created windows. However, there is a slight issue with it on 64-bit Windows – it does not work properly. Someone originally defined this function to return LONG. Unfortunately LONG is actually defined as a 32-bit integer on both 32 and 64-bit Windows. Since SetWindowLong is also intended for setting some pointer sized properties (e.g. GWL_WNDPROC) this will not do.

Fortunately Microsoft also saw the error and created new function SetWindowLongPtr which corrects declaration so it is valid for both 32 and 64-bit world. Except they didn’t.

While comment clearly says “This function has been superseded by the SetWindowLongPtr function. To write code that is compatible with both 32-bit and 64-bit versions of Windows, use the SetWindowLongPtr function.“, this is not really the full truth. If you check its declaration in WinUser.h you will see the ugly truth – on 32-bit system, SetWindowLongPtr is a simple #define toward good old SetWindowLong. This means that statement is valid for someone working in C/C++. For all other languages, this statement is misleading at best.

Solution for C# is simple, just check whether code is running in 32-bit mode and call SetWindowLong yourself. In all other cases just call SetWindowLongPtr. Code Analysis will complain a bit about CA1901 (P/Invoke declarations should be portable) and CA1400 (P/Invoke entry points should exist) but we can safely ignore these warnings if we make sure to call correct function ourselves.

These declarations should do the trick:

public static IntPtr SetWindowLongPtr(IntPtr hWnd, Int32 nIndex, IntPtr dwNewLong) {
    if (IntPtr.Size == 4) {
        return SetWindowLongPtr32(hWnd, nIndex, dwNewLong);
    } else {
        return SetWindowLongPtr64(hWnd, nIndex, dwNewLong);
    }
}

[DllImport("user32.dll", SetLastError = true, EntryPoint = "SetWindowLong")]
[SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", MessageId = "return", Justification = "This declaration is not used on 64-bit Windows.")]
[SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", MessageId = "2", Justification = "This declaration is not used on 64-bit Windows.")]
private static extern IntPtr SetWindowLongPtr32(IntPtr hWnd, Int32 nIndex, IntPtr dwNewLong);

[DllImport("user32.dll", SetLastError = true, EntryPoint = "SetWindowLongPtr")]
[SuppressMessage("Microsoft.Interoperability", "CA1400:PInvokeEntryPointsShouldExist", Justification = "Entry point does exist on 64-bit Windows.")]
private static extern IntPtr SetWindowLongPtr64(IntPtr hWnd, Int32 nIndex, IntPtr dwNewLong);

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.