Отправляет email-рассылки с помощью сервиса Sendsay

Программирование на Delphi

  Все выпуски  

Программирование на DELPHI в вопросах и ответах #12


Информационный Канал Subscribe.Ru

Программирование на Delphi в вопросах и ответах.
Выпуск №12 от 29.12.04.


Добрый день, уважаемые читатели!

Это последний выпуск нашей рассылки в этом году! Поздравляю всех вас с Новым Годом, желаю приятно провести праздники. Удачи вам и счастья в Новом 2005 Году! Отдельно хочется сказать о нашем проекте. Он существует чуть больше двух месяцев, но за это время очень успел развиться. Вышло 12 выпусков рассылки, на сайте много полезного, сайт очень преобразился по сравнению с начальной версией. Регулярно приходят новые вопросы и ответы на уже заданные. Количество подписчиков скоро подойдёт к 1500! Однако результаты работы всё равно не очень хорошие. К примеру, на просьбу проголосовать, из 1400 человек откликнулись только 45... Обидно конечно... Но я искренее надеюсь, что это всего лишь "раскачка". Начинается другой год, а значит будет много всего интересного.

Также приношу свои извинения за предыдущий выпуск рассылки. Туда не попали некоторые присланные ответы. Сегодня всё на месте. Теперь, наверное, будут выходить спец-выпуски рассылки, в которых будут публиковаться только вопросы, на которые не пришло ответов, а раздел "Вопросы, ждущие ответа" в обычных выпусках будет убран. Но периодичность этих выпусков будет предельно мала - наверное, раз в месяц. Позже решим.

В голосовании насчёт публикации лидеров по количеству баллов приняли участие 19 человек. Итак,
- публиковать лидеров хотят 13 читателей,
- не публиковать их - 6 читателей.

В общем, что делать, - неизвестно. Некоторые в письмах подписывали также "Да, обязтальено публикуйте", а некоторые - наоборот - "Ни в коем случае". Я принял решение. Публиковать лидеров мы будем, но только начиная со следующего выпуска. Кроме того, при публикации каждого из трёх лидеров будет учтён ваш голос, который вы недавно отдали (если голосовали вообще). Таким образом, если вы попадаете в тройку лидеров, то ваше имя опубликовано не будет - будет только стоять прочерк.

Напомню, что хотелось бы открыть какой-нибудь новый разделы (или разделы), посвящённые Delphi. Вот такие пришли письма с советами:


Считаю, крайне нужная вещь. Однако появление новых компонентов,
библиотек сложно назвать Новостями Делфи. Больше для этого подходят
названия "Кладовка", "Копилка", "Сундучок со сказками" (С)Домовенок
Кузя :) или что-то в этом духе.

Темой для новостей должны стать события, относящиеся к фирме
Borland. В частности интересуют последние разработки как в области
самой среды, так и средств расширяющих ее возможности: Case-,
Source-системы, UML и т.д. Слышал, что Борланд в последнее время
активно скупает компании, производящие такие системы. Хотелось также
увидеть ссылки на статьи о предполагаемых путях развития данного ПО.


Раздел "Новости из мира Delphi" не помешал бы. :))
Полезно было бы в этом разделе обмениваться ссылками на компоненты
для Делфи или другие какие-нибудь тулзы для программирования.


Ну что же, вот такие предложения. Хочу сообщить: один новый раздел мы точно откроем в ближайшем будущем. И находится в этом разделе будут разные "вещи": компоненты для Delphi, plug-in'ы, исходники программ. Этот раздел будет открыт как в рассылке, так и на сайте. Соответственно, в рассылке будет публиковаться описание новых поступлений, а сами файлы будут находиться на сайте. Этот раздел мы откроем сразу же после Нового Года!

Оставляю вас наедине с рассылкой, приятного чтения. До встречи в Новом 2005 Году!!!

Правила нашей рассылки:
1. Не присылайте ответов на вопросы вроде "да я не знаю" или "да/нет". Такие ответы не публикуются.
2. Вопросы, не касающиеся Delphi, не принимаются (для этого существуют другие рассылки).
3. Запрещено присылать вложенные файлы, размером более 100 Кб, без предварительной связи с администратором.
4. Не изменяйте тем присылаемых писем. писем Письма с "неправильными" темами не публикуются!



Статья по Delphi.

Написание инсталлятора на Delphi (Часть 5).

Копирование - 2, Сжатие

Существует такая штука, как сжатие данных. Хорошо, если вы инсталлируете свою программу с компакт-диска. Как правило, в такой ситуации вы не знаете, что ещё записать на эту бездонную болванку и несколько лишних мегабайт вам в данной ситуации совсем не помешают :)

Совершенно по другому обстоят дела с дискетами. На дискеты надо помещать сжатую информацию — в этом случае вся ваша программа возможно (только возможно) влезет всего лишь на тридцать четыре дискеты. Вот как делать сжатие — это вопрос.

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

Метод номер раз — это сжатие файлов стандартными утилитами, разработанными фирмой Microsoft. Когда-то это была программа compress.exe, сейчас — cabarc.exe.
hInFile := LZOpenFile(PChar(SourcePath), ofInReOpenBuff, OF_READ);
hOutFile := LZOpenFile(PChar(TargetPath), ofOutReOpenBuff, OF_CREATE or OF_WRITE);
iLZError := LZCopy(hInFile, hOutFile);
if iLZError > 0 then
// Операция выполнилась успешно, скопировано iLZError байт
else
// Ошибка номер iLZError
LZClose(hOutFile);
LZClose(hInFile);

Метод номер два — сжатие файлов с помощью библиотеки ZLib, которая поставляется вместе с Delphi (она находится в каталоге \Info\Extras\ZLib оригинального диска с Delphi). Она предоставляет вам два класса, которые являются наследниками TStream. Вы можете воспользоваться кодом функции копирования при помощи TFileStream из предыдущей статьи для того, чтобы реализовать как сжатие, так и распаковку произвольного потока (в том числе и файла).

Ещё один метод — использование динамической библиотеки unrar.dll, разработанной Евгением Рошалом. Существуют и другие библиотеки (даже компоненты), вы можете свободно найти их, если будете достаточно долго шляться по Интернету.

Примечание:
Мне кажется, что менее всего размер вашей инсталляции увеличится, если вы используете методы, предлагаемые Microsoft; ровно потому, что они встроены в Windows и вам не надо записывать их на дискету.
Копирование нескольких файлов представляется достаточно простым, раз уж мы научились копировать один файл. Наиболее просто это делается, если ваши файлы поставляются в одном архиве (.CAB или .RAR). Сложным может показаться копирование файлов по маске (*.*) и копирование вложенных подкаталогов. Ниже приводится исходный текст процедуры, которая составляет список файлов в каталоге и всех вложенных подкаталогах.
procedure ReadTree(Path: String; Strings: TStrings);
procedure ReadFolder(Path: String; Strings: TStrings);
var
SearchRec: TSearchRec;
FindResult: Integer;
begin
FindResult := FindFirst(Path + '*.*', faAnyFile, SearchRec);
while FindResult = 0 do
begin
// Если найден подкаталог, рекурсивно читаем его содержимое
// Не забываем игнорировать подкаталоги '.' и '..'
with SearchRec do
if (Name <> '.') and (Name <> '..') then
begin
Strings.Add(Path + Name);
if (Attr and faDirectory <> 0) then
ReadFolder(Path + Name + '\', Strings);
end;
FindResult := FindNext(SearchRec);
end;
FindClose(SearchRec);
end;
begin
// Эта процедура заносит в Strings список файлов во всех вложенных папках
// каталога Path и сами эти папки
Strings.Clear;
if (Length(Path) > 0) and (Path[Length(Path)] <> '\') then
Path := Path + '\';
ReadFolder(Path, Strings);
end;

Отдельно стоит поговорить о тех файлах, которые могут использоваться сразу несколькими программами. Для этих файлов существует даже специальное название — разделяемые (поскольку несколько программ делят их между собой). Обычно они записываются в системный каталог Windows (для Windows 98 это как правило \WINDOWS\SYSTEM, для Windows NT — \WINNT\SYSTEM32). Если системный каталог доступен только для чтения, то эти файлы необходимо записывать в каталог Windows (\WINDOWS и \WINNT соответственно), который всегда доступен для записи.
function GetSysDir: String;
var
szPath: array [0..MAX_PATH - 1] of Char;
I: Integer;
Stream: TStream;
begin
// Получаем системный каталог
GetSystemDirectory(szPath, MAX_PATH);
Result := StrPas(szPath);
// Добавляем обратный слеш в конец пути, если его там нет
if (Length(Result) > 0) and (Result[Length(Result)] <> '\') then
Result := Result + '\';
// Подбираем имя файла вида XXXXXXXX.TMP, где XXXXXXXX ---
// шестнадцатиричное число, который не существует в системном каталоге
I := 0;
while FileExists(Result + IntToHex(I, 8) + '.TMP') do
Inc(I);
try
// Создаём файл и удаляем его. Если всё нормально, то каталог доступен
// для записи.
Stream := TFileStream(Result + IntToHex(I, 8) + '.TMP', fmCreate);
Stream.Free;
DeleteFile(Result + IntToHex(I, 8) + '.TMP');
except
// Если создать файл не удалось, в качестве системного каталога будем
// использовать каталог Windows.
GetWindowsDirectory(szPath, MAX_PATH);
Result := StrPas(szPath);
if (Length(Result) > 0) and (Result[Length(Result)] <> '\') then
Result := Result + '\';
end;
end;

Если разделяемый файл уже существует в целевом каталоге, то необходимо сравнить версии, языки и др. характеристики двух файлов и на основании этого сравнения решать — копировать файл или не надо.

При копировании разделяемых файлов требуется уведомить Windows о том, что одним разделением стало больше. Это делается через реестр. При замещении файлов, которые в момент инсталляции используются Windows требуется определённая техника, поскольку перезаписать занятый файл нельзя. Эти вопросы в ближайшее время будут освещены в следующей статье. Поистине, копирование файлов — тема неисчерпаемая :)

Напоследок, исследуем вопрос о том, куда копировать файлы? В соответствии с рекомендациями Microsoft, каталог вашей программы должен иметь форму <Program Files>\<Название вашей фирмы>\<Название продукта>. Вы можете также включить в название каталога информацию о версии продукта, например C:\Program Files\Borland\Delphi 7, хотя возможен и вариант C:\Program Files\Borland\Delphi\7 (реально фирма Borland использует первый вариант). Бывают и исключения, в частности FAR Евгения Рошала ставится в C:\Program Files\FAR.

Разделяемые файлы следует копировать в системный каталог Windows, а если он защищён от записи — в каталог Windows.

Дальше мы пойдём по реестру...

Обход дерева каталогов с прерыванием и возобновлением
или "Куда мы идем завтра?"

Недавно занимаясь интересной задачкой по написанию службы индексации, столкнулся с интересным вопросом: " А как бы нам поиск заморозить и продолжить после (через минуту, завтра, через месяц)?". Да конечно можно сказать - что у тебя за машина такая, вот у меня дерево каталогов обходит за 3 минуты... Согласен, это не вопрос. Но когда нужно не просто обходить, а еще и выполнять некоторые действия с файлами, да если их на диске 150 тыс. и больше, да еще не загружая процессор на 100%, то время может затянуться до нескольких суток, вот тогда - как быть?

Вот этой теме я и решил посвятить статью. Как оказалось, в Интернете информации по этой теме нет. Либо это слишком просто, либо никому не нужно. Как выяснилось - ни то ни другое.

Со стандартной процедурой обхода дерева сталкивались очень многие

procedure FileFind(path:string);
  var sr:Tsearchrec;// Описываем структуру, которую использует для поиска система
  found:integer; // найдено или нет
begin
  found:=FindFirst(path + '\*.*', FaAnyfile, sr); {по команде FindFirst программа создает
    структуру следующего типа
    TsearchRec = record
     Time: Integer; // время создания
     Size: Integer; // его размер
     Attr: Integer;// атрибуты
     Name:TFileName // = TString; собственно имя файла
     ExcludeAttr: Integer; найденные атрибуты
     FindHandle: THandle; // !!! указатель на структуру поиска, которую создает система, 
     а не наша программа. Вот для чего обязательно в конце поиска указывать FindClose - 
     это высвобождает память
     FindData: TWin32FindData; // собственно эта структура
 end;}
  while (found = 0) do // если хоть чтото найдено
   begin
    if (sr.name <> '.') and (sr.name <> '..') then
    begin // если это не указатели на корневые каталоги, то чтото нашли
      if (sr.attr and FaDirectory) = FaDirectory then
        // ага вот поддиректория - вызываем себя рекурсивно, но с поиском уже в этой директории
        FileFind(path+'\'+sr.name)
        else
        begin
          // вот тут выполняем чтото с найденным файлом
          ......
          mainform.memo1.lines.append(path+'\'+sr.name);
        end
      end;
   found:=findnext(sr); // есть ли еще файлы или каталоги
   end;
   FindClose(sr); // поиск закончен - нужно освободить память
end;

Казалось бы сохранить состояние процедуры поиска  просто - достаточно сохранить структуру - sr:TsearchRec, а потом ее восстановить и поиск продолжится.
Первое - Однако при даже невнимательном рассмотрении процедуры видно, что она вызывает сама себя - налицо обычная рекурсия. Получается что надо сохранять не одну SearchRec, а несколько. Полдела - сохранить, но ведь нужно и восстановить эти рекурсивные вызовы. Т.е при продолжении поиска построить этакую матрешку из процедур поиска, а потом уже его продолжать.
Второе - сама SearchRec. Казалось бы она находится в области данных нашей программы. Да это наполовину верно. Верхняя половина SearchRec действительно лежит в области данных нашей программы и делать мы с ней можем что душе угодно. Это переменные  Time: Integer;  Size: Integer;  Attr: Integer; Name:TFileName; ExcludeAttr: Integer;. А вот вторая ее половина (FindHandle: THandle; FindData: TWin32FindData;) нам не принадлежит - ее генерирует система по нашему запросу FindFirst(.....) и уничтожает по команде FindClose(....).
Третий казалось бы простой вопрос - SearchRec.Name имеет тип TFileName=TString. Какую длину он имеет? Одни скажут 255, другие 65535. Согласен, и то и другое верно, но не тут. Длина действительно 255. А вот с типом нас нагло обманули. Реально в памяти хранится не TString [255], а PChar {Имя файла}+PChar{его расширение}. Для нас с вами это преобразуется в обычную строку при обращении, и до столкновения с данной ситуацией я свято верил что там TString[255]. Кстати в чем разница между Богом и билом гейтсом? Бог не считает себя билом гейтсом ...

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

Третий вопрос - как сохранить , а потом восстановить SearchRec, если он состоит непонятно из чего. А давайте сделаем свой SearchRec, как нам нужно. А именно так

type // этот тип почти полностью переписывается со стандартного TSearchRec
   TMysearchRec = record
     Time: Integer; 
     Size: Integer; 
     Attr: Integer;
     Name: string[250];//вот тут обрабатывалось неверно при типе TString, как длина ?
     ExcludeAttr: Integer;  
     FindHandle: THandle;  // в принципе не нужен, но не будем сильно пугать читателей
                           // сильными отличиями, да и бог с ними - с восемью байтами
     FindData: TWin32FindData;
   end;

но нам еще требуется сохранять несколько переменных самой программы, а именно Found - найдено чтото или нет и Path - с каким параметром нас вызывали, поэтому на основе этого типа делаем еще один

   TMyRec_Sea = record
       Rec_Sea:TMySearchRec; // наша структура поиска
       path:String[250]; // откуда начинали
       found:integer; // при остановке нашли чтото или нет
   end;

Второй вопрос после первого решается не очень красиво, но довольно легко. Да система генерит структуру: FindHandle: THandle; FindData: TWin32FindData. FindData - собственно сама структура и FindHandle - указатель на нее. Пусть система генерит что угодно, если с умом, то можно обойти и это. Многие ли помнят такое INT21h->INT 13H. Думаю вспомнили. При восстановлении поиска дадим команду FindFirst, а потом подменим FindData и остальные поля, не трогая FindHandle, иначе сразу после окончания поиска (!!! ???) получим обращение к недопустимому адресу и вылет программы.

    ......
    // создаем запись для поиска
    FindFirst(path+'\'+mask, FaAnyfile, sr);
    delfile:=false; found:=buffer.found;
    // загоняем в SEARCHREC все кроме FINDHANDLE (он создается системой)
    sr.Time:=buffer.rec_sea.Time; sr.Size:=buffer.rec_sea.Size;
    sr.Attr:=buffer.rec_sea.Attr; sr.Name:=buffer.rec_sea.Name;
    sr.ExcludeAttr:=buffer.rec_sea.ExcludeAttr; sr.FindData:=buffer.rec_sea.FindData;

Первый вопрос - как же сохранять состояние процедуры при рекурсии?. Давайте сохранять SearchRec в файл и используем принцип магазина (не продуктового, а от автомата калашникова) - последний вошел - первый вышел. Вот примерная структура процедуры при выполняющемся поиске ( при нескольких рекурсивных вызовах)

Findfile('c:\')
    Findfile('c:\Docs')
        FindFile(c:\Docs\Delphi')
           ......
      

При получении сигнала на остановку процедуры начинают писать в файл в обратном порядке, а именно - FindFile(c:\Docs\Delphi'),Findfile('c:\Docs'),Findfile('c:\'). Примерно так

Findfile('c:\')------------------------------------+
    Findfile('c:\Docs')---------------------+      !
        FindFile(c:\Docs\Delphi') ---+      !      !
                                     v      v      v
       [файл сохранений состояния] [rec1] [rec2] [rec3]  
      

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

Да, едва не забыл, как мы узнаем что надо приостановить поиск ? Давайте заведем глобальную переменную Process. Как она станет False - пора останавливаться

Ниже приведена часть модуля с использованием описанных алгоритмов

Unit unit1;
 ......
var
 ....
  process:boolean; // вот глобальная переменная она и управляет поиском true - можно
                   // false - стоп с запоминанием состояния
 .....

procedure FileFind(path:string;resume:boolean);
{ сканирует диск (вернее дерево каталогов) при вызове PATH - начальный каталог для обхода
RESUME - если TRUE - то продолжать сохраненный поиск (тогда значение PATH игнорируется,
кроме случая, когда не обнаружен файл сохранения поиска)
при установке глобальной переменной PROCESS в false останавливается
с запоминанием предыдущего состояния,внимание - РЕКУРСИЯ !!! }
const
   save_ext='.rec'; // в каталоге приложения создает SAVE файл с именем приложения и указанным расширением
   mask='*.*';
type
   TMysearchRec = record
   // пришлось написать свой тип SEARCHREC с NAME фиксированной длины
     Time: Integer; Size: Integer; Attr: Integer;
     Name: string[250]; //вот тут обрабатывалось неверно при типе TString, как длина ?
     ExcludeAttr: Integer;  FindHandle: THandle;  FindData: TWin32FindData;
   end;
   TMyRec_Sea = record
       Rec_Sea:TMySearchRec;
       path:String[250];  found:integer;  delfile:boolean;
   end;
var
  sr:TSearchRec; 
  RecFile:TFileStream;
  buffer:tMyRec_Sea;
  sp,save_file_name:string; found:integer; delfile:Boolean;
  delfile:Boolean;
begin
  if resume then
  // возобновить поиск или начать новый
  begin
        save_file_name:=ChangeFileExt(ParamStr(0),save_ext);
        if FileExists(save_file_name) then
        begin
           RecFile:=TFileStream.Create(save_file_name,fmOpenReadWrite);
           // чистим буфер, не важно, необходимо для отладки
           fillchar(buffer,sizeof(buffer),#0);
           // читаем сохранение начиная с конца файла
           RecFile.Seek(-1*sizeof(buffer),soFromEnd);
           RecFile.Readbuffer(buffer,sizeof(buffer));
           path:=buffer.path; sp:=path;
           // создаем запись для поиска
           FindFirst(path+'\'+mask, FaAnyfile, sr);
           delfile:=false; found:=buffer.found;
           // загоняем в SEARCHREC все кроме FINDHANDLE (он создается системой)
           sr.Time:=buffer.rec_sea.Time; sr.Size:=buffer.rec_sea.Size;
           sr.Attr:=buffer.rec_sea.Attr; sr.Name:=buffer.rec_sea.Name;
           sr.ExcludeAttr:=buffer.rec_sea.ExcludeAttr; sr.FindData:=buffer.rec_sea.FindData;
           // режем кусок уже прочитали свои данные - другим они не понадобятся
           RecFile.Seek(-1*sizeof(buffer),soFromEnd);  recfile.Size:=RecFile.Position;
           // дорезались - дозагружаться неоткуда
           if RecFile.Size=0 then delfile:=true;
           RecFile.Free;
           if delfile then sysutils.DeleteFile(save_file_name);
        end
        else
        // нет сохраненных поисков
        begin
           // начинаем новый
           sp:=path;  resume:=false;
           // тут исправляется разница между C:\ и C:\DOCS - убираем последний слэш
           if sp[length(sp)]='\' then  sp:=copy(sp,1,length(sp)-1);
           found:=FindFirst(sp + '\'+mask, FaAnyfile, sr);
        end
  end
  else
  begin
     // новый поиск - пристрелить старые записи
     save_file_name:=ChangeFileExt(ParamStr(0),save_ext);
     if fileExists(save_file_name) then sysutils.DeleteFile(save_file_name) ;
     sp:=path;
     if sp[length(sp)]='\' then  sp:=copy(sp,1,length(sp)-1);
     found:=FindFirst(sp + '\'+mask, FaAnyfile, sr);
  end;
  // закончена подготовка - вперед поиск
  while (found = 0) and process do
  begin
    application.ProcessMessages;
    if (sr.name <> '.') and (sr.name <> '..') then
    begin
       if (sr.attr and FaDirectory) = FaDirectory
          then
          begin
             FileFind(sp+'\'+sr.name,resume);
          end
          else
          begin
            // ну тут разные действия с найденым файлом
            mainform.label1.caption:=('начат разбор  '+sp+'\'+sr.name) ;
             ................
            // закончили действия

            Application.ProcessMessages; // а вот без этого мы никогда не узнаем что пора поиск
                                         // закончить
          end;
    end;
    if process then found:=findnext(sr);
  end;
  if not process then
    // получили сигнал на остановку сканирования нужно запомнить состояние
    begin
        save_file_name:=ChangeFileExt(ParamStr(0),save_ext);
        if not FileExists(save_file_name) then RecFile:=TFileStream.Create(save_file_name,fmCreate)
          else RecFile:=TFileStream.Create(save_file_name,fmOpenReadWrite);
        RecFile.Seek(0,soFromEnd);
        // заполняем буфер текущим состоянием
        buffer.rec_sea.Time :=sr.Time; buffer.rec_sea.Size :=sr.Size ;
        buffer.rec_sea.Attr :=sr.Attr ; buffer.rec_sea.Name :=sr.Name ;
        buffer.rec_sea.ExcludeAttr :=sr.ExcludeAttr ;  buffer.rec_sea.FindHandle :=sr.FindHandle ;
        buffer.rec_sea.FindData :=sr.FindData ; buffer.path:=sp; buffer.found:=found;
        RecFile.Writebuffer(buffer,sizeof(buffer));
        RecFile.Free;
    end;
  Application.ProcessMessages;
  sysutils.FindClose(sr);
end;

Данная статья не претендует на оригинальность и призвана помочь в разработке своих программ, 
если есть замечания, усовершенствования, пишите: Звягинцев Павел.

[Статью прислал: Звягинцев Павел, получив за это 10 баллов].


Присылайте свои статьи по адресу delphi-faq@list.ru с темой 'Clause' (без кавычек), и они будут опубликованы в ближайших выпусках рассылки. Большая просьба: статью оформляйте в -txt или -doc формате и используйте -zip или -rar сжатие (без самораспаковки).


Новые вопросы:

34. Здравствуйте все! Меня интересует вот какой вопрос: какие принципы и средства в Delphi можно использовать для написания программы по озвучиванию текста. Заранее благодарен. [Ответить].

35. Учуся работать с базами данных. Можно ли использовать в TTable в свойстве DataBazeName каталог программы??? А то при переносе базы на другой комп приходится изменять исходник. Я конечно могу написать сложный алгоритм, но не хочется. [Ответить].

36. Вопрос такой: как сделать вместо обычного курсора песочные часы, так чтобы они были над всей формой, а не над отдельными компонентами. Т.е. можно изменять форму курсора для каждого компонента, находящегося на форме, но это неудобно. Наверняка есть простой способ изменения формы курсора, как это сделано в виндах когда что-то грузится. [Ответить].

37. Как отправить текст из переменной на сайт в интернете в текстовой или HTML файл? [Ответить]. 

38. Доброго времени суток всем! При создании отчета главный-детальный при вызове Preview программа виснет. Связано ли это с данными (dBase таблицы) или я чего не того делаю? Заранее спасибо. С уважением, Александр! [Ответить].

39. У меня два вопроса: 1. Как сделать, чтобы в ComboBox список открывался не вниз, а вверх? Может, есть другие компоненты? 2. Как открыть в Memo или RichEdit (или еще в чем-то) Text DOS? [Ответить].



Ответы на вопросы:

26. (Работа с xls-файлами). [Отвечает: Feniks]: Для экспорта данных в Excel я рекомендую библиотеку VtkExport от компании VtkTools. У них есть еще и другие полезные компоненты для формирования отчетов. Библиотека экспорта бесплатная, с большими возможностями, кидает данные не в Excel, а сразу в файл xls без использования OLE, DDE, т.е. для ее работы вообще не нужен MS Office. Плюс отличный хелп на русском.

30. (Редактирование реестра из командной строки). [Отвечает: Igor]: Для редактирования системного реестра из командной строки используйте reg.exe(он у меня находится в папке Windows/System32). Для справки запустите reg /?, там все подробно описано.

[Отвечает: Звягинцев Павел]: Ну, положим, одним батником не отделаешься. Поступим так: 1 создаем REG файл с информацией о том, что нужно занести в реестр например 1.REG:
-------кусь-----------
REGEDIT4
[HKEY_CURRENT_USER\Software\Copernic Technologies\Copernic4Plus\Preferences]
"ShowAd"=dword:00000000
-------кусь-----------
кидаешь его например в каталог WINDOWS. Теперь как его занести в реестр? Для этого есть программа Regedit.exe 2. Самый простой вариант - создать BAT с такой командой c:\windows\regedit.exe c:\windows\1.reg в этом варианте - сообщение на экране всё же будет. Другой красивый вариант - создать файл Winstart.bat и запихнуть его в каталог Windows. Если винь его видит при загрузке, то выполняет автоматом, а после удаляет. Сообщений на экране не будет. Кстати именно так работал вирь "Чернобыль", подменяя эксплорер. Недостаток - запуск-то удаляется, т.е. это средство одноразового использования.

[Отвечает: Юрий]: Не очень понял вопрос, но поробую ответить... Создаешь файл *.reg (допустим 1.reg) в котором задаешь чего надо изменить... А далее зависит от системы, сети и т.д. Так для 2000(ХР, 2003) создаешь файл *.cmd (допустим 1.cmd) и пишеш в нем regedit [путь]1.reg и ставишь его в автозагрузку ... При этом пользователь должен быть администратором рабочей станции Если сеть построена на домене, то есть возможность его поставить применять в групповых политиках... В 98 и ниже тоже но через бат файл... Это если я верно понял суть вопроса.

31. (Управление приложениями MS Office). [Отвечает: Звягинцев Павел]: Да также, как и из-под любой Дельфи - используй модули EXCEL_TLB.PAS, OFFICE_TLB.PAS. Вот с модулем WORD_TLB.PAS сложнее - там часто и густо используется Application, что приводит к пересечению со стандартным типом приложения, и ошибкам. Этот модуль нужно переписать, заменив Application на MyApplication. После все работает нормально. Другой вариант - в Дельфе же есть встроенные классы TWordApplication,..., которые делают тоже самое.

25. (Ресурсы в DLL). [Отвечает: Feniks]: Если ты хочешь сохранить (хранить) некие файлы внутри своего ЕХЕ или DLL и их от туда вытягивать и работать с ними, могу посоветовать следующие на Делфи (на Билдере будет тоже самое, но другой синтаксис):

создаешь текстовый файл с расширением .RC и пишешь в нем<имя ресурса> <тип хранимого ресурса> <имя файла> например:
BackGround0 BITMAP "BackGround0.bmp"
BackGround1 BITMAP "BackGround1.bmp"
BackGround2 BITMAP "BackGround2.bmp"
BackGround3 BITMAP "BackGround3.bmp"
SoundAbout WAVE "About.wav"
CurSQLWait RCDATA "SQLWait.ani"
AnimateWait AVI "AnimateWait.avi"

Тут перечислены стандартные типы ресурсов. так же можно использовать свои, наприме MYGIF. но лучше использовать стандартные, так тогда проще их вытягивать будет. Так как для GIF и JPG нет стандартных типов, необходимо использовать RCDATA. Потом все это сохраняешь под именем myres.rc
ОЧЕНЬ ВАЖНО, что бы все перечисленные в нем файлы и сам файл RC лежали в одной папке.

Далее, компилируешь его с помощью утилиты brcc32 в файл .RES brcc32 myres.rc В результате получаешь myres.res Вот его потом присоединяешь в свое проект или в отдельный модель. {$R myres.res}

Тот файл RES, который сама Делфя используем по умолчания для каждого проекта, лучше не использовать, получишь много головной боли хлопот. Я это сам на своей шкуре испытал.

После присоединения его к проекты надо проект перебилдить (Build), а не перекомпилировать, т.к. при компиляции он не компилит файл RES в
ЕХЕ. И в последующих изменениях в RC файле надо каждый раз билдить весь проект.

Что бы вытянуть эти ресурсы из ЕХЕ файла и сохранить их в файлы:
var
ResStream : TResourceStream;
begin
ResStream := TResourceStream.Create(hInstance, <имя ресурса>, RT_RCDATA);
ResStream.SaveToFile('NameFile.ext');
ResStream.Free;
end;

Смотри в Хелпе: TResourceStream, LoadImage, LoadFromResourceName, LoadFromResourceID, LoadIcon, PlaySound, FindResource, FindResourceEx и т.д. и т.п.

32. (Вывод времени в строке состояния). [Отвечает: Dron]: Лично я не вижу в этом ничего неправильного. Сам делаю точно также. Более простого решения нет. Есть только специальные компоненты - навороченные статусбары, в которых есть автоматический вывод текущего времени, даты, а также отображение состояния клавиш Num, Caps и Scroll Lock'ов. Но это особо не нужно и легко сделать самому. Так что спокойно можешь так и оставлять. Единственное, что хочу добавить, - Timer должен иметь частоту срабатывания РОВНО 1000 миллисекунд (т.е. 1 секунду). Если поставить больше или меньше, то вывод времени с точностью до секунд будет колебаться, подобно синусоиде Если непонятно, почему - поставь Interval = 750 и посмотри. Разница очевидна. 

[Отвечает: Vasiliy Gorshkov]: var dt:TDateTime; ... dt:=now; statusbar.value:=DateTimeToStr(dt);

Быстрые ответы

Как видите, появился ещё один новый раздел - "Быстрые ответы". Его пришлось создать по очень простой причине: на нашу рассылку подписаны также и новички в программировании. И вот пришёл простейший вопрос, публиковать который просто нет смысла. Такие вопросы будут публиковаться в этом разделе, а отвечать на них будет администратор (т.е. я :) ).

-1. (Какой функцией проверить файл на наличие???). [Отвечает: Администратор]: Это функция FileExists('путь_к_файлу'): Boolean. True - существует, False - нет.

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

Вы также можете ответить на предыдущие вопросы. Поскольку на них уже ответили как минимум раз, они больше не публикуются в рассылке. Но если вы можете что-то добавить к ответам других, пожалуйста, отвечайте - ответы будут опубликованы. Найти предыдущие вопросы вы можете на нашем сайте: http://www.delphi-faq.fatal.ru/.



Друзья:

Здесь представлены ссылки на дружественные сайты нашего портала. Если вы тоже хотите стать нашим другом, разместите баннер на главной странице своего сайта. Подробнее о том, как стать другом, можно прочитать здесь: http://www.delphi-faq.fatal.ru/banner.htm, а узнать о всех наших друзьях - на странице http://www.delphi-faq.fatal.ru/friends.htm

http://www.x-program.narod.ru/ - На нашем сайте Вы найдёте некоторые наши программы. Также мы занимаемся создание ПО для любой версии ОС Windows под заказ.
http://www.basic.webhost.ru/ - Программирование на языках Basic и Visial Basic. На сайте Вы найдете версии Бейсик, игры, вопросы и ответы, статьи, а также многое другое...
http://www.sashook.nm.ru/ - Игры, флешки, обои, компьютерные приколы.
http://infomania2004.webhost.ru/ - Этот сайт создан для того, чтобы вы могли получить интересующую вас информацию с минимальными затратами сил и времени. Если вы не нашли здесь нужной информации, вы можете оставить заявку на ее поиск. Как только информация будет найдена, она появится на сайте, а вам сообщат об этом.
http://www.dcar.nm.ru - У Вас есть компьютер подключённый к интернет? Тогда у Вас есть всё, чтобы делать деньги прямо сейчас. Создайте свою собственную денежную машину.



Юмор:

CONNECT приходит и уходит,
А BUSY - это навсегда...

- 'Не' с глаголами пишется слитно или раздельно?
- Через пробел!

На уроке литературы с углубленным изучением компьютерных технологий:
- Герасим был: этим: вобщем, не было у него звуковой карты!

Житель крайнего севера в компьютерном магазине:
- У вас операционные системы есть?
- Есть.
- А многозадачные?
- Есть.
- Тогда дайте мне трехзадачную.

В ходе судебного разбирательства с 'Microsoft' были предложены четыре варианта решений:
1. Билл Гейтс должен застрелиться.
2. Билл Гейтс должен утопиться.
3. Билл Гейтс должен повеситься.
4. Билл Гейтс должен опубликовать исходные коды 'Windows'.


Присылайте свои "компьютерные" анекдоты по этой ссылке: delphi-faq@list.ru и они обязательно будут опубликованы!


Товарищи программисты! Проявляйте свою активность. Давайте помогать друг другу!
Если вы не нашли ответа на свой вопрос, не отчаивайтесь! Ведь количество подписчиков постоянно растёт и, наверняка, найдётся тот человек, который поможет вам!
На сегодня всё. До встречи через неделю!


Сайт рассылки: http://www.delphi-faq.fatal.ru E-mail: Delphi-FAQ@list.ru


http://subscribe.ru/
http://subscribe.ru/feedback/
Подписан адрес:
Код этой рассылки: comp.soft.prog.delphifaq
Отписаться

В избранное