html текст
All interests
  • All interests
  • Design
  • Food
  • Gadgets
  • Humor
  • News
  • Photo
  • Travel
  • Video
Click to see the next recommended page
Like it
Don't like
Add to Favorites

Как в приложении получить список файлов, переданных из проводника посредством Drag-and-Drop перевод

О чем данная статья:


Множество программ позволяют пользователю открывать файлы посредством перетаскивания их из проводника в окно приложения. Как правило это более удобно для пользователя, в отличии от стандартной модели открытия посредством выбора меню «Файл -> Открыть» или нажатия соответствующей кнопки на панели инструментов, так как в данном случае пользователь пропускает этап работы с диалоговым окном.
Добавление данной возможности в ваше приложение сделает его более «профессиональным».

Как это работает:


Есть два основных пути добавления данного функционала в Windows приложение.
Первый способ заключается в использовании стандартных Windows Drag-and-Drop API функций и обработке сопутствующих оконных сообщений.
Вторым методом является использование технологии OLE (COM), который предоставляет расширенные методы передачи данных между приложениями.
В данной статье будет рассмотрен первый и самый простой способ.

Краткий обзор:


По умолчанию, поддержка перетаскивания и приема файлов в Windows приложениях отключена.
Для включения данной возможности мы должны сказать Windows о том, что мы хотим получать уведомления о перетаскивании, а так-же указать окно, отвечающее за обработку данных уведомлений.
Данное окно должно уметь обрабатывать сообщение WM_DROPFILES, приходящее ему в тот момент, когда пользователь завершил операцию перетаскивания.
Это сообщение предоставляет нам возможность реализовать обработчик события Drop, из которого мы может вызвать различные API функции, для получения информации о том, что именно нам было передано.
По завершении обработки данного сообщения, хорошим тоном будет уведомить Windows о том, что мы обработали данную операцию и больше не хотим получать уведомления о ней.



Проблемы с безопасностью в Windows Vista (и выше)
По соображениям безопасности в Windows Vista возможность перетаскивания между окнами, имеющими различные параметры безопасности, может быть запрещена.
Как следствие получение сообщения WM_DROPFILES вашим приложением может быть заблокирована.

Рассмотрим шаг за шагом, что нужно сделать в Delphi приложении для реализации приема файлов посредством Drag-and-Drop:

1. Для получения доступа к необходимым API функциям необходимо подключить модуль ShellAPI:

uses 
  ShellAPI;

2. Вызовите функцию DragAcceptFiles, передав первым параметром хэндл окна, которое будет отвечать за обработку сообщения WM_DROPFILES, а вторым значение True, указывающее что данное окно готово получать уведомления.
К примеру данным окном будет главная форма нашего приложения:

DragAcceptFiles(Form1.Handle, True);

3. Обработайте сообщение WM_DROPFILES. В Delphi мы можем объявить обработчик сообщения в классе главной формы приложения:

procedure WMDropFiles(var Msg: TWMDropFiles); message WM_DROPFILES;

4. При получении сообщения WM_DROPFILES используйте функции DragQueryXXX для получения информации о переданных файлах — смотрите пояснения ниже.

5. Уведомьте Windows об окончании обработки данного сообщения.
Это делается посредством вызова функции DragFinish, в которую параметром передается дескриптор HDROP, полученный при обработке сообщения WM_DROPFILES.
Вызов данной функции освободит ресурсы, используемые для хранения данных об операции Drag-and-Drop.

DragFinish(DropH);

6. Когда будет происходить закрытие приложения, необходимо повторно вызвать функцию DragAcceptFiles, но в этот раз вторым параметром необходимо передать False, что уведомит Windows о том, что окно более не обрабатывает сообщение WM_DROPFILES.

DragAcceptFiles(Form1.Handle, False);

Получение информации о перенесенных файлах.


Мы будем использовать две функции для получения информации о перенесенных файлов — DragQueryFile и DragQueryPoint.
Обе данных функции требуют параметром дескриптор HDROP, полученный при обработке сообщения WM_DROPFILES.

Приветствуем мастера на все руки — DragQueryFile.

Как и большинство функций Windows API, DragQueryFile может реализовывать несколько вариантов поведения, в зависимости от переданных ему параметров.
Такое поведение иногда приводит к тому, что программисту достаточно сложно запомнить правильный вариант использования данной функции.
Давайте рассмотрим её параметры (с учетом синтаксиса Delphi):

  1. DropHandle: HDROP – дескриптор операции, переданный сообщением WM_DROPFILES.
  2. Index: Integer – индекс файла, при запросе к списку переданных файлов.
  3. FileName: PChar – указатель на буфер, в который будет передано имя файла.
  4. BufSize: Integer – размер буфера FileName.

Вызов данной функции дает нам возможность получения следующих данных:

  • Получение количества переданных файлов. Мы можем получить данную информацию передав $FFFFFFFF или DWORD(-1) в параметре Index, при этом параметр FileName должен быть «nil», а параметр BufSize, отвечающий за размер буфера FileName, должен быть равен нулю. Возвращаемое функцией значение будет равно количеству переданных файлов. Достаточно очевидно.
  • Получение размера области памяти, требуемой для хранения имени файла на основании параметра Index (отсчет идет от нуля, т.е. первый файл индексируется в списке нулем). Для этого необходимо указать необходимый файл в списке, посредством параметра Index, оставив параметры FileName и BufSize такими-же, как и в предыдущем пункте. В результате функция возвратит количество байт, требуемых для хранения имени файла.
  • Получение имени файла на основе переданного индекса. Для этого необходимо, помимо указания индекса файла через параметр Index, подготовить буфер (чуть больше требуемого размера), который сможет принять данные о пути к файлу, плюс терминирующий ноль.

Пока что все это выглядит достаточно не понятно, но в конце статьи мы рассмотрим все эти этапы в виде самостоятельного кода, реализованного в виде класса.

DragQueryPoint

Данная функция в принципе является альтернативной DragQueryFile, вызвав ее, мы можем узнать координаты, по которым завершилось перетаскивание файлов.

Рассмотрим ее параметры:
  • DropHandle: HDROP – дескриптор операции, переданный сообщением WM_DROPFILES.
  • var Point: TPoint – структура TPoint, в которой возвращаются данные о точке завершения операции Drag-and-Drop. Если операция завершилась в клиентской области окна, то она вернет ненулевое значение, в противном случае функция вернет ноль.

Рассмотрим все целиком.


Создайте новый проект.
У нас есть «рыба» Delphi приложения и нам необходимо добавить в нее возможность получения информации о переданных файлах посредством Drag-and-Drop.
В нашей форме мы зарегистрируем обработчик интересующего нас события:

procedure TForm1.FormCreate(Sender: TObject);
begin
  // ... здесь некий код
  DragAcceptFiles(Self.Handle, True);
  // ... здесь некий код
end;

Данным кодом мы подписались на прием сообщения WM_DROPFILES.
Вот так выглядит обработчик данного сообщения:

procedure TForm1.WMDropFiles(var Msg: TWMDropFiles);
var
  DropH: HDROP;               // дескриптор операции перетаскивания
  DroppedFileCount: Integer;  // количество переданных файлов
  FileNameLength: Integer;    // длина имени файла
  FileName: string;           // буфер, принимающий имя файла
  I: Integer;                 // итератор для прохода по списку
  DropPoint: TPoint;          // структура с координатами операции Drop
begin
  inherited;
  // Сохраняем дескриптор
  DropH := Msg.Drop;
  try
    // Получаем количество переданных файлов
    DroppedFileCount := DragQueryFile(DropH, $FFFFFFFF, nil, 0);
    // Получаем имя каждого файла и обрабатываем его
    for I := 0 to Pred(DroppedFileCount) do
    begin
      // получаем размер буфера
      FileNameLength := DragQueryFile(DropH, I, nil, 0);
      // создаем буфер, который может принять в себя строку с именем файла
      // (Delphi добавляет терминирующий ноль автоматически в конец строки)
      SetLength(FileName, FileNameLength);
      // получаем имя файла
      DragQueryFile(DropH, I, PChar(FileName), FileNameLength + 1);
      // что-то делаем с данным именем (все зависит от вашей фантазии)
      // ... код обработки пишем здесь
    end;
    // Опционально: получаем координаты, по которым произошла операция Drop
    DragQueryPoint(DropH, DropPoint);
    // ... что-то делаем с данными координатами здесь
  finally
    // Финализация - разрушаем дескриптор
    // не используйте DropH после выполнения данного кода...
    DragFinish(DropH);
  end;
  // Говорим о том, что сообщение обработано
  Msg.Result := 0;
end;

В самом начале мы запоминаем дескриптор операции Drop в локальной переменной.
Это делается просто для удобства, в дальнейшем мы будем использовать данное значение для вызовов функции DragQueryFile (как впрочем и для остальных DragХХХ), первым вызовом которой мы получим количество файлов в списке.
Следующий наш шаг заключается в получении имени каждого файла в цикле по его индексу (файлы в списке индексируются от нуля — не забывайте это).
Для каждого файла необходимо изначально узнать размер буфера, получить который можно при помощи второго способа использования DragQueryFile, который был описан выше, а затем выделить память под строку, которая будет хранить в себе путь к файлу.
В заключение требуется прочитать путь к файлу в выделенный буфер, посредством вызова DragQueryFile третьим способом.

После чтения всей информации об именах файлов, мы можем получить координаты точки, в которую пользователь перетащил файлы.
После того как мы выполнили все операции, мы вызовем функцию DragFinish, для освобождения дескриптора.
Обратите внимание на то, что мы должны выполнить это действие в любом случае, даже при поднятии исключения.
Ну и в самом конце мы укажем результат обработки сообщения, выставив значение 0 в структуре Msg.Result, указав, таким образом, что мы успешно обработали данное сообщение.

При завершении работы приложения мы отключим главное окно от получения сообщений WM_DROPFILES.

procedure TForm1.FormDestroy(Sender: TObject);
begin
  // ... здесь некий код
  DragAcceptFiles(Self.Handle, False);
  // ... здесь некий код
end;

Реализуем это в виде класса (The Delphi Way)


Если вы согласны с тем, что использование API напрямую, непосредственно в коде базового приложения, вносит некоторый беспорядок, то думаю вы согласитесь и с тем, что гораздо удобнее вынести данный функционал во вне, в виде некоего вспомогательного класса.
Как пример, вот вам небольшой класс, который умеет обрабатывать сообщение WM_DROPFILES.
Он прячет в себе достаточно много кода, который мы должны были-бы реализовать самостоятельно без его использования.
Правда есть нюанс — мы все-же должны уведомлять Windows о наличии окна, обрабатывающего сообщение WM_DROPFILES.

Декларация данного класса выглядит так:

type
  TFileCatcher = class(TObject)
  private
    fDropHandle: HDROP;
    function GetFile(Idx: Integer): string;
    function GetFileCount: Integer;
    function GetPoint: TPoint;
  public
    constructor Create(DropHandle: HDROP);
    destructor Destroy; override;
    property FileCount: Integer read GetFileCount;
    property Files[Idx: Integer]: string read GetFile;
    property DropPoint: TPoint read GetPoint;
  end;

… ну и его реализация:

constructor TFileCatcher.Create(DropHandle: HDROP);
begin
  inherited Create;
  fDropHandle := DropHandle;
end;

destructor TFileCatcher.Destroy;
begin
  DragFinish(fDropHandle);
  inherited;
end;

function TFileCatcher.GetFile(Idx: Integer): string;
var
  FileNameLength: Integer;
begin
  FileNameLength := DragQueryFile(fDropHandle, Idx, nil, 0);
  SetLength(Result, FileNameLength);
  DragQueryFile(fDropHandle, Idx, PChar(Result), FileNameLength + 1);
end;

function TFileCatcher.GetFileCount: Integer;
begin
  Result := DragQueryFile(fDropHandle, $FFFFFFFF, nil, 0);
end;

function TFileCatcher.GetPoint: TPoint;
begin
  DragQueryPoint(fDropHandle, Result);
end;

В принципе здесь нет ничего такого, что не было рассказано ранее. Весь код вы уже видели.
Конструктор принимает дескриптор HDROP, деструктор финализирует его вызовом DragFinish.
Список файлов и их количество представлены свойствами класса.
Все методы класса по сути являются оберткой над API DragQueryFile и DragQueryPoint.

Давайте перепишем обработчик сообщения WM_DROPFILES с учетом использования TFileCatcher:

procedure TForm1.WMDropFiles(var Msg: TWMDropFiles);
var
  I: Integer;                 // итератор для прохода по списку
  DropPoint: TPoint;          // структура с координатами операции Drop
  Catcher: TFileCatcher;      // экземпляр класса TFileCatcher
begin
  inherited;
  Catcher := TFileCatcher.Create(Msg.Drop);
  try
    for I := 0 to Pred(Catcher.FileCount) do
    begin
      // ... код обработки пишем здесь
    end;
    DropPoint := Catcher.DropPoint;
    // ... что-то делаем с данными координатами здесь
  finally
    Catcher.Free;
  end;
  Msg.Result := 0;
end;

Планы на будущее


Класс TFileCatcher может быть расширен для скрытия всех API используемых при операциях Drag-and-Drop. Правда для этого потребуется доступ к окну формы, для осуществления перехвата сообщения WM_DROPFILES.
Один из вариантов такой реализации заключается в сабклассинге окна формы. Правда это выходит за рамки данной статьи. Тем не менее, если вы хотите получить больше информации, посмотрите мой набор компонент Drop Files.

Демо приложение


Исходный код демоприложения базируется на коде, опубликованном в данной статье. Код был протестирован в версиях Delphi с четвертой по седьмую.

This source code is merely a proof of concept and is intended only to illustrate this article. It is not designed for use in its current form in finished applications. The code is provided on an «AS IS» basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. The code may be used in any way providing it is not sold or passed off as the work of another person. If you agree to all this then please download the code using the following link.


Скачать демонстрационный пример
Читать дальше
Twitter
Одноклассники
Мой Мир

материал с habrahabr.ru

2

      Add

      You can create thematic collections and keep, for instance, all recipes in one place so you will never lose them.

      No images found
      Previous Next 0 / 0
      500
      • Advertisement
      • Animals
      • Architecture
      • Art
      • Auto
      • Aviation
      • Books
      • Cartoons
      • Celebrities
      • Children
      • Culture
      • Design
      • Economics
      • Education
      • Entertainment
      • Fashion
      • Fitness
      • Food
      • Gadgets
      • Games
      • Health
      • History
      • Hobby
      • Humor
      • Interior
      • Moto
      • Movies
      • Music
      • Nature
      • News
      • Photo
      • Pictures
      • Politics
      • Psychology
      • Science
      • Society
      • Sport
      • Technology
      • Travel
      • Video
      • Weapons
      • Web
      • Work
        Submit
        Valid formats are JPG, PNG, GIF.
        Not more than 5 Мb, please.
        30
        surfingbird.ru/site/
        RSS format guidelines
        500
        • Advertisement
        • Animals
        • Architecture
        • Art
        • Auto
        • Aviation
        • Books
        • Cartoons
        • Celebrities
        • Children
        • Culture
        • Design
        • Economics
        • Education
        • Entertainment
        • Fashion
        • Fitness
        • Food
        • Gadgets
        • Games
        • Health
        • History
        • Hobby
        • Humor
        • Interior
        • Moto
        • Movies
        • Music
        • Nature
        • News
        • Photo
        • Pictures
        • Politics
        • Psychology
        • Science
        • Society
        • Sport
        • Technology
        • Travel
        • Video
        • Weapons
        • Web
        • Work

          Submit

          Thank you! Wait for moderation.

          Тебе это не нравится?

          You can block the domain, tag, user or channel, and we'll stop recommend it to you. You can always unblock them in your settings.

          • habrahabr.ru
          • домен habrahabr.ru

          Get a link

          Спасибо, твоя жалоба принята.

          Log on to Surfingbird

          Recover
          Sign up

          or

          Welcome to Surfingbird.com!

          You'll find thousands of interesting pages, photos, and videos inside.
          Join!

          • Personal
            recommendations

          • Stash
            interesting and useful stuff

          • Anywhere,
            anytime

          Do we already know you? Login or restore the password.

          Close

          Add to collection

             

            Facebook

            Ваш профиль на рассмотрении, обновите страницу через несколько секунд

            Facebook

            К сожалению, вы не попадаете под условия акции