пятница, 21 марта 2025 г.

Access violation at address XXXXYYYY in module 'rtl260.bpl'

При клике по гриду (DBGridEh) словил ошибку Access Violation. Причем с проектом особо ничего и не делал, обновил из гита да и всё. Ну может ещё пересобрал. А потом вдруг случайно обнаружил, что щелчок по гриду на любой из форм проекта приводит к такой ошибке.
Судя по стеку вызовов крашился именно RTL делфы. Переустановка компонентов (в моем случае это EhLib, ODAC, FastReport, ODACFR) не помогла. Тем не менее мне удалось найти решение: нужно было удалить строчку EhLib 9.3 DataDrivers for ADO из файла .dproj, которая исключала пакет Эхлиба для АДО

вторник, 25 октября 2022 г.

Установка SDK Linux

Чтобы настроить компиляцию проекта из Delphi 10.3 в Linux нужно:

1) взять соответствующий PAServer (у меня находится в C:\Program Files (x86)\Embarcadero\Studio\20.0\PAServer\LinuxPAServer20.0.tar)
2) перенести LinuxPAServer20.0.tar на машину с линуксом (в моём случае это виртуалка с Ubuntu 21.04), распаковать архив
3) с помощью BASH перейти в папку с распакованным архивом и запустить paserver
4) в IDE выбрать пункт меню Tools -> Options -> Deployment -> Add, заполнить "Profile name" и "Platform", нажать Next
5) заполнить ip-адрес машины с линуксом (в моем случае это 192.168.1.37), проверить соединение, нажав "Test connection"
6) в IDE выбрать пункт меню Tools -> Options -> SDK Manager -> Add, заполнить соответствующие поля
Затем начнется установка SDK для компиляции под Linux. Когда она будет закончена, появится соответствующее сообщение. Виртуальная машина с линуксом больше не нужна.

воскресенье, 22 мая 2022 г.

Утечка FastReport

Как же мне надоело, что в сторонних продуктах для Delphi постоянно всплывают ошибки! Когда я работал с экосистемой .NET, тоже сталкивался с ошибками, но гораздо (!) меньше. Интересно, как обстоят дела с экосистемой Java?
За последний месяц ине пришлось исправить одну ошибку в модулей сериализации, одну в библиотеке EhLib, а теперь вот проблемы с FastReport!
Работая с отчетом, содержимое которого экспортируется в Excel, я заметил, что после закрытия программы FastMM ругается на утечки памяти:
FF-FMM

Я решил попробовать поискать в исходниках по строчке "TfrxIEMStyle.Create" и вот что обнаружилось
Resize-of-FF-find

В конструкторе этого объекта как раз выделялась память, которая очень была похожа по описанию, которое выдал FastMM
FF-const

Потом я просто стал проверять, все найденные участки кода и в самом последнем обнаружил выделение памяти, которое впоследствии не освобождалось
Resize-of-FF-bug

Вылечилось это элементарным добавлением вызова деструктора
Resize-of-FF-desct

Ну и конечно же надо пересобрать компоненты FastRerpot'а с помощью их утилиты
7

понедельник, 17 мая 2021 г.

Проблема с IP*Works

Заметил, что при скачивании вложенных файлов из письма (которое в моем сервисе реализовано через Ip*Works, версия 20.0.7447) иногда имя вложенного файла определяется неверно. Каждый раз как "xls". Если смотреть содержимое письма в Outlooke, то там указано другое имя вложения.
Исследование под дебагером подтвердило, что проблема в стороннем софте, т.е. в компоненте Ip*Works
Далее я экспортировал письмо из Аутлука в файл и открыл его в HEX-редакторе.
В текстовой части (справа) мне удалось найти заголовки вложения. Они были закодированы по BASE-64. Когда я их раскодировал, оказалось, что это и было необходимое мне имя вложения.
Написав в тех. поддержку, я стал обдумывать как обойти эту проблему. Первая мысль была самостоятельно парсить заголовки и вместо "name" брать "filename". Но тут подоспел ответ от тех. поддержки Ip*Works, предлагали добавить магическую строчку ".Config('UseAttachmentNameParam=false');"

Таким образом, демонстрационный пример для скачивания вложений в письме с помощью Ip*Works выглядит так:

program Demo;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  ipwCore,
  ipwIMAP,
  ipwtypes,
  System.SysUtils;


var
  ImapHandle:  TipwIMAP;

procedure InitImap();
begin
  ImapHandle.MailServer := 
  ImapHandle.MailPort   := 
  ImapHandle.User       := 
  ImapHandle.Password   := 
  ImapHandle.SSLEnabled := False;
  ImapHandle.AutoDecodeParts := True;
  ImapHandle.Config('UseAttachmentNameParam=false');
end;

begin
  ImapHandle := TipwIMAP.Create(nil);
  try
    try
      InitImap();
      ImapHandle.Connect();
      ImapHandle.Mailbox := 'INBOX';
      ImapHandle.SelectMailbox();

      for var I := ImapHandle.MessageCount - 1 downto ImapHandle.MessageCount - 1 do
      begin
        ImapHandle.MessageSet := i.ToString();
        ImapHandle.FetchMessageInfo();

        WriteLn('Id         : ' + ImapHandle.MessageId);
        WriteLn('From       : ' + ImapHandle.MessageFrom);
        WriteLn('Subject    : ' + ImapHandle.MessageSubject);
        WriteLn('Flags      : ' + ImapHandle.MessageFlags);
        WriteLn('ContentType: ' + ImapHandle.MessageContentType);
        WriteLn('Size       : ' + ImapHandle.MessageSize.ToString);
        WriteLn('');

        for var j := 0 to ImapHandle.MessageParts.Count - 1 do
        begin
          if ImapHandle.MessageParts[j].Filename <> '' then
          begin
            WriteLn('    Id          : ' + ImapHandle.MessageParts[j].Id);
            WriteLn('    ContentId   : ' + ImapHandle.MessageParts[j].ContentId);
            WriteLn('    Filename    : ' + ImapHandle.MessageParts[j].Filename);
            WriteLn('    ContentType : ' + ImapHandle.MessageParts[j].ContentType);
            WriteLn('    Size        : ' + ImapHandle.MessageParts[j].Size.ToString);
            WriteLn('');
          end
        end
      end;

    except
      on E: Exception do
        Writeln(E.ClassName, ': ', E.Message);
    end;

  finally
    FreeAndNil(ImapHandle);
  end;

  Write(#13#10 + 'press any key to continue...');
  ReadLn;
end.

среда, 12 мая 2021 г.

Сборка OpenCV

0) Проблема: требуется установить opencv 2.4.13 и настроить для работы в VS 2017 

(в дистрибутиве OpenCV собранные бинарники для VC11, VC12 не прилинкуются в новой студии, значит надо собирать из исходников)

1) Качаем последний cmake с сайта https://cmake.org/download/

https://github.com/Kitware/CMake/releases/download/v3.18.0/cmake-3.18.0-win64-x64.msi

2) Устанавливаем cmake в каталог студии (у меня там стоял предыдущий cmake версии 3.12.0, поставленный инсталлятором),

0 просто заменяя папку CMake (если такой папки нет, то можно ставить куда угодно)

C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\CommonExtensions\Microsoft\CMake

3) Качаем opencv-2.4.13.exe и распаковываем в C:/opencv-2-4-13 

4) Запускаем "Командная строка для разработчика VS 2017" (если CMake был поставлен в другую папку, то надо в Path прописать до неё путь)

5) Набираем 

> cmake --version

Должно выдать 

> cmake version 3.18.0

6) Проверяем генераторы, набираем

> cmake -G

Должно выдать полный список генераторов. Поскольку у нас VS 2017 и Win32, то там это должно быть

(за подробностями в документацию https://cmake.org/cmake/help/v3.18/generator/Visual%20Studio%2015%202017.html)

7) Переходим в каталог C:/opencv2/sources, там должен быть файл CMakeLists.txt, набираем (если хотим собрать Win32 для opencv)

> cmake . -G "Visual Studio 15 2017" -A Win32

8) В студии открыть OpenCV.sln, пересобрать проект ALL_BUILD (Win32 Debug)

9) Создать 

C:\opencv-2-4-13\build\Debug\Win32\bin

C:\opencv-2-4-13\build\Debug\Win32\lib

C:\opencv-2-4-13\build\include

10) Скопировать в эти каталоги содержимое

C:\opencv-2-4-13\build\Debug\Win32\bin:

C:\opencv-2-4-13\sources\bin\Debug

C:\opencv-2-4-13\build\Debug\Win32\lib:

C:\opencv-2-4-13\sources\lib\Debug

C:\opencv-2-4-13\build\include:

C:\opencv-2-4-13\sources\include

C:\opencv-2-4-13\sources\modules\calib3d\include

C:\opencv-2-4-13\sources\modules\contrib\include

C:\opencv-2-4-13\sources\modules\photo\include

(Если прикомпиляции не будут найдены ещё какие-то файлы .h или .hpp надо их найти и прописать сюда путь до них)

11) Создать пустой проект С++, добавить новый файл main.cpp

12) Внутри файла:

#include <opencv2\opencv.hpp>
using namespace cv;
int main()
{
	namedWindow("Win");
	waitKey(0);
	return 0;
}

13) Правой кнопкой мыши по проекту

Проект -> Свойства -> Каталоги VC++

Каталоги исполняемых файлов -> C:\opencv-2-4-13\build\Debug\Win32\bin

Включаемые каталоги -> C:\opencv-2-4-13\build\include

Каталоги библиотек -> C:\opencv-2-4-13\build\Debug\Win32\lib

Компоновщик

Общие -> Дополнительные каталоги библиотек -> C:\opencv-2-4-13\build\Debug\Win32\lib

Ввод -> Дополнительные зависимости -> указываем всё, что есть в /lib, а именно:

opencv_calib3d2413d.lib

opencv_contrib2413d.lib

opencv_core2413d.lib

opencv_features2d2413d.lib

opencv_flann2413d.lib

opencv_gpu2413d.lib

opencv_haartraining_engined.lib

opencv_highgui2413d.lib

opencv_imgproc2413d.lib

opencv_legacy2413d.lib

opencv_ml2413d.lib

opencv_nonfree2413d.lib

opencv_objdetect2413d.lib

opencv_ocl2413d.lib

opencv_photo2413d.lib

opencv_stitching2413d.lib

opencv_superres2413d.lib

opencv_ts2413d.lib

opencv_video2413d.lib

opencv_videostab2413d.lib

События сборки -> События после сборки -> Командная строка -> xcopy /y /d "C:\opencv-2-4-

13\build\Debug\Win32\bin\*.dll" "$(OutDir)"

14) Проверка

#include <opencv2\opencv.hpp>
using namespace cv;
int main()

using namespace cv;
using namespace std;

int main()
{
	namedWindow("Win");
	waitKey(0);
	// задаём высоту и ширину картинки
	int height = 620;
	int width = 440;
	// задаём точку для вывода текста
	CvPoint pt = cvPoint(height / 4, width / 2);
	// Создаёи 8-битную, 3-канальную картинку
	IplImage* hw = cvCreateImage(cvSize(height, width), 8, 3);
	// заливаем картинку чёрным цветом
	cvSet(hw, cvScalar(0, 0, 0));
	// инициализация шрифта
	CvFont font;
	cvInitFont(&font, CV_FONT_HERSHEY_COMPLEX, 1.0, 1.0, 0, 1, CV_AA);
	// используя шрифт выводим на картинку текст
	cvPutText(hw, "OpenCV Step By Step", pt, &font, CV_RGB(150, 0, 150));
	// создаём окошко
	cvNamedWindow("Hello World", 0);
	// показываем картинку в созданном окне
	cvShowImage("Hello World", hw);
	// ждём нажатия клавиши
	cvWaitKey(0);
	// освобождаем ресурсы
	cvReleaseImage(&hw);
	cvDestroyWindow("Hello World");
	//cout << "\r\npress any key to continue...";
	//cin.get();
	return 0;
}

вторник, 12 января 2021 г.

Создаем системный хинт

Начиная с Delphi 2009 у компонента TEdit появилось замечательное булевое свойство "NumbersOnly". Его роль понятна из названия - ограничения вводимых символов (только цифры). При попытке ввести иной символ всплывает системный хинт:


Данный хинт является системным, т.е. генерируется самой ОС Windows. Если мы хотим повесить такой хинт на определенные события, придётся создавать его "вручную". Т.е. с помощью API-функций для работы с окнами.

Создадим три процедуры: для создания хинта, для его показа и для сокрытия/удаления:

const
  TOOLTIPS_CLASS    = 'tooltips_class32';

  TTS_ALWAYSTIP     = $01;
  TTS_NOPREFIX      = $02;
  TTS_BALLOON       = $40;

  TTF_SUBCLASS      = $0010;
  TTF_TRACK         = $0020;
  TTF_TRANSPARENT   = $0100;
  TTF_CENTERTIP     = $0002;

  TTM_ACTIVATE      = WM_USER + 1;
  TTM_ADDTOOL       = WM_USER + 50;
  TTM_TRACKACTIVATE = WM_USER + 17;
  TTM_TRACKPOSITION = WM_USER + 18;
  TTM_SETTITLE      = WM_USER + 32;

var
  ToolTipHandle: Cardinal;
  ToolInfo: TToolInfo;

procedure CreateToolTips();
var
  vRect: TRect;

begin
  ToolTipHandle := CreateWindowEx(WS_EX_TOPMOST,
                                  TOOLTIPS_CLASS,
                                  nil,
                                  WS_POPUP or TTS_NOPREFIX or TTS_BALLOON or TTS_ALWAYSTIP,
                                  Integer(CW_USEDEFAULT),
                                  Integer(CW_USEDEFAULT),
                                  Integer(CW_USEDEFAULT),
                                  Integer(CW_USEDEFAULT),
                                  Application.Handle,
                                  0,
                                  Application.Handle,
                                  nil);

  GetWindowRect(Application.Handle, &vRect);
end;

procedure AddToolTip(Sender: TObject; IconType: Integer; Title: AnsiString; Text: PWideChar);
var
  vControl: TWinControl;
  vCaretPos: TPoint;
  X, Y: Integer;

begin
  vControl := Sender as TWinControl;

  if ToolTipHandle = 0 then
    exit();

  SetWindowPos(ToolTipHandle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE or SWP_NOSIZE or SWP_NOACTIVATE);

  ToolInfo.cbSize := SizeOf(TToolInfo);
  ToolInfo.uFlags := {TTF_CENTERTIP or} TTF_TRACK or TTF_TRANSPARENT or TTF_SUBCLASS;
  ToolInfo.hwnd   := vControl.Handle;
  ToolInfo.hInst  := Application.Handle;

  GetClientRect(ToolInfo.hwnd, ToolInfo.Rect);
  ToolInfo.lpszText := Text;

  GetCaretPos(vCaretPos);
  X := vControl.ClientOrigin.X + vCaretPos.X;
  Y := vControl.ClientOrigin.Y + vControl.ClientHeight - vCaretPos.Y;

  SendMessage(ToolTipHandle, TTM_ADDTOOL,       WPARAM(1), LPARAM(@ToolInfo));
  SendMessage(ToolTipHandle, TTM_SETTITLE,      IconType,  LPARAM(PWideChar(Title)));
  SendMessage(ToolTipHandle, TTM_TRACKPOSITION, WPARAM(0), LPARAM(MakeLong(X, Y)));
  SendMessage(ToolTipHandle, TTM_TRACKACTIVATE, WPARAM(1), LPARAM(@ToolInfo));
end;

procedure HideToolTip();
begin
  SendMessage(ToolTipHandle, TTM_TRACKACTIVATE, WPARAM(0), LPARAM(@ToolInfo));
  SendMessage(ToolTipHandle, TTM_DELTOOL,       WPARAM(0), LPARAM(@ToolInfo));
end;

При создании формы создаем наш хинт, показывать его будем в событии OnKeyPress у TEdit'а, ну а закрывать по таймеру

procedure TForm1.edtLogKeyPress(Sender: TObject; var Key: Char);
begin
  if not charinset(Key,['0'..'9', ',', #8]) then
  begin
    MessageBeep(MB_ICONWARNING);
    AddToolTip(Sender, 3, 'Недопустимый символ', 'Разрешены только цифры и запятая');
    Timer.Enabled := True;
    Key := #0;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  CreateToolTips();
end;

procedure TForm1.TimerTimer(Sender: TObject);
begin
  HideToolTip();
  Timer.Enabled := False;
end;
Получаемый результат:

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

Про частично определенные модули

 Не всегда к имени модуля бывает добавлен префикс - имя области действия этого модуля. Я стараюсь писать программы так, чтобы все модули всегда включали такой префикс. Но как быть со сторонними модулями?

Рассмотрим случай, когда требуется использовать библиотеку OExport в консольном проекте (Embarcadero RAD Studio 10.3.3). Для этого в тестовом примере консольного приложения я подключил модули OExport.pas и OExport_VCL.pas (также я добавил модуль MemTableEh.pas библиотеки EhLib).

Код тестового примера:

При компиляции получаю ошибку, связанную с неразрешенностью компилятором частично определенных имен модулей в OExport_VCL:
Компилятор не понимает к чему относится модуль Graphics - к Vcl.Graphics или FMX.Graphics (аналогичная ошибка, если закомментировать OExport_VCL в секции uses, будет с модулем Controls в модуле DBGridEh.pas).

Для того, чтоб решить эту проблему, мне пришлось вручную править файл .dproj.
Откроем файл в текстовом редакторе. В самом начале находим узел "FrameworkType".

Необходимо заменить значение с "None" на "VCL"
Чуть ниже найдем "PropertyGroup Condition="'$(Base)'!=''", а внутри него узел "DCC_Namespace"
Добавим в его значение следующие строчки: "Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples; Vcl.Shell"
Готово! Теперь при компиляции ошибок не будет.