c - 从 32 位拖放到 64 位

标签 c winapi drag-and-drop

我正在编写一个接受文件拖放的 C 程序。 当它以 32 位编译时,它在任何情况下都可以工作。但是当它以 64 位编译时,它仅适用于从 64 位应用程序中拖动的文件:

  • 32 位 -> 32 位:成功
  • 64 位 -> 64 位:成功
  • 64 位 -> 32 位:成功
  • 32 位 -> 64 位:失败

我仍然收到 WM_DROPFILES 消息,但 DragQueryFile 没有返回任何内容(文件数为 0)。

这似乎是很多应用程序的问题,但我想知道是否有解决方法。

编辑:

  • 如果我将一个文件从 64 位可执行文件拖放到我的 64 位应用程序,wParam 的值如 0x000000F211C000B8(表明不存在转换问题)。
  • 接下来,在不关闭我的应用程序的情况下,如果我从 32 位可执行文件中拖动文件,wParam 将具有类似于 0x0000000011C000B8 或 0xFFFFFFFF11C000B8 的内容,这意味着高 32 位无效。
  • 如果我用前一条消息中的有效高位替换无效的高位(在本例中为 0x000000F2),则 DragQueryFile 有效!

所以数据在这里,在某个地方,我只是不知道如何检索它们(至少没有丑陋的黑客)。

编辑 2:

我不会提供任何代码,因为我假设那些回答的人对这个影响大量软件的问题有所了解。

-------- 编辑------------

复制它的最少代码

LRESULT WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    WCHAR sz[32];
    switch (uMsg)
    {
    case WM_DROPFILES:
        swprintf(sz, L"%p", wParam);// look for wParam
        MessageBox(0,0,sz,0);
        break;
    case WM_NCCREATE:
        DragAcceptFiles(hwnd, TRUE);
        break;
    case WM_NCDESTROY:
        PostQuitMessage(0);
        break;
    }

    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

void minimal()
{
    static WNDCLASS wndcls = { 0, WindowProc, 0, 0, 0, 0, 0, 0, 0, L"testwnd" };
    if (RegisterClass(&wndcls))
    {
        if (HWND hwnd = CreateWindowEx(WS_EX_ACCEPTFILES, wndcls.lpszClassName, 0, 
            WS_OVERLAPPEDWINDOW|WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT,
            CW_USEDEFAULT, CW_USEDEFAULT, HWND_DESKTOP, 0, 0, 0))
        {
            MSG msg;
            while (0 < GetMessage(&msg, 0, 0, 0))
            {
                if (msg.message == WM_DROPFILES)
                {
                    // look for msg.wParam returned by GetMessage
                    WCHAR name[256];
                    DragQueryFile((HDROP)msg.wParam, 0, name, RTL_NUMBER_OF(name));
                }

                DispatchMessage(&msg);
            }
        }
        UnregisterClass(wndcls.lpszClassName, 0);
    }
}

有趣的是,如果调用 DragAcceptFiles(即使只跳转到第一个 it 指令)wParam 的高 32 位将全为 1。如果不调用它,通过自行设置 WS_EX_ACCEPTFILES exstyle - wParam 的所有高位将为 0

为了测试可以执行 32 位记事本,打开打开文件对话框并将任何文件拖放到我们的窗口

最佳答案

由于问题已重新打开,我可以发布正确的答案。

这确实是 Windows 的一个错误。在 64 位进程中,wParam 是一个 64 位值,用于发送“HDROP”,它实际上是指向 DROPFILES 的指针。结构体。 测试表明 shell 使用整个 64 位,并将数据写入堆中。 如果从 32 位应用程序中拖出文件,数据仍会正确写入堆中,即使后者位于 4GB 以上。但尽管如此,在这种情况下,wParam 被转换为 32 位值,然后符号扩展为 64 位。

事实上,当我们将文件从 32 位应用程序拖到 64 位应用程序时,后者应该会崩溃,因为我们提供了一个指向 DragQueryFile() 的错误指针。但它不会,因为 DragQueryFile() 会处理这些异常。

现在,解决方案:

  • 使用 IDropTarget界面。如果您不关心使用 OLE 并在可执行文件中添加大约 10KB 只是为了读取 RAM 中已有的文件名(我不是这种情况),这是一个很好的解决方案(并且由 Microsoft 推荐)。

  • 找到一种方法来检索 wParam 的高位部分。如前所述,该值是指向堆的指针。最接近的值由 GlobalAlloc(GMEM_MOVEABLE, 0) 给出。它通常给出 wParam 的值(或它应该具有的值)+16。即使它有时可以稍微高一点,这也足以检索 wParam 缺少的高位 32 位。 为了覆盖堆与 4GB 边界重叠的不太可能的情况,我们可以尝试在高位 32 位中添加或删除 1。 请注意,GlobalFree() 仍然是必需的。否则,每次调用 GlobalAlloc() 后,您都会消耗几个字节(根据我的测试为 16 个字节)。

  • 禁用 High Entropy ASLR .这个需要 Windows 8 或更高版本,这就是为什么这个错误很少发生在 Windows 7 和更早版本上的原因。在 Windows 7 上,地址是随机的,但仍低于 4GB 的限制。 也就是说,由于符号扩展,您可能仍然需要将高位 32 位归零。而这个解决方案意味着安全性下降。

关于c - 从 32 位拖放到 64 位,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39612616/

相关文章:

c - 静态与全局

c++ - 如何将 PreShutdown 事件添加到 ATL 服务?

swing - 自 1.8.0_40 起,JavaFX swing 节点 D&D 失败

来自 toolStrip 的 C# DragDrop

cocoa - 在沙盒 Mac 中执行拖放

c++ - 数字中设置的位数

c - 在 Opencv 中使用 HSV 值检测眼睛

c - 串口ReadFile读取0字节返回true

c++ - 实现 C++ Win32 启动画面的最快方法

c - c中替换头文件iostream.h