Как "сэмулировать" драг-и-дроп?
От: alexqc Россия
Дата: 28.10.03 10:39
Оценка:
Везде с той или иной степенью подробности описана реализация Drag&Drop на принимающей стороне (WM_DROPFILES c параметром HDROP, DragQueryFile и т.д.), но нигде нет о стороне передающей. Вопрос: как мне програмно сделать d&d, или иначе как сформировать этот самый HDROP. Про ОЛЕшный вариант читал, не подходит.
Живи, Україно, прекрасна і сильна
Re: Как "сэмулировать" драг-и-дроп?
От: Patalog Россия  
Дата: 28.10.03 15:21
Оценка:
Здравствуйте, alexqc, Вы писали:

[]

http://www.rsdn.ru/Forum/Message.aspx?mid=27889&only=1
Автор: Patalog
Дата: 11.02.02
Почетный кавалер ордена Совка.
Re[2]: Как "сэмулировать" драг-и-дроп?
От: alexqc Россия
Дата: 29.10.03 07:08
Оценка:
Здравствуйте, Patalog, Вы писали:

P>http://www.rsdn.ru/Forum/Message.aspx?mid=27889&only=1
Автор: Patalog
Дата: 11.02.02


Объясните плиз, как это дальше использовать?
Живи, Україно, прекрасна і сильна
Re[3]: Как "сэмулировать" драг-и-дроп?
От: Patalog Россия  
Дата: 29.10.03 10:40
Оценка:
Здравствуйте, alexqc, Вы писали:

[]

Например так —
class drop_names
{
    std::vector<TCHAR>    drop_data_;
    size_t                cur_index;
    bool                locked_;
public:
    drop_names() : cur_index(0), locked_(false) { }
    bool add(const TCHAR* name)
    {
        _ASSERTE(!locked_);
        if (locked_)
            return false;

        const size_t name_size = lstrlen(name);
        drop_data_.resize(cur_index + name_size + 1);
        memcpy(&drop_data_[0] + cur_index, name, name_size * sizeof(TCHAR));
        cur_index += name_size;
        drop_data_[cur_index] = _T('\0');
        ++cur_index;

        return true;
    }

    const TCHAR* lock()
    {
        drop_data_.push_back(_T('\0'));
        ++cur_index;
        locked_ = true;

        return &drop_data_[0];
    }

    size_t size() const { return cur_index * sizeof(TCHAR); }
};

const TCHAR* name1 = _T("c:\\temp\\mycoolfile1.txt");
const TCHAR* name2 = _T("c:\\temp\\mycoolfile2.txt");
drop_names names;
names.add(name1);
names.add(name2);
const TCHAR* file_names = names.lock();

DROPFILES drop_struct = { sizeof(drop_struct), { 0, 0 }, 0, 0 };
const size_t drop_size = sizeof(drop_struct) + names.size();

HGLOBAL drop_data = GlobalAlloc(0, drop_size);
HGLOBAL drop_effect = GlobalAlloc(0, sizeof(DWORD));

unsigned char* clip_data = reinterpret_cast<unsigned char*>(drop_data);
unsigned long* clip_effect = reinterpret_cast<unsigned long*>(drop_effect);
        
*clip_effect = DROPEFFECT_COPY; /* or DROPEFFECT_MOVE */
memcpy(clip_data, &drop_struct, sizeof(drop_struct));
memcpy(clip_data + sizeof(drop_struct), file_names, names.size());

BOOL res = OpenClipboard();
_ASSERTE(res);

res = EmptyClipboard();
_ASSERTE(res);
    
HANDLE data = SetClipboardData(CF_HDROP, drop_data);
_ASSERTE(data);
const UINT cf = RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT);
_ASSERTE(cf);
        
data = SetClipboardData(cf, drop_effect);
_ASSERTE(data);

CloseClipboard();

ЗЫ Особо не тестил, так что може где огрехи есть, но думаю идея понятна.
Почетный кавалер ордена Совка.
Re[4]: Как "сэмулировать" драг-и-дроп?
От: alexqc Россия
Дата: 29.10.03 14:04
Оценка:
Здравствуйте, Patalog, Вы писали:

P>Например так —

P>[.......]
P>ЗЫ Особо не тестил, так что може где огрехи есть, но думаю идея понятна.

Как раз не понятна . Вернее, что делаете понятно, не понятно что мне с этим делать дальше. Что мне делать с клипбордом, в котором список файлов лежит?

Попробую свой первоначальный вопрос уточнить — есть некая программа (не моя, и менять я в ней ничего не могу), которая может принимать DnD. Мне нужно ей файлик для открытия подсунуть, это как раз было бы просто через DnD сделать, послав в ее окно WM_DROPFILES. Да вот загвоздка — не могу для WM_DROPFILES HDROP-параметр сформировать. Неужели это прерогатива только системы?
Живи, Україно, прекрасна і сильна
Re[5]: Как "сэмулировать" драг-и-дроп?
От: Patalog Россия  
Дата: 29.10.03 15:04
Оценка:
Здравствуйте, alexqc, Вы писали:

[]

A>Попробую свой первоначальный вопрос уточнить — есть некая программа (не моя, и менять я в ней ничего не могу), которая может принимать DnD. Мне нужно ей файлик для открытия подсунуть, это как раз было бы просто через DnD сделать, послав в ее окно WM_DROPFILES. Да вот загвоздка — не могу для WM_DROPFILES HDROP-параметр сформировать. Неужели это прерогатива только системы?


Так бы и говорил сразу, а то я понял так, что тебе нужно засунуть все это дело в clipboard, дабы потом можно было в проводнике сделать paste...
В твоем случае еще проще, просто делаешь
const TCHAR* name1 = _T("c:\\temp\\mycoolfile1.txt");
const TCHAR* name2 = _T("c:\\temp\\mycoolfile2.txt");
drop_names names;
names.add(name1);
names.add(name2);
const TCHAR* file_names = names.lock();

DROPFILES drop_struct = { sizeof(drop_struct), { 0, 0 }, 0, 0 };
const size_t drop_size = sizeof(drop_struct) + names.size();

HGLOBAL drop_data = GlobalAlloc(0, drop_size);
unsigned char* clip_data = reinterpret_cast<unsigned char*>(drop_data);
memcpy(clip_data, &drop_struct, sizeof(drop_struct));
memcpy(clip_data + sizeof(drop_struct), file_names, names.size());

SendMessage(somebodycoolappwindow, WM_DROPFILES, (WPARAM)drop_data, 0);

впрочем, лишние танцы вокруг HGLOBAL можно тоже убрать, но это не суть важно.

ЗЫ А вообще в TFM'е писано

DROPFILES Structure
--------------------------------------------------------------------------------
Defines the CF_HDROP clipboard format. The data that follows is a double null-terminated list of file names.

так что мог бы и сам догадаться

ЗЗЫ Хотя ежели ты сообщение шлешь в другое приложение, тогда скорее всего в clipboard таки пихать придется, а в SendMessage отдавать то, что вернет SetClipboardData.
Почетный кавалер ордена Совка.
Re[6]: Как "сэмулировать" драг-и-дроп?
От: alexqc Россия
Дата: 29.10.03 15:59
Оценка:
Здравствуйте, Patalog, Вы писали:

P>Здравствуйте, alexqc, Вы писали:


P>[]


A>>Попробую свой первоначальный вопрос уточнить — есть некая программа (не моя, и менять я в ней ничего не могу), которая может принимать DnD. Мне нужно ей файлик для открытия подсунуть, это как раз было бы просто через DnD сделать, послав в ее окно WM_DROPFILES. Да вот загвоздка — не могу для WM_DROPFILES HDROP-параметр сформировать. Неужели это прерогатива только системы?


P>Так бы и говорил сразу, а то я понял так, что тебе нужно засунуть все это дело в clipboard, дабы потом можно было в проводнике сделать paste...

Про клипбоард я вообще ни слова не говорил.

P>В твоем случае еще проще, просто делаешь

P>
P>const TCHAR* name1 = _T("c:\\temp\\mycoolfile1.txt");
P>const TCHAR* name2 = _T("c:\\temp\\mycoolfile2.txt");
P>drop_names names;
P>names.add(name1);
P>names.add(name2);
P>const TCHAR* file_names = names.lock();

P>DROPFILES drop_struct = { sizeof(drop_struct), { 0, 0 }, 0, 0 };
P>const size_t drop_size = sizeof(drop_struct) + names.size();

P>HGLOBAL drop_data = GlobalAlloc(0, drop_size);
P>unsigned char* clip_data = reinterpret_cast<unsigned char*>(drop_data);
P>memcpy(clip_data, &drop_struct, sizeof(drop_struct));
P>memcpy(clip_data + sizeof(drop_struct), file_names, names.size());

P>SendMessage(somebodycoolappwindow, WM_DROPFILES, (WPARAM)drop_data, 0);
P>

P>впрочем, лишние танцы вокруг HGLOBAL можно тоже убрать, но это не суть важно.
P>ЗЫ А вообще в TFM'е писано
P>

P>DROPFILES Structure
P>--------------------------------------------------------------------------------
P>Defines the CF_HDROP clipboard format. The data that follows is a double null-terminated list of file names.

P>так что мог бы и сам догадаться

Я для проверки делал

    int size=strlen(lpDroppedText)+2+sizeof(DROPFILES);
    HGLOBAL drop_data = GlobalAlloc(GMEM_ZEROINIT | GMEM_MOVEABLE | GMEM_DDESHARE, size);

    struct DROP_DATA{
    DROPFILES df;
    char names[1];
    } *hal=(DROP_DATA*)GlobalLock(drop_data);

    memset(hal,0,size);
    hal->df.fNC=sizeof(DROPFILES);
    strcpy(hal->names,lpDroppedText);
    GlobalUnlock(drop_data);

    int ret=DragQueryFile(HDROP (drop_data)
        ,UINT(0xFFFFFFFF), NULL,0);

    GlobalFree(drop_data);


получаю ret=0, т.е. ошибку.

Точно ли drop_data в этом случае тот самый HDROP? (об этом как раз в TFM нет ни слова )

P>ЗЗЫ Хотя ежели ты сообщение шлешь в другое приложение, тогда скорее всего в clipboard таки пихать придется, а в SendMessage отдавать то, что вернет SetClipboardData.


Что-то извратом пахнет, да и clipboard портить...
Живи, Україно, прекрасна і сильна
Re[7]: Как "сэмулировать" драг-и-дроп?
От: Patalog Россия  
Дата: 30.10.03 10:57
Оценка:
Здравствуйте, alexqc, Вы писали:

[]

P>>Так бы и говорил сразу, а то я понял так, что тебе нужно засунуть все это дело в clipboard, дабы потом можно было в проводнике сделать paste...


A>Про клипбоард я вообще ни слова не говорил.


Ну это я почему-то так понял

[]

A>Я для проверки делал

A>    int size=strlen(lpDroppedText)+2+sizeof(DROPFILES);
A>    HGLOBAL drop_data = GlobalAlloc(GMEM_ZEROINIT | GMEM_MOVEABLE | GMEM_DDESHARE, size);

A>    struct DROP_DATA{
A>    DROPFILES df;
A>    char names[1];
A>    } *hal=(DROP_DATA*)GlobalLock(drop_data);

A>    memset(hal,0,size); // не нужно, у тебя же GMEM_ZEROINIT
    hal->>df.fNC=sizeof(DROPFILES);
A>    strcpy(hal->names,lpDroppedText);
A>    GlobalUnlock(drop_data); // Думается это надо делать после DragQueryFile

A>    int ret=DragQueryFile(HDROP (drop_data)
A>        ,UINT(0xFFFFFFFF), NULL,0);

A>    GlobalFree(drop_data);

A>получаю ret=0, т.е. ошибку.

A>Точно ли drop_data в этом случае тот самый HDROP? (об этом как раз в TFM нет ни слова )


Дык я же приводил цитату из TFM. К тому же реальность данная мне в ощущениях это подтверждает.

P>>ЗЗЫ Хотя ежели ты сообщение шлешь в другое приложение, тогда скорее всего в clipboard таки пихать придется, а в SendMessage отдавать то, что вернет SetClipboardData.


A>Что-то извратом пахнет, да и clipboard портить...


Ну clipboard то ты не испортишь, бо таким образом copy\paste в explorer (или еще куда) проще всего сделать.
Правда для WM_DROPFILES это не работает, собст. как я и думал из-за границ процесса.

Вобщем лови. Писано в MFC'ях, но думается под голое API переделать труда не составит. Запускаешь 2 экземпляра приложения и тащиш мышой из одного в другое.
class drop_names
{
    std::vector<TCHAR>    drop_data_;
    size_t                cur_index;
    bool                locked_;
public:
    drop_names() : cur_index(0), locked_(false) { }
    bool add(const TCHAR* name)
    {
        _ASSERTE(!locked_);
        if (locked_)
            return false;

        const size_t name_size = lstrlen(name);
        drop_data_.resize(cur_index + name_size + 1);
        memcpy(&drop_data_[0] + cur_index, name, name_size * sizeof(TCHAR));
        cur_index += name_size;
        drop_data_[cur_index] = _T('\0');
        ++cur_index;

        return true;
    }

    const TCHAR* lock()
    {
        _ASSERTE(!locked_);
        if (!locked_)
        {
            drop_data_.push_back(_T('\0'));
            ++cur_index;
            locked_ = true;
        }

        return &drop_data_[0];
    }

    size_t size() const { return cur_index * sizeof(TCHAR); }
};

void CNewView::OnLButtonDown(UINT nFlags, CPoint point)
{
    SetCapture();
}

void CNewView::OnLButtonUp(UINT nFlags, CPoint point)
{
    drop_names d_names;
    const TCHAR* name1 = "c:\\temp\\mycoolfile1.txt";
    const TCHAR* name2 = "c:\\temp\\mycoolfile2.txt";
    const TCHAR* name3 = "c:\\temp\\mycoolfile3.txt";
    
    d_names.add(name1);
    d_names.add(name2);
    d_names.add(name3);
    const TCHAR* names = d_names_.lock();
    const size_t names_size = d_names_.size();

    DROPFILES drop_struct = { sizeof(drop_struct), { 0, 0 }, 0, 0 };
    const size_t drop_size = sizeof(drop_struct) + names_size;

    std::vector<unsigned char> drop_data(drop_size);
    memcpy(&drop_data[0], &drop_struct, sizeof(drop_struct));
    memcpy(&drop_data[0] + sizeof(drop_struct), names, names_size);

    
    ClientToScreen(&point);
    CWnd* found_wnd = WindowFromPoint(point);
    _ASSERTE(found_wnd);

    DWORD process_id = 0;
    GetWindowThreadProcessId(found_wnd->GetSafeHwnd(), &process_id);
    HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, process_id);
    _ASSERTE(process);

    LPVOID address = VirtualAllocEx(process, NULL, drop_size, MEM_COMMIT, PAGE_READWRITE);
    _ASSERTE(address);

    SIZE_T bytes_written = 0;
    BOOL res = WriteProcessMemory(process, address, &drop_data[0], drop_size, &bytes_written);
    _ASSERTE(res && bytes_written == drop_size);

    found_wnd->SendMessage(WM_DROPFILES, (WPARAM)address, 0);

    res = VirtualFreeEx(process, address, 0, MEM_RELEASE);
    _ASSERTE(res);

    ReleaseCapture();
}

void CNewView::OnDropFiles(HDROP drop_info)
{
    CClientDC dc(this);

    int item_count = DragQueryFile(drop_info, 0xFFFFFFFF, NULL, 0);
    CString text;
    text.Format("Dropped %d files", item_count);
    dc.TextOut(100, 100, text);

    for (int i = 0; i < item_count; ++i)
    {
        TCHAR buffer[MAX_PATH] = { 0 };
        int buffer_size = DragQueryFile(drop_info, i, NULL, 0);
        DragQueryFile(drop_info, i, buffer, MAX_PATH);

        dc.TextOut(100, 120 + 20 * i, buffer);
    }
}


ЗЫ Ежели надо чтоб работало и под w9x, то придеться вместо VirtualAllocEx\WriteProcessMemory использовать внедрение dll. Подробнее об этом полно инфы тут на сайте, ну иеще конечно у Рихтера.
Почетный кавалер ордена Совка.
Re[8]: Как "сэмулировать" драг-и-дроп?
От: alexqc Россия
Дата: 30.10.03 11:52
Оценка:
Здравствуйте, Patalog, Вы писали:

Я понял в чем дело. Досадная описка, незамеченная ни мной, ни Вами:

A>> hal->df.fNC=sizeof(DROPFILES);

вместо hal->df.pFiles=sizeof(DROPFILES);

А с остальным — memset лишнее, согласен, а вот GlobalUnlock надо наверно все же до DragQueryFile делать.
Потом, после DragQueryFile я делаю GlobalFree.

И такой вопрос — DragFinish что освободит? Я к тому, что если приложение куда я WM_DROPFILES пошлю сделает DragFinish, мне уже GlobalFree не надо будет делать?

Ну, ладно, счас буду проверять.
Живи, Україно, прекрасна і сильна
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.