【问题标题】:How do I pinvoke to GetWindowLongPtr and SetWindowLongPtr on 32-bit platforms?如何在 32 位平台上调用 GetWindowLongPtr 和 SetWindowLongPtr?
【发布时间】:2011-03-21 14:23:20
【问题描述】:

我想 P/Invoke 到 GetWindowLongPtrSetWindowLongPtr,但我看到关于它们的信息相互矛盾。

一些消息来源说,在 32 位平台上,GetWindowLongPtr 只是一个调用 GetWindowLong 的预处理器宏,而 GetWindowLongPtr 在 user32.dll 中不作为入口点存在。例如:

  • pinvoke.net entry for SetWindowLongPtr 有一个静态方法,它检查 IntPtr.Size,然后调用 SetWindowLong 或 SetWindowLongPtr,并带有注释说“旧版操作系统不支持 SetWindowLongPtr”。没有解释“旧版操作系统”的含义。
  • answer on StackOverflow 声明“在 32 位系统上,GetWindowLongPtr 只是一个指向 GetWindowLong 的 C 宏”。

因此,这些来源似乎表明 *Ptr 入口点根本不存在于 user32.dll 版本中,例如 32 位 Windows 7。

但我在 MSDN 文档中没有看到任何迹象。根据 MSDN,SetWindowLongPtr 取代了 SetWindowLong,简单明了。根据SetWindowLongPtr page 的要求部分,SetWindowLongPtr 似乎自 Windows 2000(客户端和服务器版本)以来一直存在于 user32.dll 中。同样,没有提及 32 位操作系统中缺少入口点。

怀疑事实介于两者之间:当您告诉 C++ 编译器以较旧的操作系统为目标(即编译将在 Win9x 和 NT4 上运行的东西)时,头文件将 SetWindowLongPtr 声明为调用 SetWindowLong 的宏,但入口点可能确实存在于 Windows 2000 及更高版本中,如果您告诉编译器以这些平台为目标,您将直接获得它(而不是宏)。但这只是一个猜测;我真的没有资源或专业知识来挖掘和验证它。

目标平台也有可能发挥了作用——如果您为 x86 平台编译应用程序,那么您不应该在 64 位操作系统上调用 SetWindowLongPtr。再说一次,我知道的足以思考这个问题,但我不知道如何找到答案。 MSDN 似乎暗示 SetWindowLongPtr 总是正确的。

谁能告诉我简单地 P/Invoke 到 SetWindowLongPtr 并完成它是否安全? (假设 Windows 2000 及更高版本。) P/Invoking to SetWindowLongPtr 会给我正确的入口点:

  • 如果我在 32 位操作系统上运行针对 x86 平台的应用程序?
  • 如果我在 64 位操作系统上运行针对 x86 平台的应用程序?
  • 如果我在 64 位操作系统上运行针对 x64 平台的应用程序?

【问题讨论】:

    标签: c# .net pinvoke setwindowlong getwindowlong


    【解决方案1】:

    我建议您像 Windows 窗体内部那样处理这个问题:

    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);
    

    【讨论】:

    • 你没有说你在哪里找到了这段代码,但我在 System.Windows.Forms.UnsafeNativeMethods 上找到了它。
    • @hans-passant:感谢这段代码。它有助于实现将对话框模式连接到另一个对话框的解决方案:stackoverflow.com/a/16088711/654244
    • 属性不应该包含SetLastError = true以便以后正确检索可能的错误吗?
    • 没错。但是,如果失败,该函数将返回 0。那么你怎么知道它是失败还是值实际上为零?你不知道。
    • @HansPassant:是的,你可以知道,文档甚至告诉你如何 - 先调用SetLastError(0),然后如果函数返回0,你可以调用GetLastError()来知道函数是否失败或者返回值真的是 0。
    【解决方案2】:
    1. 打开头文件(在 MSDN 页面上,它被列为 Winuser.h)。 Win32 标头通常位于 C:\Program Files\Microsoft SDKs\Windows\v7.0A\Include
    2. 搜索SetWindowLongPtr/GetWindowLongPtr的所有实例。
    3. 注意当_WIN64被定义时,它们是函数;如果不是,他们是 #define'd 到 SetWindowLong/GetWindowLong

    这意味着32位操作系统可能没有SetWindowLongPtr/GetWindowLongPtr作为实际功能,所以看起来pinvoke.net上的评论是正确的。

    更新(关于 _WIN64 的更多说明):

    _WIN64 由 C/C++ 编译器在编译 64 位代码(仅在 64 位操作系统上运行)时定义。所以这意味着任何使用 SetWindowLongPtr/GetWindowLongPtr 的 64 位代码都将使用实际函数,但任何使用它们的 32 位代码将使用 SetWindowLong/GetWindowLong 代替。这包括在 64 位操作系统上运行的 32 位代码。

    要在 C# 中模拟相同的行为,我建议检查 IntPtr.Size,就像 pinvoke.net 所做的那样;它会告诉您运行的是 32 位还是 64 位代码。 (请记住,32 位代码可以在 64 位操作系统上运行)。在托管代码中使用 IntPtr.Size 模拟与 _WIN64 对本机代码所做的相同行为。

    【讨论】:

    • 我的v7.0A 目录没有Include 子目录——只有BinBootstrapper。不过,我确实在v5.0 下找到了Include
    • 同样的交易。它们只是 Win32 SDK 的不同版本。
    猜你喜欢
    • 2016-11-30
    • 2014-03-10
    • 2015-02-27
    • 1970-01-01
    • 1970-01-01
    • 2010-10-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多