v77*_*v77 6 c winapi drag-and-drop
我正在编写一个接受文件拖放的C程序.当它以32位编译时,无论如何都适用.但是当它以64位编译时,它仅适用于从64位应用程序拖动的文件:
我仍然得到WM_DROPFILES消息,但DragQueryFile没有返回任何内容(文件数为0).
这似乎是许多应用程序的问题,但我想知道是否有解决方法.
编辑:
所以数据在这里,某处,我只是不知道如何检索它们(至少没有丑陋的黑客).
编辑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);
}
}
Run Code Online (Sandbox Code Playgroud)
有趣的是,如果调用DragAcceptFiles(甚至只跳过它的第一个指令)高位32位的wParam将全部为1.如果不调用它,通过设置WS_EX_ACCEPTFILES exstyle by self-所有高位wParam将为0
for test可以执行32位记事本,打开Open File Dialog并将任何文件拖放到我们的窗口
由于问题已重新提出,我可以发布正确的答案。
这确实是 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().
禁用高熵 ASLR。此问题需要 Windows 8 或更高版本,这就是为什么此错误很少出现在 Windows 7 及更早版本上。在 Windows 7 上,地址是随机的,但仍低于 4GB 限制。也就是说,由于符号扩展,您可能仍然需要将高位 32 位清零。而这种解决方案意味着安全性的降低。
| 归档时间: |
|
| 查看次数: |
1241 次 |
| 最近记录: |