Страницы

Поиск по вопросам

Показаны сообщения с ярлыком delphi-7. Показать все сообщения
Показаны сообщения с ярлыком delphi-7. Показать все сообщения

воскресенье, 8 марта 2020 г.

Почему при дробных значениях алгоритм не работает?

#delphi #алгоритм #pascal #delphi_7


Задача следующая:


  Ввести целое число a и вывести сумму квадратов всех чисел от 1 до a с шагом 0.1.
(1^2+1.1^2+1.2^2+...+a^2)


Алгоритм решения такой:

result := 0
while (a >= 1) do begin
    result := result + a * a;
    a := a - 0.1;
end;


В конце работы программы результат должен быть:

При a = 1.1 result = 2.21
При a = 3 result = 91.7


Переменная a, в любом случае, при завершении программы должна быть равна 0.9.

Проблема в следующем. При a = 1.1 все верно, но начиная с 1.2 и далее программа выдает
в результате на 1 меньше, чем нужно: a = 3; result = 90.7, при этом, в конце выполнения
программы, a = 0.9(9).

Если модифицировать алгоритм: переменную a после ввода умножать на 10, а затем в
цикле в квадрат возводить не ее, а выражение a / 10, то все начинает работать правильно
(второй строкой в цикле здесь будет: a := a - 1).

В чем может быть проблема?
    


Ответы

Ответ 1



Проблема в том, что числа представлены в двоичном виде. Десятичное значение 0,1 не может быть представлено в двоичном виде конечной дробью, оно равно 0,0001100110011...2 или 0,0(0011)2 в стандартной записи рациональных дробей. У чисел с плавающей точкой с двойной точностью хранится 53 двоичных знака после запятой, бесконечная часть дроби отбрасывается. В результате вместо точного значения 0,1 мы имеем число 0,1000000000000000055511151231257827021181583404541015625. Если вы выведете его на печать с не очень большой точностью, вы получите 0,1, но это только результат округления. При округлении до 17-го знака получается 0,100000000000000001, и последний знак участвует в вычислениях. Поэтому, два раза вычитая из 1,2 значение 0,1, вы получаете число меньшее 1. Простого решения подобных проблем на все случаи жизни не существует. Простейшим в вашем случае будет эмуляция чисел с фиксированной запятой, например так, как вы и сделали. Можно упростить вычисления, посчитав суммы квадратов целых чисел, и разделив результат на 100: a2 + (a + 0.1)2 + (a + 0.2)2 + ... = (10a)2/100 + (10a + 1)2/100 + (10a + 2)2/100 + ... Можно использовать библиотеки, которые обеспечивают работу с рациональными числами, с числами в десятичной системе счисления (как тип decimal в C#), или с числами большой точности.

Есть ли встроенные процедуры сортировок в Delphi?

#delphi #delphi_7


Нужно сделать прогу, которая бы сортировала список по определённому полю. Но писать
самому сортировку лень. Есть ли встроенные процедуры в Delphi?
    


Ответы

Ответ 1



В Delphi есть TList который может хранить произвольные типы данных и позволяет их сортировать. Метод TList.Sort(), в качестве аргумента принимает вашу функцию сортировки. Сортировка списка проводится внутри TList (используется QuickSort), но каждая пара элементов сравнивается, вызывая функцию, которую вы указали для этого метода. Минимальный пример, который показывает как можно отсортировать список по тому или иному полю, просто подставив нужную функцию сортировки: uses Classes, SysUtils; type TListItem = record F1: string; F2: Integer; end; PListItem = ^TListItem; function CompareByF1(Item1, Item2: Pointer): Integer; begin Result := CompareText(PListItem(Item1).F1, PListItem(Item2).F1); end; function CompareByF2(Item1, Item2: Pointer): Integer; begin Result := PListItem(Item1).F2 - PListItem(Item2).F2; end; procedure Test; var I: Integer; VList: TList; VTestItem: PListItem; begin VList := TList.Create; try try // заполнение списка данными for I := 9 downto 0 do begin New(VTestItem); // динамическое выделение памяти для элемента списка VTestItem.F1 := 'TestStr#' + IntToStr(I); VTestItem.F2 := I; VList.Add(VTestItem); end; // тестовая перчать списка ДО сортировки for I := 0 to VList.Count - 1 do begin Writeln(PListItem(VList.Items[I]).F1); end; Writeln; VList.Sort(CompareByF2); // сортировка по нужному нам полю // тестовая перчать списка ПОСЛЕ сортировки for I := 0 to VList.Count - 1 do begin Writeln(PListItem(VList.Items[I]).F1); end; finally for I := 0 to VList.Count - 1 do begin Dispose(VList.Items[I]); // освобождение ранее выделенной памяти end; end; finally VList.Free; end; end; А вот тут: How do I sort a generic list using a custom comparer? есть пример, как сортировать список, в версиях Delphi с поддержкой дженериков.

четверг, 5 марта 2020 г.

Вызов диалога “Обзор папок”

#delphi #delphi_7


Как вызвать точно такое же окно выбора папки?

Использовал эту функцию:

var
  chosenDirectory: string;

SelectDirectory('Выберите папку', '', chosenDirectory);


Как сюда добавить кнопку создания папки?


    


Ответы

Ответ 1



Для Delphi 7 находится вот такое (не очень красивое) решение: uses ShlObj, ActiveX; //global variables var lg_StartFolder: String; //functions //no need to declare these anywhere at the top of the unit function BrowseForFolderCallBack(Wnd: HWND; uMsg: UINT; lParam, lpData: LPARAM): Integer stdcall; begin if uMsg = BFFM_INITIALIZED then SendMessage(Wnd,BFFM_SETSELECTION, 1, Integer(@lg_StartFolder[1])); result := 0; end; function BrowseForFolder(const browseTitle: String; const initialFolder: String =''): String; var browse_info: TBrowseInfo; folder: array[0..MAX_PATH] of char; find_context: PItemIDList; begin FillChar(browse_info, SizeOf(browse_info), #0); lg_StartFolder := initialFolder; browse_info.pszDisplayName := @folder[0]; browse_info.lpszTitle := PChar(browseTitle); browse_info.ulFlags := BIF_RETURNONLYFSDIRS or BIF_EDITBOX or $40; browse_info.hwndOwner := Application.Handle; if initialFolder <> '' then browse_info.lpfn := BrowseForFolderCallBack; find_context := SHBrowseForFolder(browse_info); if Assigned(find_context) then begin if SHGetPathFromIDList(find_context,folder) then result := folder else result := ''; GlobalFreePtr(find_context); end else result := ''; end; Пример использования: procedure TForm4.Button3Click(Sender: TObject); begin BrowseForFolder('Select Dir', 'C:\'); end;

Ответ 2



не уверен на счет Delphi-7, но в более поздних версиях можно было использовать класс StdActns.TBrowseForFolder: procedure TForm1.ChooseDirButtonClick(Sender: TObject); var bff : TBrowseForFolder; begin bff := TBrowseForFolder.create(nil); try bff.Caption := 'Выберите папку'; bff.BrowseOptions := [bifNewDialogStyle]; if bff.Execute() then begin if bff.Folder <> '' then showMessage(bff.Folder); end; finally bff.Free(); end; end;

Ответ 3



Как минимум, начиная с Delphi 2007 (про Delphi 7 не уверен), в юните FileCtrl есть функция SelectDirectory, которая имеет вот такое описание: function SelectDirectory( const Caption: string; const Root: WideString; var Directory: string; Options: TSelectDirExtOpts = [sdNewUI]; Parent: TWinControl = nil ): Boolean; overload; Соответственно, передав опцию sdNewFolder в параметре Options можно добиться того, что вам нужно: uses FileCtrl; ... if SelectDirectory('Выберите папку', '', chosenDirectory, [sdNewFolder, sdNewUI, sdShowEdit, sdShowShares]) then begin ... end;

среда, 5 февраля 2020 г.

Определение положения панели задач Delphi 7

#windows #delphi #delphi_7


Как известно, панель задач можно закрепить слева\справа, снизу\сверху.
У меня панель задач находится сверху и при Form1.Create отображается за ней.
Можно ли в Delphi узнать, в какой из сторон находится панель задач?
    


Ответы

Ответ 1



У объекта Screen есть два свойства - DesktopRect и WorkAreaRect. В первом показываются координаты всего экрана, во втором - координаты "рабочей области" - это как раз экран за минусом таскбара. Если нужно расположить окно так, чтобы оно не перекрывалось с таскбаром, то проверяем координаты прямоугольника окна и корректируем их так, чтобы оно не выходило за пределы WorkAreaRect. В простейшем случае поможет функция IntersectRect(). Вот Вам примерчик, в котором окно ограничивается в размерах, чтобы не вылезать за пределы рабочей области. Работает при любом выравнивании таскбара. procedure TForm1.Button1Click(Sender: TObject); var da, wa, r, clip, me: TRect; s: string; begin da := Screen.DesktopRect; wa := Screen.WorkAreaRect; s := Format('Screen.DesktopRect = { left: %d, top: %d, right: %d, bottom: %d }', [da.Left, da.Top, da.Right, da.Bottom]) + sLineBreak + Format('Screen.WorkAreaRect = { left: %d, top: %d, right: %d, bottom: %d }', [wa.Left, wa.Top, wa.Right, wa.Bottom]); Self.Left := 0; Self.Top := 0; Self.Width := Screen.Width; Self.Height := Screen.Height; Application.ProcessMessages(); ShowMessage(s); // раскомментировать фрагменты, чтобы получить отступы от границ рабочей области clip := Rect(wa.Left {+ 25}, wa.Top {+ 25}, wa.Right {- 25}, wa.Bottom {- 25}); me := Rect(Self.Left, Self.Top, Self.Left + Self.Width, Self.Top + Self.Height); if (IntersectRect(r, clip, me)) then begin Self.Left := r.Left; Self.Width := r.Right - r.Left; Self.Top := r.Top; Self.Height := r.Bottom - r.Top; end; end; Если необходима работа приложения в конфигурациях с несколькими мониторами, обращайтесь к свойству Screen.Monitors: в классе TMonitor имеются свойства BoundsRect и WorkAreaRect, с помощью которых тоже можно делать всё то же самое. P.S.: если всё-таки необходимо узнать выравнивание таскбара: можно заметить, что где у Screen.WorkAreaRect отличается координата (Left/Top/Right/Bottom) от соответствующей координаты в Screen.DesktopRect, там и лежит таскбар :)

Ответ 2



На мой взгляд, эффективнее воспользоваться стандартной функцией API SHAppBarMessage. Без FindWindow нам тоже не обойтись. Вот, набросал консольный пример, пока вспоминал, как работать с этой функцией. Работает в любом положении панели задач, не важно, скрытая она или нет. program taskbarpos; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, Windows, shellapi; function FindTaskBarPos(var ARect: TRect): integer; / найти положение панели задач // ARect - координаты, результат - положение, см. константы ниже var AppData: TAppBarData; begin AppData.Hwnd := FindWindow('Shell_TrayWnd', nil); if AppData.Hwnd = 0 then RaiseLastWin32Error; // на всякий случай :) AppData.cbSize := SizeOf(TAppBarData); if SHAppBarMessage(ABM_GETTASKBARPOS, AppData) = 0 then raise Exception.Create('SHAppBarMessage возвратило ошибку при попытке обращения к позиции Панели задач'); Result := AppData.uEdge; ARect := AppData.rc; end; var tr:TRect; edge:integer; begin edge:=FindTaskBarPos(tr); case edge of ABE_BOTTOM: Writeln('Снизу'); ABE_LEFT: Writeln('Слева'); ABE_RIGHT: Writeln('Справа'); ABE_TOP: Writeln('Сверху'); end; Writeln('Правая координата:' + IntToStr(tr.Right)+' нижняя координата:' + IntToStr(tr.Bottom)); end. tr.Left и tr.Top вернут, соответственно, координаты левой части и верхней.

суббота, 1 февраля 2020 г.

Как сделать “светящиеся” кнопки в Delphi?

#delphi #delphi_7


Чтоб вид был посовременнее, а не квадратное и серое? Я использовал Delphi 7. Может,
для этого нужно использовать другую версию?
    


Ответы

Ответ 1



В 7ке вы можете подключить специальный файл манифеста в приложение, благодаря чему оно будет использовать визуальное оформление операционной системы. Почитать об этом можно например тут: http://www.programmon.ru/Delphi7prof/6.html Кроме того, вы всегда можете купить красивые компоненты типа TMS или DevExpress. Они не так и дороги по сравнению с ценой самого Delphi )

Ответ 2



Для семерки есть и куча бесплатных "красивостей", типа Raize или JVCL библиотек. По мне так Raize выглядит наиболее приятным и современным вариантом, от которого (при грамотном использовании), в глазах рябить точно не будет )))

воскресенье, 26 января 2020 г.

Как сделать HTTPS запрос с защитой от MITM?

#delphi_7 #https #openssl #сертификат


Нужен пример кода для скачивания странички или файла по https протоколу с проверкой
подлиности то есть, что бы в сессию нельзя было вклинится и подделать ответ. Все это
дело будет на делфи 7 но устроит и код на си можно использовать winapi (Win7+) и/или
openSSL dll'ку.
    


Ответы

Ответ 1



Достаточно будет сделать дополнительную проверку публичного ключа сервера. Смысл в том, чтобы клиент был вкурсе, что на стороне сервера нет чужака, т.к. задача MITM - подменить для клиента публичный ключ, которым клиент шифрует pre master secret. Если Вы заранее знаете приватный ключ сервера, то MITM Вам уже не страшен, т.к. вы можете легко проверить, соответствует ли Вашему, заранее известному, приватному ключу пришедший со стороны сервера публичный ключ. Это легко сделать, если взять произвольную строку, закодировать ее публичным ключом, и раскодировать приватным (заранее известным Вам). Если не получили исходную строку, то можете сигнализировать пользователю об атаке MITM и предпринимать соответствующие действия. Код не привожу, т.к. решение слишком специфично и требует на мой взгляд какой-то готовой инфраструктуры, которой у меня сейчас нет. Возможно позднее, когда будет на это время и возможности. Но если Вы предоставите свой код или API, который умеет у Вас мониторить SSL, в частности - пакет Server Certificate то минимальный алгоритм работы с OpenSSL я приведу. В большинстве случаев, приватный ключ Вам доступен не будет. Это логично, на то он и "приватный", чтобы гарантировать безопасность только диалога, а не всей цепочки посредников, так или иначе, вовлеченных в "беседу". Поэтому я приведу еще один оч. хороший вариант, т.н. - сertificate authority. В такой конфигурации, для проверки подлинности сертификата, владелец сервера выдает1 своим пользователям корневой сертификат (CA). Суть этой защиты в том, что любой сертификат сервера подписывается цифровой подписью, которая может быть проверена этим CA. Конечно, OpenSSL имеет и на такой случай API, но я пока не сталкивался с необходимостью такой проверки, поэтому пока не подскажу с кодом, но его можно легко нагуглить по ключевым словам openssl api verify ca. Более простой вариант - воспользоваться openssl напрямую: $ openssl verify -verbose -CAfile cacert.pem server.crt server.crt: OK но это потребует установленных бинарников openssl на стороне клиента. 1 Сертификат CA передается клиенту любым доступным способом, например: высылается по почте, или выкладывается в публичный доступ на сайте владельца, или устанавливается в хранилище сертификатов на стороне клиента через инсталятор - вариантов масса, главное - чтобы он был доступен клиенту в момент установки соединения с сервером. Поскольку ответ стал жертвой модераторского произвола, и все мои комментарии кто-то заботливо выпилил в чат, оставив только "правильные". привожу основные доводы в самом ответе: Этот изувеченный модератором мой второй ответ. Мой первый ответ был дан, чтобы объяснить, как приватный ключ защищает от MITM (и этот ответ модератор совершенно необдуманно слил со вторым). Мой второй ответ был дан чтобы показать способ, придуманный специально для предотвращаения MITM. Дискуссия по поводу неправильных действий модератора на мете. Дискуссия по аналогичному вопросу с общей меты (благодарю @Саша Черных за информацию) Надеюсь, здравый смысл участников сообщества победит неквалифицированное поведение администрации.

воскресенье, 12 января 2020 г.

Можно ли одновременно изменить размеры компонентов внутри компонента при входе в полноэкранный режим?

#delphi #delphi_7





Возможно ли одновременно расширить все компоненты при развертывании в полноэкранный
режим, находящиеся в ScrollBox, и при свертывании вернуть размеры? Знаю, как прописать
для одного, но компонентов там много.
    


Ответы

Ответ 1



Установите Anchors контролов [право, лево, верх] и они сами расширятся. Обратите внимание на предложение @kami.

понедельник, 6 января 2020 г.

Пишу скринсейвер для Windows. Как показать окно с настройками?

#delphi #windows #delphi_7 #хранитель_экрана


Есть экранная заставка(.scr) написанная на Delphi. 2 формы: 1 форма для анимации,
2 форма для настроек. По комбинации клавиш вызывается вторая форма с настройками. При
нажатии на "Параметры.." в Параметрах экранной заставки, включается анимация (1 форма),
а я бы хотел, чтоб появлялась вторая форма с настройками. 

 

Как это сделать? 
    


Ответы

Ответ 1



При нажатии на кнопку "Параметры" заставка запускается с параметром /c, где также в передается значение дескриптора окна (hwnd), поверх которого следует показывать форму параметров. Итого вид: /c:HWND. Поэтому ваша задача сводится у к тому, чтобы при запуске приложения в dpr проверить paramCount и переданный параметр. Еще может быть параметр /p HWND - это превью на форме выбора заставки, здесь передается дескриптор панели предпросмотра на форме и /s это предпросмотр в полном экране, а /S это автоматический запуск. возможно, по этой ссылке еще что-нибудь полезное найдете

понедельник, 23 декабря 2019 г.

Меняю цвет заголовков и оглавления, но когда в Word нажимаю “обновить оглавление”, цвет сбрасывается

#delphi #delphi_7 #com #ms_word


У меня есть кнопка, при нажатии на которую создаются пара заголовков, а потом, на
их основе создается оглавление. Я меняю цвет заголовков, и меняю цвет оглавления, но
потом, когда в файле Word нажимаю "обновить оглавление", установленный цвет оглавления
сбрасывается. Как сделать так, чтобы цвет оглавления не сбрасывался, а оставался?

procedure TForm1.Button1Click(Sender: TObject);
var
  wrd:OleVariant;
  StartRange, tableOfContentsRange: Variant;
  tableOfContents: Variant;
begin
  wrd:=CoWordApplication.Create;
  wrd.Visible:=true;
  wrd.documents.add(EmptyParam,EmptyParam, EmptyParam,EmptyParam);
  wrd.Selection.Style := 'Заголовок 1';
  wrd.Selection.Font.Color := RGB(255,0,0);
  wrd.Selection.Text:='Первый Заголовок';
  wrd.Selection.MoveDown;
  wrd.Selection.TypeParagraph;
  wrd.Selection.Style := 'Заголовок 1';
  wrd.Selection.Font.Color := RGB(255,0,0);
  wrd.Selection.Text:='Второй заголовок';
  wrd.Selection.MoveDown;
  wrd.Selection.TypeParagraph;

  StartRange := wrd.ActiveDocument.Range(0, 0);
  StartRange.Font.Color:=RGB(0,255,0);
  tableOfContents := wrd.ActiveDocument.TablesOfContents.Add(StartRange);
  tableOfContentsRange := tableOfContents.Range;
  tableOfContentsRange.Collapse;
  tableOfContentsRange.Select;
  wrd.Selection.TypeText('Оглавление' + #13);
  tableOfContents.Update;
end;

    


Ответы

Ответ 1



Все изменения, которые представлены в Вашем коде, применяются непосредственно к тексту. В вашем случае нужно изменять данные непосредственно у стилей, которым предстоит отображать оглавление (поскольку обновление оглавления практически "создает" его заново). Обратите внимание, что стили оглавления и стили основного текста - это разные вещи (хоть и могут ссылаться друг на друга), поэтому изменение одного скорее всего ни коим образом не повлияет на другое. Ниже - переделанный метод с достаточно подробными комментариями: procedure TForm8.btn1Click(Sender: TObject); var wrd, Document: OleVariant; Selection: Variant; tableOfContents: Variant; TOC1Style: OleVariant; begin // общий принцип - при работе с Word и другими приложениями из MS Office, // если какой-либо объект используется более 1 раза, то лучше // завести для него отдельную переменную и работать уже с ней. // иначе конструкции типа // wrd.ActiveDocument.ActiveWindow.Selection.блабла1 // wrd.ActiveDocument.ActiveWindow.Selection.блабла2 // будут приводить к тому, что КАЖДЫЙ раз сперва будет вызываться wrd на предмет получения ActiveDocument, // потом ActiveDocument чтобы получить ActiveWindow ... ну и так далее. // Всё это, получаемое многократно, может замедлить и без того не быструю работу с MS Office. // Помимо прочего это в некоторой степени ограждает от действий пользователя // к примеру, пока работает ваша программа, пользователь может клацнуть по другому документу // соответственно wrd.ActiveDocument будет уже указывать не на нужный нам документ. wrd := CoWordApplication.Create; wrd.Visible := true; Document := wrd.documents.add; Selection := Document.ActiveWindow.Selection; Selection.Style := wdStyleHeading1; // для установки стилей лучше использовать // вот такие встроенные константы. Иначе есть риск напороться на иноязычный // Office, в котором "верхний" стиль будет называться к примеру 'Header 1' Selection.Font.Color := wdColorRed; Selection.Text := 'Первый Заголовок'; Selection.MoveDown; Selection.TypeParagraph; Selection.Style := wdStyleHeading1; Selection.Font.Color := wdColorRed; Selection.Text := 'Второй заголовок'; Selection.MoveDown; Selection.TypeParagraph; // меняем стиль "Оглавление 1". В этом случае также лучше пользоваться константами. TOC1Style := Document.Styles.Item(wdStyleTOC1); TOC1Style.AutomaticallyUpdate := true; TOC1Style.Font.Color := wdColorGreen; // хочу зелененькое :) TOC1Style.Font.Italic := true; TOC1Style.Font.Bold := true; // создаем содержание tableOfContents := Document.TablesOfContents.add( Range := Document.Range(0, 0), RightAlignPageNumbers := true, UseHeadingStyles := true, UpperHeadingLevel := 1, // применяя стили заголовков от 1 (изменен нами) LowerHeadingLevel := 3, // до 3 IncludePageNumbers := true, AddedStyles := '', UseHyperlinks := true, HidePageNumbersInWeb := true, UseOutlineLevels := true); Document.Range(0, 0).Select; Selection.TypeText('Оглавление' + #13#10); Selection.Move(wdStory, 1); tableOfContents.Update; // это излишне, содержание после создания // обновляется автоматически. Вот если бы мы после его создания // изменили основное содержимое, тогда да, обновить содержание не помешает. end;

суббота, 21 декабря 2019 г.

Закрытые текущего окна в Delphi 7 и открытие следующего по нажатию кнопки

#delphi #delphi_7


Как при нажатии на кнопку (Log In), закрыть окно авторизации (Form1) и открыть основную
программу (Form2)?

Код:

procedure TForm1.LogInButtonClick(Sender: TObject);
var
  pw:string;
begin
  pw := 'NXJ';
  if PasswordEdit.Text = pw then
  begin
    Form1.Close;     {вот эта строка полностью закрывает программу}
    Form2.Show();
  end
  else
    ShowMessage('Wrong password');
end;

    


Ответы

Ответ 1



Откройте Ваш .dpr файл (Project - View Source) и посмотрите порядок создания форм. Форма, которая создается первой с помощью Application.CreateForm, становится главной формой приложения. Ее закрытие ведет к выходу из программы. Поменяйте местами строчки, создающие формы.

Ответ 2



Судя по всему, вы всё-таки основной формой называете не родительскую, а ту, в которой затем вы будете проводить основные действия. Тогда стоит сделать это по-другому: <...> begin Form1.Visible:=false; Form2.Show; end <...> Что вы потом будете делать с Form1 - уже на ваше усмотрение. Делайте это из методов Form2, но, в любом случае, вызов Close для родительской формы вызовет завершение программы. Либо вам стоит пересмотреть подход и вызывать форму с логином из родительского окна, тогда затем можете смело ее закрывать.

среда, 18 декабря 2019 г.

Как работает try… except?

#delphi #исключения #delphi_7


Как вообще работает этот блок? Нашел простой код его применения, привязал к кнопке,
но он мне выдает ошибку, что-то там by zero (я так понял типа на ноль нельзя делить)
и приходиться сбрасывать работу программы...

var
    number, zero: Integer;
begin
    // Попытка делить целое число на ноль - чтобы поднять исключение
    try
        zero := 0;
        number := 1 div zero;
        ShowMessage('number / zero = '+IntToStr(number));
    except
        ShowMessage('Неизвестная ошибка');
    end;
end;

    


Ответы

Ответ 1



Это очень полезный инструмент ,и, кроме того, очень распространенный. Не знаю, как вы искали в гугле, если ничего не нашли на эту тему... Конструкция Try -> EXCEPT -> END управляет поведением возможной исключительной ситуацией, которая может возникнуть в секции "TRY". Если такая возникает, то выполнение кода в секции "TRY" прекращается и моментально перепрыгивает в начало секции "EXCEPT" и код, находящийся там, выполняется до конца, т.е до "END". Рассмотрим пример обработки исключительной ситуацией "EZeroDevide"(деление на ноль) : ... var a: integer; ... begin try a := 1/0; except on EZeroDivide do showmessage('Divide by zero not allowed!'); // обработка КОНКРЕТНОЙ исключителной ситуации end; end; Также существует еще одна конструкция обработки исключительных ситуаций: TRY -> FINALLY -> END Данный блок функционирует немного иначе: если в секции "TRY" возникнет исключительная ситуация, то выполнение кода оставновится в этой секции и перепрыгнет в секцию "FINALLY". Но даже если никакой исключительной ситуации не возникнет, то в конце выполнения кода в "TRY", секция "FINALLY" все равно будет выполнена. Эту конструкцию уместно применять, если в конце работы надо произвести операции по, например, освобождению памяти. Пример: 1 случай: ... try a := 1/0; finally showmessage('Divide by zero not allowed!'); end; ... 2 случай: ... try a := 1/1; finally showmessage('Divide by zero not allowed!'); end; ... В обоих случаях будет выведено сообщение.

среда, 19 июня 2019 г.

Можно ли одновременно изменить размеры компонентов внутри компонента при входе в полноэкранный режим?


Возможно ли одновременно расширить все компоненты при развертывании в полноэкранный режим, находящиеся в ScrollBox, и при свертывании вернуть размеры? Знаю, как прописать для одного, но компонентов там много.


Ответ

Установите Anchors контролов [право, лево, верх] и они сами расширятся.
Обратите внимание на предложение @kami.

вторник, 18 июня 2019 г.

Создание экземпляра класса, независимого от других объектов

Здравствуйте. Создаю экземпляр по такой схеме:
type TExercise = object;
var TMyExercise: TExercise;
Получается что я могу не объявлять переменные на форме для создания экземпляра класса и обращаться к объекту без ссылок на форму(если бы я создал из нее). Так же это позволяет мне иметь единственный экземпляр, в котором хранятся нужные мне данные, используемые несколькими формами.
Но так создают из-за обратной совместимости, судя по справке.
Есть ли другой способ создавать независимый от других объектов класс в Borland Delphi 7?


Ответ

В Delphi обычно не принято называть переменные с большой буквой T в начале, поскольку такая буква в начале литерала обычно говорит о том, что это тип (от англ. type, например: TExercise, TObject, TStringList и т.д.). Так удобнее читать код, даже свой :)
Обычно класс объявляется, например, так:
TExercise = class(TObject) private FMyInt: Integer; FMyString: string; public property Int: Integer read FMyInt write FMyInt; property Str: string read FMyString; // только для чтения end;
Для того, чтобы создать экземпляр класса, нужно вызвать его конструктор:
MyExercise := TExercise.Create;
Когда работа с объектом окончена и он больше не нужен, обязательно нужно освободить память занимаемую объектом:
MyExercise.Free;
Иначе будут утечки памяти и она рано или поздно закончится.

Для создания "по-быстрому" независимого глобального объекта для использования на разных формах можно сделать так:
unit UnitExercise;
interface
type TExercise = class(TObject) private MyInt: Integer; MyString: string; public property Int: Integer read MyInt write MyInt; property Str: string read MyString; // только для чтения end;
var MyExercise: TExercise;
implementation
initialization MyExercise := TExercise.Create;
finalization MyExercise.Free;
end.
И добавить модуль UnitExercise в список используемых модулей (uses) в те модули, в которых предполагается использование этого объекта.
Однако, в такой конструкции есть недостаток: вы практически не контролируете порядок создания и уничтожения таких объектов (если их много, они в разных модулях и еще зависят друг от друга), что может быть актуально для больших проектов. Управлять порядком вызова секций initialization и finalization можно, меняя порядок объявления модулей в проекте и его модулях, но это очень сложная эквилибристика. Если интересно, то это тема для отдельного вопроса.

воскресенье, 26 мая 2019 г.

Почему при дробных значениях алгоритм не работает?

Задача следующая:
Ввести целое число a и вывести сумму квадратов всех чисел от 1 до a с шагом 0.1. (1^2+1.1^2+1.2^2+...+a^2)
Алгоритм решения такой:
result := 0 while (a >= 1) do begin result := result + a * a; a := a - 0.1; end;
В конце работы программы результат должен быть:
При a = 1.1 result = 2.21 При a = 3 result = 91.7
Переменная a, в любом случае, при завершении программы должна быть равна 0.9
Проблема в следующем. При a = 1.1 все верно, но начиная с 1.2 и далее программа выдает в результате на 1 меньше, чем нужно: a = 3; result = 90.7, при этом, в конце выполнения программы, a = 0.9(9)
Если модифицировать алгоритм: переменную a после ввода умножать на 10, а затем в цикле в квадрат возводить не ее, а выражение a / 10, то все начинает работать правильно (второй строкой в цикле здесь будет: a := a - 1).
В чем может быть проблема?


Ответ

Проблема в том, что числа представлены в двоичном виде. Десятичное значение 0,1 не может быть представлено в двоичном виде конечной дробью, оно равно 0,0001100110011...2 или 0,0(0011)2 в стандартной записи рациональных дробей.
У чисел с плавающей точкой с двойной точностью хранится 53 двоичных знака после запятой, бесконечная часть дроби отбрасывается. В результате вместо точного значения 0,1 мы имеем число 0,1000000000000000055511151231257827021181583404541015625. Если вы выведете его на печать с не очень большой точностью, вы получите 0,1, но это только результат округления. При округлении до 17-го знака получается 0,100000000000000001, и последний знак участвует в вычислениях. Поэтому, два раза вычитая из 1,2 значение 0,1, вы получаете число меньшее 1.
Простого решения подобных проблем на все случаи жизни не существует. Простейшим в вашем случае будет эмуляция чисел с фиксированной запятой, например так, как вы и сделали.
Можно упростить вычисления, посчитав суммы квадратов целых чисел, и разделив результат на 100:
a2 + (a + 0.1)2 + (a + 0.2)2 + ... =
(10a)2/100 + (10a + 1)2/100 + (10a + 2)2/100 + ...
Можно использовать библиотеки, которые обеспечивают работу с рациональными числами, с числами в десятичной системе счисления (как тип decimal в C#), или с числами большой точности.

Есть ли встроенные процедуры сортировок в Delphi?

Нужно сделать прогу, которая бы сортировала список по определённому полю. Но писать самому сортировку лень. Есть ли встроенные процедуры в Delphi?


Ответ

В Delphi есть TList который может хранить произвольные типы данных и позволяет их сортировать. Метод TList.Sort(), в качестве аргумента принимает вашу функцию сортировки. Сортировка списка проводится внутри TList (используется QuickSort), но каждая пара элементов сравнивается, вызывая функцию, которую вы указали для этого метода.
Минимальный пример, который показывает как можно отсортировать список по тому или иному полю, просто подставив нужную функцию сортировки:
uses Classes, SysUtils;
type TListItem = record F1: string; F2: Integer; end; PListItem = ^TListItem;
function CompareByF1(Item1, Item2: Pointer): Integer; begin Result := CompareText(PListItem(Item1).F1, PListItem(Item2).F1); end;
function CompareByF2(Item1, Item2: Pointer): Integer; begin Result := PListItem(Item1).F2 - PListItem(Item2).F2; end;
procedure Test; var I: Integer; VList: TList; VTestItem: PListItem; begin VList := TList.Create; try try // заполнение списка данными for I := 9 downto 0 do begin New(VTestItem); // динамическое выделение памяти для элемента списка VTestItem.F1 := 'TestStr#' + IntToStr(I); VTestItem.F2 := I; VList.Add(VTestItem); end;
// тестовая перчать списка ДО сортировки for I := 0 to VList.Count - 1 do begin Writeln(PListItem(VList.Items[I]).F1); end; Writeln;
VList.Sort(CompareByF2); // сортировка по нужному нам полю
// тестовая перчать списка ПОСЛЕ сортировки for I := 0 to VList.Count - 1 do begin Writeln(PListItem(VList.Items[I]).F1); end; finally for I := 0 to VList.Count - 1 do begin Dispose(VList.Items[I]); // освобождение ранее выделенной памяти end; end; finally VList.Free; end; end;
А вот тут: How do I sort a generic list using a custom comparer? есть пример, как сортировать список, в версиях Delphi с поддержкой дженериков.

среда, 22 мая 2019 г.

Вызов диалога “Обзор папок”

Как вызвать точно такое же окно выбора папки?
Использовал эту функцию:
var chosenDirectory: string;
SelectDirectory('Выберите папку', '', chosenDirectory);
Как сюда добавить кнопку создания папки?


Ответ

Для Delphi 7 находится вот такое (не очень красивое) решение:
uses ShlObj, ActiveX;
//global variables var lg_StartFolder: String;
//functions //no need to declare these anywhere at the top of the unit function BrowseForFolderCallBack(Wnd: HWND; uMsg: UINT; lParam, lpData: LPARAM): Integer stdcall; begin if uMsg = BFFM_INITIALIZED then SendMessage(Wnd,BFFM_SETSELECTION, 1, Integer(@lg_StartFolder[1])); result := 0; end;
function BrowseForFolder(const browseTitle: String; const initialFolder: String =''): String; var browse_info: TBrowseInfo; folder: array[0..MAX_PATH] of char; find_context: PItemIDList; begin FillChar(browse_info, SizeOf(browse_info), #0); lg_StartFolder := initialFolder; browse_info.pszDisplayName := @folder[0]; browse_info.lpszTitle := PChar(browseTitle); browse_info.ulFlags := BIF_RETURNONLYFSDIRS or BIF_EDITBOX or $40; browse_info.hwndOwner := Application.Handle;
if initialFolder <> '' then browse_info.lpfn := BrowseForFolderCallBack; find_context := SHBrowseForFolder(browse_info); if Assigned(find_context) then begin if SHGetPathFromIDList(find_context,folder) then result := folder else result := ''; GlobalFreePtr(find_context); end else result := ''; end;
Пример использования:
procedure TForm4.Button3Click(Sender: TObject); begin BrowseForFolder('Select Dir', 'C:\'); end;

вторник, 12 февраля 2019 г.

Пишу скринсейвер для Windows. Как показать окно с настройками?

Есть экранная заставка(.scr) написанная на Delphi. 2 формы: 1 форма для анимации, 2 форма для настроек. По комбинации клавиш вызывается вторая форма с настройками. При нажатии на "Параметры.." в Параметрах экранной заставки, включается анимация (1 форма), а я бы хотел, чтоб появлялась вторая форма с настройками.

Как это сделать?


Ответ

При нажатии на кнопку "Параметры" заставка запускается с параметром /c, где также в передается значение дескриптора окна (hwnd), поверх которого следует показывать форму параметров. Итого вид: /c:HWND. Поэтому ваша задача сводится у к тому, чтобы при запуске приложения в dpr проверить paramCount и переданный параметр. Еще может быть параметр /p HWND - это превью на форме выбора заставки, здесь передается дескриптор панели предпросмотра на форме и /s это предпросмотр в полном экране, а /S это автоматический запуск.
возможно, по этой ссылке еще что-нибудь полезное найдете

среда, 12 декабря 2018 г.

Как работает try… except?

Как вообще работает этот блок? Нашел простой код его применения, привязал к кнопке, но он мне выдает ошибку, что-то там by zero (я так понял типа на ноль нельзя делить) и приходиться сбрасывать работу программы...
var number, zero: Integer; begin // Попытка делить целое число на ноль - чтобы поднять исключение try zero := 0; number := 1 div zero; ShowMessage('number / zero = '+IntToStr(number)); except ShowMessage('Неизвестная ошибка'); end; end;


Ответ

Это очень полезный инструмент ,и, кроме того, очень распространенный. Не знаю, как вы искали в гугле, если ничего не нашли на эту тему...
Конструкция
Try -> EXCEPT -> END
управляет поведением возможной исключительной ситуацией, которая может возникнуть в секции "TRY". Если такая возникает, то выполнение кода в секции "TRY" прекращается и моментально перепрыгивает в начало секции "EXCEPT" и код, находящийся там, выполняется до конца, т.е до "END". Рассмотрим пример обработки исключительной ситуацией "EZeroDevide"(деление на ноль) :
... var a: integer; ... begin try a := 1/0; except on EZeroDivide do showmessage('Divide by zero not allowed!'); // обработка КОНКРЕТНОЙ исключителной ситуации end; end;
Также существует еще одна конструкция обработки исключительных ситуаций:
TRY -> FINALLY -> END
Данный блок функционирует немного иначе: если в секции "TRY" возникнет исключительная ситуация, то выполнение кода оставновится в этой секции и перепрыгнет в секцию "FINALLY". Но даже если никакой исключительной ситуации не возникнет, то в конце выполнения кода в "TRY", секция "FINALLY" все равно будет выполнена. Эту конструкцию уместно применять, если в конце работы надо произвести операции по, например, освобождению памяти. Пример:
1 случай:
... try a := 1/0; finally showmessage('Divide by zero not allowed!'); end; ...
2 случай:
... try a := 1/1; finally showmessage('Divide by zero not allowed!'); end; ...
В обоих случаях будет выведено сообщение.