从32位拖放到64位 [英] Drag-and-drop from 32 to 64-bit

查看:56
本文介绍了从32位拖放到64位的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am writing a C program that accepts drag-and-drop of files. When it is compiled in 32-bit, it works in any case. But when it compiled in 64-bit, it works only for files dragged from a 64-bit application:

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

我仍然收到WM_DROPFILES消息,但是DragQueryFile不返回任何信息(文件数为0).

I still get the WM_DROPFILES message, but DragQueryFile returns nothing (the number of files is 0).

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

This seems to be an issue for a lot of applications but I would like to know if there is a workaround about that.

  • 如果我将文件从64位可执行文件拖放到我的64位应用程序中,则wParam的值将为0x000000F211C000B8(这表明没有强制转换问题).
  • 接下来,在不关闭我的应用程序的情况下,如果我从32位可执行文件中拖动文件,则wParam将具有0x0000000011C000B8或0xFFFFFFFF11C000B8之类的东西,这意味着高32位无效.
  • 如果我用上一条消息中的有效高位替换无效的高位(在本例中为0x000000F2),那么DragQueryFile可以工作!

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

So the data are here, somewhere, I just don't know how to retrieve them (at least without an ugly hack).

我不会提供任何代码,因为我假设回答的人对此问题有一定的了解,而该问题会影响大量的软件.

I will provide no code because I assume that those who answer know something about this issue that affects a large number of softwares.

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

------ EDIT ----------

再现它的最小代码

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(甚至只跳到它的第一条指令),wParam的高32位将全部为1.如果不调用它,则通过自行设置WS_EX_ACCEPTFILES exstyle-wParam的所有高位将为0

interesting that if call DragAcceptFiles (even only jump on first it instruction) high 32 bits of wParam will be all 1. if not call it, by set WS_EX_ACCEPTFILES exstyle by self - all high bits of wParam will be 0

要进行测试,可以执行32位记事本,打开打开文件"对话框,然后将任何文件拖放到我们的窗口中

for test can exec 32 bit notepad, open Open File Dialog and drag-drop any file to our window

推荐答案

问题已重新打开,我可以发布适当的答案.

As the question has been reopened, I can post a proper answer.

这确实是Windows的错误.在64位过程中,wParam是一个64位值,并按原样发送"HDROP",它实际上是指向

This is truly a bug of Windows. In a 64-bit process, wParam is a 64-bit value and is used as is to send a "HDROP", which is in fact a pointer to a pointer to a DROPFILES structure. The tests showed that the shell uses the whole 64 bits, and writes the data into the heap. If a file is dragged from a 32-bit application, the data are still properly written into the heap, even if the latter is located above 4GB. But despite that, in this case, wParam is converted to a 32-bit value, and then sign-extended to 64-bit.

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

In fact, when we drag a file from a 32-bit application to a 64-bit one, the latter is supposed to crash, because we provide an incorrect pointer to DragQueryFile(). But it does not, because DragQueryFile() handles these exceptions.

现在,解决方案:

  • 使用IDropTarget 界面.如果您不关心使用OLE并仅在读取可执行文件中已经存在于RAM中的文件名的情况下在可执行文件中添加大约10KB(这不是我的情况),这是一个很好的解决方案(Microsoft推荐).

  • Use the IDropTarget interface. This is a good solution (and recommended by Microsoft) if you don't care about using OLE and adding about 10KB in your executable only for reading a file name that is already in RAM (it's not my case).

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

Find a way to retrieve the high part of wParam. As explained, this value is a pointer to the heap. The closest value is given by GlobalAlloc(GMEM_MOVEABLE, 0). It usually gives the value of wParam (or the one it is supposed to have) +16. Even if it can be sometimes slightly higher, this is enough to retrieve the lacking high order 32 bits of wParam. To cover the unlikely case where the heap overlaps a 4GB boundary, we can try by adding or removing 1 to the high order 32 bits. Note that GlobalFree() remains required. Otherwise, you consume a few bytes (16 according to my tests) after each call to GlobalAlloc().

禁用高熵ASLR .这需要Windows 8或更高版本,这就是为什么此错误很少在Windows 7和更低版本上发生的原因.在Windows 7上,地址是随机的,但保持在4GB的限制内.就是说,由于符号扩展,您可能仍必须将高阶32位清零.而且这种解决方案意味着安全性降低.

Disable the High Entropy ASLR. This one requires Windows 8 or later, and that's why this bug rarely occurs on Windows 7 and prior. On Windows 7, the addresses are randomized, but remain under the 4GB limit. That said, you may still have to zero the high order 32 bits, because of the sign extension. And this solution means a security decrease.

这篇关于从32位拖放到64位的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆