Открыть и выделить файлы в проводнике

Тема в разделе "WASM.BEGINNERS", создана пользователем ActioN, 29 апр 2006.

  1. ActioN

    ActioN New Member

    Публикаций:
    0
    Регистрация:
    1 апр 2005
    Сообщения:
    160
    Адрес:
    Ukraine
    Есть список имен файлов. Есть путь к ним. Открываю проводник. Как можно выделить нужные мне файлы в этом открытом окне проводника? Тут говорят, что у Рихтера про это написано, нужно использовать внедрение dll и что гемороя много. Может есть другой выход?
     
  2. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    Файлы в проводнике отображаются в стандартном контроле SysListView32. Сообщения для поиска/выделения итемов в стандартном SysListView32 описаны в msdn.
     
  3. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    ActioN

    Другого выхода нет, SysListView32, в отличие от некоторых других контролов, доступен только из процесса, в котором он находится. Поэтому нужно подгружать код, который будет с ним работать, в процесс, владеющий им.
     
  4. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    cresta



    Что за новость? Дочерние контролы syslistview32 посылают сообщения родительскому напрямую, в обход очереди, но это единственная "изюминка" данного контрола, AFAIK. Только что попробовал послать пару сообщений этому контролу в проводнике - получилось без проблем.
     
  5. ActioN

    ActioN New Member

    Публикаций:
    0
    Регистрация:
    1 апр 2005
    Сообщения:
    160
    Адрес:
    Ukraine
    Quantum, а можно подробнее, какие сообщения нужно отсылать. Хендл ListView'a я нашел, а вот с сообщениями проблема. Как я понял, нужно сначала использовать LVM_FINDITEM для получения индекса. Для этого нужно заполнить структуру _LV_FINDINFO:
    Код (Text):
    1. flags = LVFI_STRING;
    2. psz   = "ToSort.ac\0";


    Другие параметры заполнять не нужно, правильно?

    Потом использовать сообщение LVM_SETITEM, заполнив структуру LV_ITEM.

    Но у меня проблемы с LVM_FINDITEM. Если в проводнике ничего не выделено, то сообщение возвращает -1 (элемент не найден, хотя он точно существует). А если выделить какой-нибудь файл, то проводник вобще вылетает с ошибкой и закрывается. А то бывало и експлорер перезапускался. Что я нетак делаю?



    P.S. еще как альтернативный вариант нашел сообщение WM_KEYDOWN. Если в цикле отослать в ListView буквы имени файла, то он должен выделиться. Но наверное я что-то с wParam и lParam нетак делаю. Item не выделяется. Ну это вариант на крайний случай. Помогите разобраться с 1-м (нормальным) вариантом.
     
  6. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    ActioN

    Поделитесь своим тестовым кодом. Очевидных ошибок пока не заметно.
     
  7. ActioN

    ActioN New Member

    Публикаций:
    0
    Регистрация:
    1 апр 2005
    Сообщения:
    160
    Адрес:
    Ukraine
    Quantum, ну вобще-то я делаю на C++ Builder :) (Это ничего, что я на форуме по ассемблеру спрашиваю про задачу на C++ Builder? Думаю, что ничего, алгоритм все-таки один и тот же). Щас выложу код на C++ Builder, а в оффлайне постараюсь переделать под MASM32.


    Код (Text):
    1.  
    2. ....
    3. typedef struct _LV_FINDINFO
    4. {
    5.     UINT    flags;
    6.     LPCTSTR psz;
    7.     LPARAM  lParam;
    8.     POINT   pt;
    9.     UINT    vkDirection;
    10. } LV_FINDINFO;
    11.  
    12. LV_FINDINFO *x = new LV_FINDINFO;
    13. x->flags = LVFI_STRING;
    14. x->psz   = "ToSort.ac\0";
    15.  
    16.  
    17. int index = SendMessage(hwndChild,LVM_FINDITEM,-1,LPARAM(x));
    18. if (index != -1)
    19. {
    20.     ShowMessage(index);
    21. }
    22. else
    23.     ShowMessage("NOT FOUND");
     
  8. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    ActioN

    Такие сообщения как LVM_SETBKCOLOR работают без проблем. Ошибка в LVM_FINDITEM связана с тем, что указатель на структуру LV_FINDINFO не принадлежит адресному пространству процесса, в котором сидит обработчик сообщений листвью.
     
  9. ActioN

    ActioN New Member

    Публикаций:
    0
    Регистрация:
    1 апр 2005
    Сообщения:
    160
    Адрес:
    Ukraine
    Quantum, вот переделал, но почему-то всегда index = -1:
    Код (Text):
    1. LV_FINDINFO x;
    2. x.flags = LVFI_STRING;
    3. x.psz   = "ToSort.ac\0";
    4.  
    5. int index = SendMessage(hwndChild,LVM_FINDITEM,-1,(long)&x);
    6. if (index != -1)
    7.     ShowMessage(index);
    8. else
    9.     ShowMessage("NOT FOUND");


    Вроде бы все правильно, да?
     
  10. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    Такой вариант у меня работает:
    Код (Text):
    1. #include <commctrl.h>
    2.  
    3. char file_name[] = "имя файла.расширение";
    4.  
    5. void main(){
    6. int pid,i;
    7. HANDLE hProcess;
    8. LV_FINDINFO* plvfi;
    9. char msg[32];
    10. HWND hLV = (HWND)0x000D04D4; // это хендл листвью в моём проводнике
    11.     GetWindowThreadProcessId(hLV,&pid);
    12.     hProcess = OpenProcess(PROCESS_ALL_ACCESS,0,pid);
    13.     if(hProcess){
    14.         plvfi = VirtualAllocEx(hProcess,0,sizeof(LV_FINDINFO) + strlen(file_name) + 1,MEM_COMMIT,PAGE_READWRITE);
    15.         if(plvfi){
    16.             i = WriteProcessMemory(hProcess,plvfi + 1,file_name,strlen(file_name) + 1,0);
    17.             if(i){
    18.                 i = LVFI_STRING;
    19.                 WriteProcessMemory(hProcess,(void*)&(plvfi->flags),&i,4,0);
    20.                 i = (int)(plvfi + 1);
    21.                 WriteProcessMemory(hProcess,(void*)&(plvfi->psz),&i,4,0);
    22.                 i = SendMessage(hLV,LVM_FINDITEM,-1,(long)plvfi);
    23.                 if(i != -1){
    24.                     wsprintf(msg,"item found @ %d",i);
    25.                     MessageBox(0,msg,"Success",MB_ICONINFORMATION);
    26.                 }else
    27.                     MessageBox(0,"SendMessage",0,MB_ICONSTOP);
    28.             }else
    29.                 MessageBox(0,"WriteProcessMemory",0,MB_ICONSTOP);
    30.             VirtualFreeEx(hProcess,(void*)plvfi,0,MEM_DECOMMIT);
    31.         }else
    32.             MessageBox(0,"VirtualAllocEx",0,MB_ICONSTOP);
    33.     }else
    34.         MessageBox(0,"OpenProcess",0,MB_ICONSTOP);
    35.     ExitProcess(0);
    36. }


    Все 3 вызова WriteProcessMemory можно соптимизировать в один. Потом для LVM_SETITEM нужно сделать тоже самое.



    Как вариант можно использовать CreateRemoteThread или инжектировать DLL на старте.



    Посылать WM_KEYDOWN может оказаться не так уж и плохо :)
     
  11. ActioN

    ActioN New Member

    Публикаций:
    0
    Регистрация:
    1 апр 2005
    Сообщения:
    160
    Адрес:
    Ukraine
    %)Ну и код. Вот нашел точно такую же проблему:

    ---------------------------

    I use Listview and want to use FindItem function to update the data, but I always get -1 from Finditem.



    extern char *tid;



    LVFINDINFO lvf;

    CListCtrl & clc = GetListCtrl();

    lvf.flags = LVFI_PARTIAL|LVFI_STRING;

    lvf.psz = (LPCTSTR) tid;

    int nIndex = clc.FindItem(&lvf, -1 );

    ---------------------------

    Пробовал у себя сделать что-то подобное, все равно -1.

    А при использовании WM_KEYDOWN что нужно писать в lParam и wParam, если нужно, к примеру, отослать букву 'A'?

    Так вроде бы правильно, только все равно у меня не работает почему-то:

    SendMessage(hwndChild,WM_KEYDOWN,VkKeyScan('A'),0);
     
  12. ActioN

    ActioN New Member

    Публикаций:
    0
    Регистрация:
    1 апр 2005
    Сообщения:
    160
    Адрес:
    Ukraine
    Работает!!! Только не WM_KEYDOWN а WM_CHAR. Между ними нету большой разницы?
     
  13. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    ActioN



    В сообщении "Дата: Апр 30, 2006 00:35:19" я уже объяснил почему такой код не может работать.





    Код принципиально основан на Вашем примере. Просто структура LV_FINDINFO помещается в адресное пространство хозяина листвью, чтобы адрес этой структуры в контексте данного адресного пространства указывал на структуру. Логично, согласитесь. Просто реализовывается это немного громоздко.





    Есть, конечно, большая разница, но раз работает, то какая уже разница? :)
     
  14. ActioN

    ActioN New Member

    Публикаций:
    0
    Регистрация:
    1 апр 2005
    Сообщения:
    160
    Адрес:
    Ukraine
    А еще такой вопрос несовсем в тему, как можно узнать хендл запущеного окна ListView. ShellExecute возвращает HINSTANCE.
     
  15. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    Код (Text):
    1. hExplorerWnd = FindWindow("ExploreWClass",0);
    2. hListView = GetWindow(
    3.                 GetWindow(
    4.                    GetWindow(
    5.                       GetWindow(
    6.                          GetWindow(
    7.                             GetWindow(hExplorerWnd,GW_CHILD),
    8.                          GW_HWNDNEXT),
    9.                       GW_HWNDNEXT),
    10.                    GW_HWNDNEXT),
    11.                 GW_HWNDNEXT),
    12.              GW_CHILD);


    Но не факт, что в разных версиях проводника иерархия неизменна. Надёжнее в плане совместимости использовать EnumWindows и рекурсивно проверять все окна пока не будет найден нужный хендл.
     
  16. ActioN

    ActioN New Member

    Публикаций:
    0
    Регистрация:
    1 апр 2005
    Сообщения:
    160
    Адрес:
    Ukraine
    Quantum, а разве нету такой функции, которая бы после запуска возвращала хендл запущенного окна?
     
  17. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    А чем это поможет? ListView, которое отображает файлы в проводнике далеко не главное окно, которое косвенно можно получить по HINSTANCE. Посмотрите внимательнее мой предыдущий пост. Главное окно, кстати, возвращает FindWindow, но потом нужно основательно покопаться в его дочерних окнах, чтобы выйти на ListView.
     
  18. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257


    Пробовал получать итем - и получал ACCESS_DENIED.

    Да и от других слышал не раз такое, так что это не новость.

    К тому же, если не нужно подгружаться в процесс владеющий листвью, то к чему в вашем коде

    OpenProcess

    VirtualAllocEx

    WriteProcessMemory
     
  19. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    cresta



    ListView, как и любое другое окно, доступен из любого процесса.





    Повторение - мать учения. Итак, имеем один процесс (P1), который посылает сообщение окну ListView, обработчик которого сидит в другом процессе (P2). Сообщения обычно посылаются через функцию SendMessage, которая позволяет передать получателю максимум 64 бит данных прямо через стек: параметры wParam и lParam. Попробуйте послать окну ListView такие сообщения как LVM_ARRANGE, LVM_DELETEALLITEMS, LVM_DELETECOLUMN, LVM_DELETEITEM, LVM_EDITLABEL, LVM_ENSUREVISIBLE, LVM_GETBKCOLOR / LVM_SETBKCOLOR, ... и Вы увидите, что оно их обработает. Тем не менее, параметры таких сообщений как LVM_FINDITEM и LVM_SETITEMSTATE не умещаются в 64 бита. Поэтому они помещаются в структуру и одним из параметров SendMessage передаётся указатель, т.е. адрес этой структуры. Где хранится на самом деле структура? - В адресном пространстве P1. SendMessage копирует из стека P1 в стек P2 только uMsg, lParam и wParam, т.е. только указатель на структуру LV_FINDINFO, но не саму структуру и тем более не ту ASCII строку, на которую указывает поле psz. Далее, в адресном пространстве P2 обрабатывается сообщение LVM_FINDITEM. Обработчик пытается воспользоваться параметром lParam как указателем на структуру LVM_FINDITEM, но уже в СВОЁМ адресном пространстве и, естественно, может найти по данному адресу всё что угодно, но не оригинальную структуру. Чтобы этого избежать, нужно поместить структуру LV_FINDINFO и ту строку, на которую указывает psz, в адресное пространство P2. Самая банальная реализация - та, что я привёл в одном из предыдущих постов. Конечно, можно сделать проще. Можно подгрузить в проводник свою DLL и она окажется в адресном пространстве P2. Можно обойтись сообщениями, которые не требуют указателей (ActioN посылает окну WM_CHAR).
     
  20. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257




    ну дык а я о чем говорю :)

    Я наверное не совсем конкретно выразился. не суть важно, что писать, но открывать процесс и лезть в него обязательно придётся. А чтобы сделать это без лишнего геморроя, самое простое решение - писать всё скопом, и код и данные, ибо механизм подгрузки dll отработан от и до.