2016-09-21 3 views
6

Я пишу программу на C, которая принимает перетаскивание файлов. Когда он скомпилирован в 32-разрядный, он работает в любом случае. Но когда он компилируется в 64-битном, это работает только для файлов, тащили из 64-битного приложения:Перетаскивание с 32 до 64 бит

  • 32-бит -> 32-бит: успех
  • 64-битном -> 64-бит: успех
  • 64-бит -> 32-бит: успех
  • 32-бит -> 64-бит: сбой

я все еще получаю сообщение WM_DROPFILES, но DragQueryFile ничего не возвращает (количество файлов 0).

Это, кажется, проблема для множества приложений, но я хотел бы знать, есть ли обходной путь об этом.

Edit:

  • Если я перетащить и падение файла из 64-разрядных исполняемого файла для моего 64-битного приложения, WPARAM имеет такие значения, как 0x000000F211C000B8 (который показывает, что нет ни одного вопроса, литого).
  • Далее, не закрывая мое приложение, если я перетащил файл из 32-разрядного исполняемого файла, wParam будет иметь что-то вроде 0x0000000011C000B8 или 0xFFFFFFFF11C000B8, а это значит, что 32 бита высокого порядка недействительны.
  • Если я заменил недопустимый высокий порядок на действующий из предыдущего сообщения (в этом примере это будет 0x000000F2), тогда будет работать DragQueryFile!

Итак, данные здесь, где-то, я просто не знаю, как их получить (по крайней мере, без уродливого взлома).

Edit 2:

Я не дам никакого кода, потому что я полагаю, что те, кто отвечает что-то знать об этой проблеме, которая затрагивает большое количество программного обеспечения.

------ 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 вызов (даже только прыгать на первой инструкции так ли) максимум 32 бит wParam будут все 1. если не вызывать его, задайте WS_EX_ACCEPTFILES exstyle самостоятельно - все старшие бит wParam будут 0

для тестового exec 32-битного блокнота, открыть диалог открытия файла и перетащить, отбросить любой файл в наше окно

+0

Просьба указать либо C, либо C++: это может привести к привлечению downvotes, если вы сохраните его таким образом. Также теги windows API. – Bathsheba

+0

Пожалуйста, опубликуйте [SSCCE] (http://sscce.org). –

+1

Просто гадать здесь. По-видимому, эта особая функция имеет специальный случай для значения 0xFFFFFFFF. Если код использует эту функцию, и кто-то написал код, например '~ 0', чтобы получить это значение, тогда код может превратиться в не переносимый, когда меняются размеры' int'. – Lundin

ответ

2

Поскольку вопрос был вновь открыт, я могу отправить правильный ответ.

Это действительно ошибка Windows. В 64-битном процессе wParam является 64-битным значением и используется как для отправки «HDROP», который на самом деле является указателем на указатель на структуру DROPFILES. Тестирование показало, что оболочка использует все 64 бита и записывает данные в кучу. Если файл перетаскивается из 32-разрядного приложения, данные все же должным образом записываются в кучу, , даже если последний находится выше 4 ГБ. Но, несмотря на это, в этом случае wParam преобразуется в 32-битное значение, а затем расшифровывается до 64-битного.

Фактически, когда мы перетаскиваем файл из 32-разрядного приложения в 64-разрядный, последний должен сбой, потому что мы указываем неверный указатель на DragQueryFile(). Но это не так, потому что DragQueryFile() обрабатывает эти исключения.

Теперь решения:

  • Используйте интерфейс IDropTarget. Это хорошее решение (и рекомендованное Microsoft), если вы не хотите использовать OLE и добавляете около 10 КБ в свой исполняемый файл только для чтения имени файла, которое уже находится в ОЗУ (это не мое дело).

  • Найдите способ получить высокую часть wParam. Как объяснено, это значение является указателем на кучу. Ближайшее значение дается GlobalAlloc(GMEM_MOVEABLE, 0). Обычно он дает значение wParam (или того, которое должно быть у него) +16. Даже если это может быть иногда немного выше, этого достаточно, чтобы получить недостающие 32-разрядные бит wParam. Чтобы покрыть маловероятный случай, когда куча перекрывает границу 4 ГБ, мы можем попытаться добавить или удалить 1 в 32 бита высокого порядка. Обратите внимание, что GlobalFree() по-прежнему требуется. В противном случае вы потребляете несколько байтов (по моим испытаниям по 16) после каждого звонка до GlobalAlloc().

  • Отключить High Entropy ASLR. Для этого требуется Windows 8 или более поздняя версия, и поэтому эта ошибка редко возникает в Windows 7 и ранее. В Windows 7 адреса рандомизированы, но остаются под лимитом 4 ГБ. Тем не менее, вам все равно придется обнулить 32 бита высокого порядка из-за расширения знака. И это решение означает снижение безопасности.

Смежные вопросы