Как вызвать GetWindowLongPtr и SetWindowLongPtr на 32-разрядных платформах?

Я хочу P/Invoke для GetWindowLongPtr и SetWindowLongPtr, и я вижу противоречивую информацию о них .

Некоторые источники говорят, что на 32-разрядных платформах GetWindowLongPtr — это просто макрос препроцессора, который вызывает GetWindowLong, а GetWindowLongPtr не существует в качестве точки входа в user32.dll. Например:

  • Запись pinvoke.net для SetWindowLongPtr имеет статический метод, который проверяет IntPtr. Size, а затем вызывает SetWindowLong или SetWindowLongPtr с комментарием о том, что «устаревшие ОС не поддерживают SetWindowLongPtr». Нет никакого объяснения того, что имеется в виду под «старыми операционными системами».
  • В ответе на StackOverflow говорится: "В 32-битных системах GetWindowLongPtr - это просто C макрос, указывающий на GetWindowLong".

Таким образом, эти источники указывают на то, что точек входа *Ptr просто нет в версии user32.dll, которая поставляется, скажем, с 32-разрядной Windows 7.

Но я не вижу никаких указаний на это в документации MSDN. Согласно MSDN, SetWindowLongPtr заменяет SetWindowLong, обычный и просто. И в соответствии с разделом требований на странице SetWindowLongPtr. , похоже, что SetWindowLongPtr находится в user32.dll с Windows 2000 (как в клиентской, так и в серверной версиях). Опять же, нет упоминания об отсутствии точек входа в 32-битных ОС.

Я подозреваю, что истина находится где-то посередине: когда вы говорите компилятору C++ ориентироваться на более старые ОС (т. е. компилировать что-то, что будет работать на Win9x и NT4), то файлы заголовков объявляют SetWindowLongPtr как макрос, который вызывает SetWindowLong, но точка входа, вероятно, существует в Windows 2000 и более поздних версиях, и вы получите ее напрямую (вместо макроса), если вы укажете компилятору ориентироваться на эти платформы. Но это всего лишь предположение; У меня действительно нет ресурсов или ноу-хау, чтобы копаться и проверять это.

Также возможно, что целевая платформа играет роль — если вы компилируете приложение для платформы x86, вам не следует вызывать SetWindowLongPtr в 64-разрядной ОС. Опять же, я знаю достаточно, чтобы обдумать вопрос, но не знаю, как найти ответ. MSDN, кажется, предполагает, что SetWindowLongPtr всегда правильный.

Может ли кто-нибудь сказать мне, безопасно ли просто P/Invoke для SetWindowLongPtr и покончить с этим? (Предположим, Windows 2000 и более поздние версии.) Будет ли P/Invoking SetWindowLongPtr давать мне правильную точку входа:

  • если я запускаю приложение для платформы x86 в 32-битной ОС?
  • если я запускаю приложение для платформы x86 в 64-битной ОС?
  • если я запускаю приложение для платформы x64 в 64-битной ОС?

person Joe White    schedule 27.07.2010    source источник


Ответы (2)


Я бы рекомендовал вам справиться с этим так, как Windows Forms делает это внутри:

public static IntPtr GetWindowLong(HandleRef hWnd, int nIndex)
{
    if (IntPtr.Size == 4)
    {
        return GetWindowLong32(hWnd, nIndex);
    }
    return GetWindowLongPtr64(hWnd, nIndex);
}


[DllImport("user32.dll", EntryPoint="GetWindowLong", CharSet=CharSet.Auto)]
private static extern IntPtr GetWindowLong32(HandleRef hWnd, int nIndex);

[DllImport("user32.dll", EntryPoint="GetWindowLongPtr", CharSet=CharSet.Auto)]
private static extern IntPtr GetWindowLongPtr64(HandleRef hWnd, int nIndex);
person Hans Passant    schedule 27.07.2010
comment
Вы не сказали, где нашли этот код, но я нашел его в System.Windows.Forms.UnsafeNativeMethods. - person Joe White; 28.07.2010
comment
@hans-passant: Спасибо за этот фрагмент кода. Было полезно реализовать решение для модального диалога для другого диалога: stackoverflow.com/a/16088711/654244 - person KyleK; 18.04.2013
comment
Разве атрибут не должен включать SetLastError = true, чтобы позже правильно получить возможную ошибку? - person Ray; 21.09.2016
comment
Это правильно. Однако функция возвращает 0 в случае сбоя. Итак, как узнать, произошел ли сбой или значение на самом деле равно нулю? Вы не можете знать. - person Hans Passant; 21.09.2016
comment
@HansPassant: да, вы можете знать, и документация даже говорит вам, как - сначала вызовите SetLastError(0), а затем, если функция возвращает 0, вы можете вызвать GetLastError(), чтобы узнать, не удалось ли выполнить функцию или возвращаемое значение действительно равно 0. - person Remy Lebeau; 31.05.2017
comment
Я не вижу такого словоблудия в текущих статьях MSDN. - person Hans Passant; 31.05.2017

  1. Откройте заголовочный файл (на странице MSDN он указан как Winuser.h). Заголовки Win32 обычно находятся по адресу C:\Program Files\Microsoft SDKs\Windows\v7.0A\Include.
  2. Найдите все экземпляры SetWindowLongPtr/GetWindowLongPtr.
  3. Обратите внимание, что когда _WIN64 определено, это функции; когда это не так, они #define превращаются в SetWindowLong/GetWindowLong.

Это означает, что 32-битные ОС могут не иметь SetWindowLongPtr/GetWindowLongPtr в качестве фактической функции, поэтому может показаться, что комментарий на pinvoke.net верен.

Обновление (дополнительные разъяснения по _WIN64):

_WIN64 определяется компилятором C/C++ при компиляции 64-битного кода (который будет работать только в 64-битной ОС). Таким образом, это означает, что любой 64-битный код, использующий SetWindowLongPtr/GetWindowLongPtr, будет использовать фактические функции, но любой 32-битный код, использующий их, вместо этого будет использовать SetWindowLong/GetWindowLong. Сюда входит 32-битный код, работающий в 64-битной ОС.

Чтобы эмулировать такое же поведение в C#, я рекомендую проверить IntPtr.Size, как это сделано на pinvoke.net; который говорит вам, используете ли вы 32-битный или 64-битный код. (Учитывая, что 32-битный код может работать в 64-битной ОС). Использование IntPtr.Size в управляемом коде эмулирует то же поведение, что и _WIN64 для машинного кода.

person Stephen Cleary    schedule 27.07.2010
comment
В моем каталоге v7.0A нет подкаталога Include — только Bin и Bootstrapper. Однако я нашел Include под v5.0. - person Joe White; 28.07.2010
comment
Та же сделка. Это просто разные версии Win32 SDK. - person Stephen Cleary; 28.07.2010