Поставили как-то передо мной задачу написать несколько юнитов на Delphi, обеспечивающих доступ к следующим возможностям Windows:
- Shared memory.
- Pipes.
Переписать код примеров из документации Microsoft с C на Delphi – это было даже не полдела, а процентов 10. Главное было связать это все вместе, дописать, где нужно, дополнительные функции и заставить всё это работать, а не просто существовать в виде исходного кода. В результате родился такой уродливый монстр, что его не то, что показывать кому-то, а просто смотреть на него было страшно.
Однако, может, кто-то найдет в его недрах что-то для себя полезное (а вдруг?), поэтому я решил его здесь выложить. На всеобщее поругание. Полный исходный код этой abomination выложен на GitHub. В тексте статьи будут даны названия файлов, где можно посмотреть тот или иной кусок реализации.
Первая задача – сортировка текстового файла – решалась так:
- Открываем файл (CreateFile).
- Используем CreateFileMapping и MapViewOfFile (commonMemoryMappedFileUnit.pas).
- Определяем кодировку файла. Здесь я ограничился, по сути, только двумя кодировками: UTF-16 и ACP – однобайтовая кодировка с кодовой страницей по умолчанию. В русскоязычной Windows обычно используется Windows-1251.
- Разбиваем текст на строки. То есть ищем маркеры конца строк и, соответственно, считаем текст от маркера до маркера одной строкой.
- Сортируем полученный массив строк (SortingTestMergeSorterUnit.pas).
- Записываем отсортированный массив строк обратно в файл.
- PROFIT!
Вторая задача – передача файла от одного приложения к другому – оказалась куда сложнее. Получить реализацию pipes на Delphi на основе примеров Microsoft – это одно, но сделать из нее рабочую передавалку файлов – совсем другое. В качестве программы-сервера Microsoft предлагает несколько вариантов:
Как открывать файлы в другой программе
- Multithreaded Pipe Server.
- Named Pipe Server Using Overlapped I/O.
- Named Pipe Server Using Completion Routines.
Вкратце, передача файлов работает так:
- Создаем сервер, то есть создаем named pipe.
- Создаем клиент, который подключается к этому named pipe.
- Крутим цикл «посылаем запрос – ждем ответ – читаем ответ» на клиенте и на сервере.
- Большие файлы посылаем частями и потом собираем файл из этих кусочков.
- По окончании передачи файлов закрываем соединение.
Да, еще там до кучи лежит оптимизированная реализация функции StringReplace (BlitzTestStringReplaceCustomUnit.pas). Может, кому пригодится.
Любые комментарии (в том числе и откровенная ругань) приветствуются.
Если вдруг (ну, а вдруг?) кого-то заинтересуют другие мои репозитории, то могу и о них пару слов написать. Если, конечно, этот текст вообще опубликуют.
Источник: habr.com
Показываю как удалить файл, занятый в другой программе
Урок 22 — Принцип работы с файлами
Давайте ознакомимся с набором функции необходимых для работы.
function FileCreate(const FileName: String): integer;
Создаёт файл в указанном пути FileName и возвращает индекс созданного файла. В случае ошибки при создании, она вернет -1.
function FileOpen(const FileName: String; Mode: LongWord): integer;
- fmOpenRead — открытие только на чтение
- fmOpenWrite — открытие только на запись
- fmOpenReadWrite — открытие и на чтение и на запись
function FileRead(handle: integer; var Buffer; Count: integer): integer;
Читает открытый или созданный файл. Вместо пути она принимает индекс файла handle. Buffer — куда будет записано содержимое. Count — количество байтов которое нужно прочитать.
function FileWrite(handle: integer; var Buffer; Count: integer): integer;
Аналогичная функция чтению, но вместо чтения она записывает содержимое размером Count (в байтах) переменной Buffer в файл.
function FileClose(handle: integer);
Закрывает файл, чтобы другие программы тоже имели доступ к нему.
function FileSearch(const Name, DirList: string): String;
Осуществляет поиск файла Name в одной или более папках DirList, отделённых друг от друга точкой с запятой. Необходимо указывать не только название папки, но и полный адрес этой папки.
Имя файла может быть, как файловым именем, так и полным адресом файла.
Если файл будет найден, то возвращается полный адрес файла включая имя файла, в случае не нахождения искомого файла будет возвращена пустая строка.
ПРЕДУПРЕЖДЕНИЕ: ВСЕГДА сначала поиск будет проходить в текущей папке, независимо от перечисленных директорий. Если файл обнаруживается там, то путь файла возращён не будет, а только имя файла.
function FileSeek(Handle, Offset, Origin: Integer): Integer;
- File_Begin (0) — Смещение (offset) рассчитывается относительно начала файла
- File_Current (1) — Смещение рассчитывается относительно текущей позиции в файле
- File_End (2) — Смещение рассчитывается относительно конца файла
Все с теорией мы покончили, теперь что-нибудь считаем. вытащим на форму 2 кнопки и Memo и напишем в обработчике события 1 кнопки OnClick следующий код:
procedure TForm1.Button1Click(Sender: TObject); var F: integer; //наш файл Str: string[8]; //Строка которую мы будем писать begin F:= FileCreate(‘D:/1.txt’); //Создаём файл FileWrite(F, ‘123456789’, SizeOf(str)); //Записываем в него строку «123456789» FileClose(F); //Закрываем файл end;
функция SizeOf возвращает размер типа или самой переменно в байтах, т.к. заранее размер строки неизвестен. скомпилируем и нажмем кнопочку.
Теперь прочитаем это. создаём обработчик события 2 кнопки OnClick и пишем код:
procedure TForm1.Button2Click(Sender: TObject); var F: integer; //наш файл Str: string[8]; //Строка в которую мы сохранять begin F:= FileOpen(‘D:/MyGame/1.txt’, fmOpenRead); //открываем файл на чтение FileRead(F, Str, SizeOf(str)); //читаем Memo1.Text:=Str; //присваиваем memo нашу строку FileClose(F); //Закрываем файл end;
Удачи!
Встретимся в следующем уроке!
Источник: thedelphi.ru
DelphiComponent.ru — бесплатно видеоуроки по Delphi, статьи, исходники
Вот поэтому рекомендуется использовать специализированный в Delphi объект TFiiestream. Вы можете написать и свой класс, который будет делать то же самое, но TFiiestream делает это достаточно хорошо.
СОВЕТ. Объект TFileStream рекомендуется использовать по следующей причине. Если Microsoft снова введет какие-то нововведения, Borland учтет их в объекте, и вам нужно будет только перекомпилировать свои программы. Никаких изменений в код вносить не надо. Вы один раз изменяете объект (или это делает Borland) и компилируете все свои программы с новыми возможностями.
Это громадное преимущество ООП, и вы должны его использовать, потому что оно упрощает жизнь.
У TFileStream есть еще одно преимущество — вам не нужно отслеживать нововведения от Microsoft. В этой корпорации постоянно появляются новые идеи, из-за которых появляются новые функции, а мы, программисты, должны постоянно изучать новинки и переписывать код. Лично я намучился уже с функциями работы с файлами. Их так много, что с первого взгляда даже не поймешь, какие новые и рекомендуются к использованию, а какие устарели. В любом случае, использование этого объекта намного проще, чем WinAPI, поэтому желательно начинать изучение работы с файлами именно с него.
Итак, давайте взглянем на объект TFileStream. Первое, что надо сделать, — это объявить какую-нибудь переменную типа TFileStream:
Вот так мы объявили переменную f типа объекта TFileStream. Теперь можно проинициапизировать переменную.
Инициализация — выделение памяти и установка значений по умолчанию. За эти действия отвечает метод create. Нужно просто вызвать его и результат выполнения присвоить переменной. Например, в нашем случае нужно вызвать TFileStream.create и результат записать в переменную f.
f := TFileStream.Create(параметры);
Давайте разберемся, какие параметры могут быть при инициализации объекта TFileStream. У метода create может быть три параметра, причем последний можно не указывать.
- Имя файла (или полный путь к файлу), который надо открыть. Этот параметр — простая строка.
- Режим открытия. Здесь вы можете указать один из следующих параметров открытия файла:
- fmCreate— создать файл с указанным в первом параметре именем. Если файл уже существует, то он откроется в режиме для записи;
- fmOpenRead — открыть файл только для чтения. Если файл не существует, то произойдет ошибка. Запись в файл в этом случае не возможна;
- fmopenwrite — открыть файл для записи. При этом во время записи текущее содержимое уничтожается;
- fmOpenReadWrite — открыть файл для редактирования (чтения и записи).
- fmshareCompat — при этих правах другие приложения тоже имеют права работать с открытым файлом;
- fmShareExciusive — другие приложения не смогут открыть файл;
- fmShareDenyWrite — при данном режиме другие приложения не смогут открывать этот файл для записи. Файл может быть открыт только для чтения;
- fmshareDenyRead— при данном режиме другие приложения не смогут открывать этот файл для чтения. Файл может быть открыт только для записи;
- fmShareDenyNone — не мешать другим приложениям работать с файлом.
С первыми двумя параметрами все ясно. Но зачем же нужны права доступа к файлам. Допустим, что вы открыли файл для записи. Потом его открыло другое приложение и тоже для записи. Обе программы записали какие-то данные. После этого ваше приложение закрыло файл и сохранило все изменения.
Тут же другое приложение перезаписывает ваши изменения, даже не подозревая о том, что они уже прошли. Вот так ваша информация пропадает из файла, потому что другая программа просто перезаписала ее.
Если вы пишете однопользовательскую программу, и к файлу будет иметь доступ только одно приложение, то про права можно забыть и не указывать их.
После того как вы поработали с файлом, достаточно вызвать метод Free, чтобы закрыть его:
Теперь давайте познакомимся с методами чтения, записи и внутренней структурой файла. Начнем со структуры. Когда вы открыли файл, позиция курсора устанавливается в самое начало и любая попытка чтения или записи будет происходить в эту позицию курсора. Если вам надо прочитать или записать в любую другую позицию, то надо передвинуть курсор. Для этого есть метод seek. У него есть два параметра:
- Число, указывающее на позицию, в которую надо перейти. Если вам нужно передвинуться на 5 байт, то просто поставьте цифру 5.
- Откуда надо двигаться. Тут возможны три варианта:
- soFromBeginning — двигаться на указанное количество байт от начала файла.
- soFromCurrent — двигаться на указанное количество байт от текущей позиции в файле к концу файла.
- soFromEnd — двигаться от конца файла к началу на указанное количество байт.
Не забывайте, что 1 байт— это один символ. Единственное исключение — файлы в формате Unicode. В них один символ занимает 2 байта. Так образом, надо учитывать, в каком виде хранится информация в файле.
Итак, если вам надо передвинуться на 10 символов от начала файла, можете написать следующий код:
f.Seek(10, soFromBeginning);
Метод seek всегда возвращает смещение курсора от начала файла. Этим можно воспользоваться, чтобы узнать, где мы сейчас находимся, а можно и узнать общий размер файла. Если переместиться в конец файла, то функция вернет количество байт от начала до конца, т. е. полный размер файла.
В следующем примере устанавливается позиция в файле на 0 байт от конца, т. е. в самый конец. Тем самым получается полный размер файла:
Размер файла := f.Seek(0, soFromEnd);
Вот таким небольшим трюком можно узнать размер. Правда, для этого придется использовать три операции: открыть, переместиться в конец файла и потом закрыть его.
Для чтения из файла нужно использовать метод Read. И снова у этого метода два параметра:
- переменная, в которую будет записан результат чтения;
- количество байт, которые надо прочитать.
Давайте взглянем на код чтения из файла, начиная с 15-й позиции. Этот код вы можете увидеть в листинге
f:TFileStream; //Переменная типа объект
TFileStream. buf: array[0..10] of char; // Буфер для хранения прочитанных данных begin
//В следующей строке я открываю файл filename.txt для чтения и записи,
f:= TFileStream.Create(1 с:filename.txt’, fmOpenReadWrite);
f.Seek(15, soFromCurrent); // Перемещаюсь на 15 символов вперед,
f.Read(buf, 10); // Читаю 10 символов из установленной позиции.
f.Free; // Уничтожаю объект и соответственно закрываю файл, end;
Обратите внимание, что в методе seek движение происходит на 15 символов не от начала, а от текущей позиции, хотя нужно от начала. Это потому, что после открытия файла текущая позиция и есть начало. Но лучше на это не рассчитывать.
Метод Read возвращает количество реально прочитанных байт (символов). Если не возникло никаких проблем, то это число должно быть равно количеству запрошенных для чтения байт.
Есть только два случая, когда эти числа отличаются:
- при чтении был достигнут конец файла и дальнейшее чтение стало невозможным;
- ошибка на диске или любая другая проблема.
Осталось только разобраться с записью. Для этого мы будем использовать метод write. У него так же два параметра:
- переменная, содержимое которой нужно записать;
- число байт для записи.
Пользоваться этим методом можно точно так же, как и методом для чтения.
ЗАМЕЧАНИЕ. После чтения или записи текущая позиция в файле смещается на количество прочитанных байт. То есть текущая позиция становится в конец прочитанного блока данных.
Пример пока рассматривать не будем, потому что в принципе и так все ясно. А если есть вопросы, то на практике мы закрепим этот материал буквально через один или два раздела, когда будем работать со структурами.
Источник: delphicomponent.ru