Главная страница  Карта сайта  Печать  Написать письмо  Twitter  RSS
Войти
Персональный сайт
Стеллецкого Владимира
Обновлено: 26.12.2012 г.

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

«  ‹  1  2  следующая ›  последняя »

Автообновление приложения (26.12.2012) #

Часто даже довольно опытные разработчики пренебрегают элементарными удобствами в разрабатываемом ими приложении. Одним из таких несомненных удобств является какой-либо механизм автоматического обновления приложения (т.е. доставки новой версии программы пользователю).

Решений тут может быть много, и зависят они от нескольких факторов (размер приложения, количество файлов, наличие/необходимость установщика, расположение пользователей, качество каналов связи и т.п.). Так, например, для пользователей в одной локальной сети можно рассмотреть:

  • Ручное обновление - подойдет только для самых не ленивых, так как мне очень быстро надоедает такой подход даже при 2-3 пользователях.
    Плюсы: ничего реализовывать не надо.
    Минусы: при выходе очередной версии программы необходимо вручную скопировать ее каждому пользователю (или обучить копированию самого пользователя).
  • Использование ярлыка на запуск из общедоступной папки.
    Плюсы: простота решения - создал ярлык каждому пользователю и готово.
    Минусы: необходимо разрешение на запуск из указанной папки; при наличии довольно большого количества пользователей сложно найти время, когда программа не запускается, чтобы выложить новую версию; приложение каждый раз копируется каждому пользователю, что может создать лишнюю нагрузку на сеть; в случае отсутствия папки (например, из-за сбоя) пользоваться приложением не сможет никто.
  • bat (cmd) файл, централизовано копирующий новую версию. Это своего рода "половинчатое" решение, т.к. во-первых его тоже как-то надо раздать пользователям, подготовить механизм для запуска, а также следует учесть, что и его может понадобиться обновить.
  • Реализация простейшей системы самообновления внутри распространяемой программы. Например:
    {$REGION 'АВТООБНОВЛЕНИЕ'}
    
    function GetUpdatePath: String;
    begin
      Result := '\\fileserver\application$\';
    end;
    
    procedure UpdateApplication;
    var
      FileName, Parametrs: string;
      h: HWND;
    begin
      h := Application.Handle;
      FileName := GetUpdatePath + ExtractFileName(Application.ExeName);
      if not FileExists(FileName) then
        Exit;
      if FileAge(Application.ExeName) = FileAge(FileName) then
        Exit;
    //  MessageDlg('Обновление программы...', mtInformation, [mbOK], 0);
      DeleteFile(Application.ExeName + 'old');
      RenameFile(Application.ExeName, Application.ExeName + 'old');
      CopyFile(PChar(FileName), PChar(Application.ExeName), False);
      ShellExecute(h, 'open', PChar(Application.ExeName), PChar(Parametrs), nil, SW_SHOWNORMAL);
      Application.Terminate;
    end;
    
    {$ENDREGION}
    
  • Отдельная программа обновлятор. На первых порах думаю это излишне сложное решение, оправданное только в случае большого количества дополнительных манипуляций проводимых при обновлении. Из плюсов могу отметить, что однажды реализованный и отлаженный, он практически не изменяется в течение цикла разработки приложения, что с большой вероятностью гарантирует обновление основного приложения.

Комментарии

swi, www (31.12.2012)
bat-файл может только обращаться к другому bat-файлу на сервере. В этом случае его обновлять не понадобится. :)

Обновление проектов из SVN (02.03.2014) #

На работе в качестве системы контроля версий исходного кода для текущих проектов используется Subversion (wiki). На своём компьютере я завел папку Projects, в которой собрал все проекты, с которыми работаю. С ростом количества проектов, возник вопрос автоматизации обновлений из центрального репозитория, и через некоторое время родился данный bat-файл:

@echo off
rem Обновление из репозитория
for %%n in (Project Project4Build Project2) do (
echo %%n
cd %%n
"PathToSVN\svn.exe" update
cd ..
echo. 
)
pause

Отдельно хочу отметить, что при появлении необходимости обновлять еще какой-либо проект (или, например, копию папки для сборки финальной или тестовой версии) все что необходимо, так это прописать имя папки в скобках после for %%n in и все.

PS: Новые проекты планируем вести на Mercurial (wiki) или Git (wiki)

Коварный SET LANGUAGE (03.04.2012) #

Вчера нашли ответ на давно мучившую нас проблему: время от времени у разных пользователей программы, в разных её частях возникала одинаковая ошибка:

"Ошибка преобразования даты или времени из символьной строки."

После чего для продолжения работы необходимо было перезагружать приложение, так как практически ни один модуль не работал.

Неприятными моментами была неповторяемость, то есть после переоткрытия приложения всё работало нормально, и то, что запросы, которые вызывали ошибку, отлично выполнялись в редакторе запросов.

Причина ошибки была найдена, как это часто бывает, случайно. Обсуждая новый функционал залезли в один из модулей программы, потом вернулись в обсуждаемый, и тут бац - ошибка. Проанализировав только что открываемые модули нашли ответ: на одной из форм приложения в одном из запросов (TQuery) был примерно такой запрос:

SET LANGUAGE RUSSIAN
SELECT DATENAME(MONTH, GETDATE()) +' '+ CAST(YEAR(GETDATE()) AS Varchar)

Этот приём был использован для получения русского названия месяца, вот только вернуть значение по умолчанию забыли:

SET LANGUAGE US_ENGLISH

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

В среде разработки SQL запросов все отлично выполнялось, так как это была отдельная сессия, а поиску ошибки мешало то, что данный приём был использован всего в нескольких местах и выявить закономерность никак не удавалось.

ScreenShot (21.05.2010) #

Разбирая исходники наткнулся на решении задачки по регулярному сохранению снимков экрана (скриншотов):

procedure TMainForm.tmrScreenShotTimer(Sender: TObject);
var
  bmp: TBitmap;
  st: string;
begin
  bmp := TBitmap.Create;
  try
    bmp.Width := Screen.Width;
    bmp.Height := Screen.Height;
    BitBlt(bmp.Canvas.Handle, 0, 0, Screen.Width, Screen.Height,
           GetDC(0), 0, 0, SRCCOPY);
    st := ComputerName + ' - ' + DateTimeToStr(Now);
    // заменим точки и двоеточия на дефисы, чтобы не было проблем с именем файла.
    st := StringReplace(st, '.', '-', [rfReplaceAll]);
    st := StringReplace(st, ':', '-', [rfReplaceAll]);
    // мало ли сохранить не удастся
    try
      bmp.SaveToFile('\\fileserver\screenshotfolder$\' + st + '.bmp');
    except
    end;
  finally
    bmp.Free;
  end;
end;

GExperts (26.03.2010) #

Думаю, многие начинающие Delphi-программисты не знают о существовании очень удобной надстройки над средой разработки Delphi - GExperts, но я без неё свою работу в Delphi уже не представляю. Надстройка эта стабильная (Delphi с ней не тормозит и не "падает"), бесплатная, есть практически для каждой версии Delphi или C++Builder, а возможностей просто куча. Перечислю только наиболее мной востребованные:

  • Отображение списка всех процедур и функций в модуле с возможностью быстрого поиска по любой части названия и фильтрации по кассам (Ctrl + G);
  • Поиск следующего/предыдущего вхождения текста под курсором в коде (Ctrl + Shift + стрелка вверх/вниз);
  • Диалог для построения сообщений MessageDlg/MessageBox (Ctrl + D);
  • Переход к соответствующему разделителю (Ctrl + Shift + стрелка влево/вправо) - упрощает разбор скобок, кода показывая, где соответствующие, например, begin-end;
  • Комментирование/снятие комментария с выделенного кода (Ctrl + Alt + . / ,).
  • Автозамена (в разделе Code Proofreader). Например, я себе включил замену ":+", ";+" и ";=" на правильные ":=", что существенно ускорило разработку, сократив количество опечаток;
  • Смена регистра выделенного текста (Shift + Alt + C), правда в этом мне помогает ещё и Punto Switcher;
  • Библиотека частоиспользуемого кода;
  • Просмотр классов проекта;
  • Очистка директорий проекта от временных файлов и многое другое.

Owner и Parent (04.12.2009) #

Многие начинающие Delphi программисты при динамическом создании компонент путаются со свойствами Owner и Parent. Попробую внести некоторую ясность.

Для начала определим, за что же отвечают эти свойства.

  • Parent (TWinControl) - родитель. Задаётся прямым указанием после создания компонента. Отвечает за отрисовку компонента.
  • Owner (TComponent) - владелец. Задаётся при создании компонента, как параметр конструктора Create. Отвечает за удаление компонента. Т.е. при уничтожении компонента, он вызывает уничтожение всех, у которых он указан в качестве владельца.

Среда Delphi, когда программист перетаскивает компоненты с панели компонентов на форму автоматически присваивает создаваемому компоненту владельцем Форму, а родителем тот компонент, на который осуществлено перетаскивание.

Для поиска компонент существуют методы:

  • TComponent.FindComponent(const AName: string): TComponent ищет среди компонент, у которых текущий указан в качестве владельца.
  • TWinControl.FindChildControl(const AName: string): TControl - ищет среди дочерних контролов. Тут необходимо уточнить, что если на панели Panel1, лежит другая панель Panel2, а не ней уже поле ввода EditName, то при вызове Panel1.FindChildControl(EditName) функция вернёт nil. Т.к. для Panel1 дочерним является только Panel2.

Часто удобно именно «динамическое» обращение к компонентам по их имени. Например, когда необходимо установить Enabled у всех TEdit на форме, или только на одной какой-то панели. Или же, только у компонент по списку имён, например, аналогичных названиям полей в запросе из БД. Или, если сохранены имена компонент по умолчания, то по номерам (диапазонам номеров)

Для примера приведу код процедуры, в которой осуществляется выравнивание пар контролов (метка TLabel и поле ввода TEdit) в зависимости от видимости полей ввода. Т.е. на форме, на Panel1 лежат все необходимые поля ввода, но при создании формы в зависимости от параметров часть из них скрывается. Возникла необходимость выровнить остальные по высоте.

procedure TForm1.SetParamPosition;
var
  nTopEd, nTopLb: integer;
  i: integer;
  st: string;
  lb, ed: TControl;
begin
  // устанавливаем видимые компонеты по высоте.
  nTopEd := 22;
  nTopLb := 25;
  for i := 0 to 5 do
  begin
    case i of
      0: st := 'Group';
      1: st := 'SubGroup';
      2: st := 'Code';
      3: st := 'Name';
      4: st := 'Spec';
      5: st := 'Status';
    end;
    lb := TControl(FindComponent('lb' + st));
    ed := Panel1.FindChildControl('ed' + st);
    if Assigned(lb) and Assigned(ed) then
    begin
      lb.Visible := ed.Visible;
      lb.Top := nTopLb;
      ed.Top := nTopEd;
      if ed.Visible then
      begin
        nTopEd := nTopEd + ed.Height + 6;
        nTopLb := nTopLb + ed.Height + 6;
      end;
    end;
  end;
end;

Формат mailto (11.01.2010) #

Время от времени мне необходимо открывать почтовую программу с заполненной темой. Такие задачи возникают и при разработке этого сайта, и в работе - при программировании на Delphi. Наверное почти все знают, что чтобы открыть почтовую программу с адресом надо вызвать "mailto:адрес", но у этой команды несколько больше параметров:

mailto:[ mail address ] [?] [subject=subject] [&cc=mail address] [&bcc=mail address] [&body=message body]

mail address — адрес получателя (несколько адресов разделённых запятой)
subject — тема сообщения
cc — копия (copy to)
bcc — скрытая копия (blind copy to)
body — само сообщение

Перевод Фамилии и Имени в транслитерацию (для банковских карт СберБанка) (09.12.2009) #

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

function BankCardTranslit(sFam, sName: string): string;
var
  aChar:array [char] of string;
  stFamE, stNameE: string;
  
  function GetChar(c: char): string;
  begin
    Result := '';
    if (c = 'Ь') or (c = 'Ъ') then Exit;
    Result := c;
    if not (c in ['A'..'Z', '0'..'9']) then
      Result := aChar[c];
  end;
  
  function ToEnglish(ast: string): string;
  var i: integer;
      st: string;
  begin
    i := 1; Result := '';
    while i <= Length(ast) do
    begin
      if i < Length(ast) then
      begin
        st := ast[i] + ast[i+1];
        if (st = 'КС') then begin Result := Result+'X'; i := i + 2; Continue; end;
        if (st = 'ЬЯ') or (st = 'ИЯ') then begin Result := Result + 'IA'; i := i + 2; Continue; end;
        if (st = 'ИЙ') then begin Result := Result + 'Y'; i := i + 2; Continue; end;
        if (st = 'ЫЙ') then begin Result := Result + 'YY'; i := i + 2; Continue; end;
      end;
      Result := Result + GetChar(ast[i]);
      Inc(i);
    end;
  end;
begin
  sFam := Trim(AnsiUpperCase(sFam));
  sName := Trim(AnsiUpperCase(sName));
  aChar['А'] := 'A';
  aChar['Б'] := 'B';
  aChar['В'] := 'V';
  aChar['Г'] := 'G';
  aChar['Д'] := 'D';
  aChar['Е'] := 'E';
  aChar['Ё'] := 'E';
  aChar['Ж'] := 'ZH';
  aChar['З'] := 'Z';
  aChar['И'] := 'I';
  aChar['Й'] := 'Y';
  aChar['К'] := 'K';
  aChar['Л'] := 'L';
  aChar['М'] := 'M';
  aChar['Н'] := 'N';
  aChar['О'] := 'O';
  aChar['П'] := 'P';
  aChar['Р'] := 'R';
  aChar['С'] := 'S';
  aChar['Т'] := 'T';
  aChar['У'] := 'U';
  aChar['Ф'] := 'F';
  aChar['Х'] := 'KH';
  aChar['Ц'] := 'TS';
  aChar['Ч'] := 'CH';
  aChar['Ш'] := 'SH';
  aChar['Щ'] := 'SHCH';
  aChar['Ъ'] := '';
  aChar['Ы'] := 'Y';
  aChar['Ь'] := '';
  aChar['Э'] := 'E';
  aChar['Ю'] := 'YU';
  aChar['Я'] := 'YA';
  // поиск имен
  if sName = 'АЛЕКСАНДР' then stNameE := 'ALEXANDER';
  if sName = 'ВЯЧЕСЛАВ' then stNameE := 'VJATCHESLAV';
  if sName = 'ВИКТОР' then stNameE := 'VICTOR';
  if sName = 'КУЗЬМА' then stNameE := 'KOUZMA';
  if sName = 'ЛЮБОВЬ' then stNameE := 'LIUBOV';
  if sName = 'ЛЮДМИЛА' then stNameE := 'LIUDMILA';
  if sName = 'НАДЕЖДА' then stNameE := 'NADEZDA';
  if sName = 'ФИЛИПП' then stNameE := 'PHILIPP';
  if sName = 'ЮЛИЯ' then stNameE := 'JULIA';
  if sName = 'ЮРИЙ' then stNameE := 'YURI';
  if sName = 'ЯКОВ' then stNameE := 'IAKOV';

  stFamE := ToEnglish(sFam);
  if (stNameE = '') then stNameE := ToEnglish(sName);
  // Проверка допустимой длины: если фамилия длинная, то берем только первую букву имени
  if (Length(stFamE) + Length(stNameE) + 1) > 19 then stNameE := GetChar(sName[1]);
  Result := stFamE + ' ' + stNameE;
end;

«  ‹  1  2  следующая ›  последняя »

  Вы 21575 посетитель этой странички
с 02 марта 2006 года
© 2000–2018 http://svv-home.ru
О сайте