Drag and Drop from 32 to 64 bit

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

  • 32-bit -> 32-bit: success
  • 64-bit -> 64-bit: success
  • 64-bit -> 32-bit: success
  • 32-bit β†’ 64-bit: crash

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

This seems to be a problem for many applications, but I would like to know if there is a workaround.

Edit:

  • If I drag a file from a 64-bit executable to my 64-bit application, wParam has a value such as 0x000000F211C000B8 (which indicates there is no problem with casting).
  • Then, without closing my application, if I dragged a file from a 32-bit executable, wParam will have something like 0x0000000011C000B8 or 0xFFFFFFFF11C000B8, which means that 32 high-order bits are invalid.
  • If I replaced the invalid high order with the valid one from the previous message (in this example, it will be 0x000000F2), then DragQueryFile will work!

So, the data here, somewhere, I just don’t know how to get it (at least without an ugly hack).

Edit 2:

I will not indicate the code, because I assume that those who answer know something about this problem, which affects a large number of programs.

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

minimum code that reproduces it

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

Interestingly, if you call DragAcceptFiles (even just jump to its instruction first), then the high 32 bits of wParam will be equal to 1. If you do not call it, set WS_EX_ACCEPTFILES exstyle yourself - all the most significant bits of wParam will be 0

for verification, you can run a 32-bit notepad, open the "Open file" dialog box and drag any file into our window

+6
source share
1 answer

When the question is resumed, I can send the correct answer.

This is really a Windows error. In a 64-bit process, wParam is a 64-bit value and is used to send "HDROP", which is actually a pointer to a pointer to DROPFILES . Tests showed that the shell uses all 64 bits and writes data to the heap. If a file is dragged from a 32-bit application, the data is still written to the heap , even if the latter is above 4 GB . But despite this, in this case wParam is converted to a 32-bit value, and then subscribes to 64-bit.

In fact, when we drag a file from a 32-bit application to a 64-bit application, the latter should crash, because we are providing an invalid pointer to DragQueryFile() . But this is not so, because DragQueryFile() handles these exceptions.

Now solutions:

  • Use the IDropTarget interface . This is a good solution (and recommended by Microsoft) if you do not want to use OLE and add about 10 KB to your read-only file name that is already in RAM (this is not my case).

  • Find a way to get the high part of wParam. As explained, this value is a pointer to a heap. The closest value is given by GlobalAlloc(GMEM_MOVEABLE, 0) . Usually it gives the value of wParam (or the one it should have) +16. Even if it can sometimes be slightly higher, this is enough to get the missing 32-bit wParam bits. To cover the unlikely event that the heap spans 4 GB, we can try adding or removing 1 to 32 high order bits. Note that GlobalFree() remains necessary. Otherwise, you consume several bytes (according to my tests of 16) after each call to GlobalAlloc() .

  • Disable High Entropy ASLR . This requires Windows 8 or later, and why this error rarely occurs in Windows 7 or earlier. In Windows 7, addresses are randomized, but remain under the 4 GB limit. However, you still have to clear 32 high order bits due to the expansion of the character. And this decision means reduced security.

+2
source

All Articles