Lazarus在Linux上使用自定义动态库

前置知识 Windows上叫动态链接库,通常以*.dll形式命名;Linux上叫共享库,通常以lib*.so形式命名。(此处统一叫动态库) Windows上dll路径的一般搜索顺序为:当前目录->系统目录(如:C:\Windows\System32、C:\Windows\SysWOW64)->Windows目录(如C:\Windows)->PATH环境变量指定的目录。 Linux上so路径的一般搜索顺序为:编译时使用-rpath指定的路径->LD_LIBRARY_PATH环境变量指定的路径->系统默认库路径(如/lib、/usr/lib等)->/etc/ld.so.conf和/etc/ld.so.conf.d/目录中配置的路径。 Lazarus/Delphi调用动态库有两种形式:静态调用和动态调用。 静态调用:主程序启动时加载,若动态库不存在或不匹配,则主程序抛异常并中止;主程序退出时卸载。 动态调用:主程序在需要时可随时加载,不需要时可随时卸载;动态库发生异常时,一般不会导致主程序中止。 使用动态库 动态库 动态库本身不需要特殊设置,正常编译、构建即可。(以名为dll的动态库为例,实际文件名为libdll.so) 主程序 编译时 静态调用方式 主程序直接编译会报错:Warning: linker:/usr/bin/ld: cannot find -ldll: No such file or directory,意思是链接器找不到名为dll的动态库文件。 解决方案 打开Project Options->Compiler Options->Compilation and Linking,勾选Pass options to linker with "-k", delimiter is space,并在下方填入-L’动态库所在路径’,保存即可正常编译。 动态调用方式 主程序直接编译即可。 运行时 静态调用方式 此时编译后的程序并不能正常运行,会报错error while loading shared libraries: libdll.so: cannot open shared object file: No such file or directory,意思是加载动态库时找不到名为libdll.so的动态库文件。 这是前面提到的路径搜索顺序导致的,可将动态库放入相应的搜索路径下,也可使用-rpath指定路径。对于自定的动态库,建议使用-rpath指定路径,最好指定的路径为当前路径(与Windows保持一致)。 解决方案 打开Project Options->Compiler Options->Compilation and Linking,勾选Pass options to linker with "-k", delimiter is space,并在下方填入-rpath='$ORIGIN'(与之前的-L’动态库所在路径’之间要添加一个空格),保存并重新编译,即可正常运行。...

2025-02-20 21:51:24 · 1 分钟 · 慢步道人

Lazarus为应用添加版本信息

需求背景 在开发过程中,我们经常需要给应用添加版本信息,以便于追踪和管理应用的版本。 对于windows平台,直接在Project->Project Options...对话框中的Project Options->Version Info内进行设置即可。 但是,对于非windows平台,该方案便不可行。 而且,该方案还存在一个明显的问题:版本信息需要手动维护,未能与版本控制系统进行有效关联,很可能出现应用与源码不一致的情况。 解决思路 不使用Version Info功能,但取其把版本信息编译到可执行文件中的思想。 借鉴vscode的关于方案,版本信息中包含版本号和提交ID即可使应用和源码进行关联。 只要解决了在构建时动态获取版本信息的问题,便可以解决该问题。 解决方案 经多种尝试,最终确定的方案如下: 在源码中添加ver.inc文件,用于存储版本信息,在关于界面等进行展示。 编写脚本,获取版本信息并写入ver.inc文件。 打开Project->Project Options...对话框,在Compiler Options->Compiler Commands->Execute before中添加脚本的路径。 正常构建应用即可。 如要跨平台,可分别编写对应平台的脚本并创建对应的Build mode,在Build modes中选择对应的模式,然后配置对应的脚本,正常构建即可。 附脚本 以使用git为例: windows 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @echo off ::获取版本号 git describe --tags >nul 2>nul if errorlevel 1 ( set VER_NO=0.0 ) else ( for /f "delims=" %%a in ('git describe --tags') do set VER_NO=%%a ) ::获取提交id git rev-parse HEAD >nul 2>nul if errorlevel 1 ( set COMMIT_ID=0000000000000000000000000000000000000000 ) else ( for /f "delims=" %%b in ('git rev-parse HEAD') do set COMMIT_ID=%%b ) ::写入文件 echo const > ver....

2024-11-12 20:08:32 · 1 分钟 · 慢步道人

Lazarus获取纳秒级时间

背景 最近在改写一个加密算法,算法不可避免用到了随机数,但是Lazarus默认的随机种子初始化函数Randomize是依赖于GetTickCount的,而GetTickCount的时间精度是毫秒级的(windows平台实际大约是16ms级),日常是够用的,但在加密算法中就显得太过粗糙了。 关于GetTickCount的时间精度,详见Delphi中的延时和在Lazarus中分析Windows和Linux的延时。 在以上文章中有提到,windows平台下可以使用QueryPerformanceFrequency和QueryPerformanceCounter获取高精度的时间,事实上是微秒级的时间(本机实测是0.1微秒级,即百纳秒)。 考虑跨平台的话,如何获取非windows平台的高精度时间就变得很有必要了。 解决方案 搜索资料及查看Lazarus源码,找到了fpgettimeofday和clock_gettime两个函数。 fpgettimeofday fpgettimeofday实际上就是UNIX平台的gettimeofday,只引用unix单元即可。其定义如下: 1 function fpgettimeofday(tp: ptimeval;tzp:ptimezone):cint; external name 'FPC_SYSC_GETTIMEOFDAY'; 其参数ptimeval的定义如下: 1 2 3 4 5 6 7 8 9 10 timeval = record tv_sec:time_t; {$ifdef CPUSPARC64} tv_usec:cint; {$else CPUSPARC64} tv_usec:clong; {$endif CPUSPARC64} end; ptimeval = ^timeval; TTimeVal = timeval; 另外,unix平台的GetTickCount64用到了fpgettimeofday: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function GetTickCount64: QWord; var tp: TTimeVal; {$IFDEF HAVECLOCKGETTIME} ts: TTimeSpec; {$ENDIF} begin {$IFDEF HAVECLOCKGETTIME} if clock_gettime(CLOCK_MONOTONIC, @ts)=0 then begin Result := (Int64(ts....

2024-08-31 21:25:25 · 2 分钟 · 慢步道人

Lazarus跨平台方案总结

前言 对于Lazarus跨平台开发接触有一段时间了,虽然踩了不坑,但仍觉得已踩过的坑不过是九牛一毛。不过还是应该记录一下,以备忘,以后又踩新坑了再补充吧。 环境 项目 Linux Windows 操作系统 Debian 12 Win10 CPU架构 x86_64 x86_64 桌面环境 xfce 4 / 输入法 fcitx 5 系统自带 GUI环境 gtk2/qt5/qt6 win32/gtk2/qt5 目标程序 64位 32位/64位 Qt6Pas1.dll未能得到,win + qt6未测试 linux 64位系统未能设置好32位运行环境,未测试 qt5未能得到64位的dll,未测试 坑 windows Application.MessageBox按钮文字英文,无法直接本地化,Windows.MessageBoxAPI可以 TPageControl组件自带的关闭按钮在win32下不可用,gtk2和qt5正常 qt5下,某些组件会有很诡异的现象 gtk2下,64位目标程序运行时报错 gtk2下,默认界面很丑,需要用皮肤 带GUI的dll机制和行为与delphi不一致 使用ShareMem单元需要自行构建fpcmemdll.dll linux gtk2版IDE无法输入中文;目标程序可正常输入(SynEdit不可以,必须修改相关源码) qt5和qt6版IDE可输入中文,但部分字无法输入,也无法正常输入多于两个字的词组;目标程序存在相同的问题 qt5版IDE及目标程序所需要的libqt5pas一定要和编译用的IDE版本匹配 总结 基于有限的测试经验,得出了以下结论: 虽然Lazarus是跨平台的,并且号称一次编写,到处编译,但GUI的跨平台表现是不完全一致的,需要分别处理 整体上,gtk2兼容性和稳定性优于qt 最佳的开发方案:在windows系统上开发,然后交叉编译或在目标环境下进行编译(若使用了SynEdit,必须修改相关源码) 保持跨平台一致性的方案:windows平台采用32位的gtk2+皮肤,linux平台采用gtk2+皮肤

2024-08-07 21:57:25 · 1 分钟 · 慢步道人

在Debian上使用Lazarus的Qt5问题

事件 环境 项目 值 操作系统 Debian 12 桌面环境 xfce 4 输入法 fcitx 5 背景 最早在Debian上安装Lazarus是直接sudo apt install lazarus,即使用的是官方仓库的版本,也是戏称为万年不更新版。默认安装的是Gtk2版,该版存在中文输入法问题(输入法问题可参照解决Debian上Lazarus输入法问题);也可以把IDE改为Qt5版,同样可以使用中文输入法。 想用最新版Lazarus 3.4,直接在官网下载最新的deb包,没有安装成功。 后来使用的是fpcupdeluxe-x86_64-linux安装,即Gtk2版,但是由于官方并没有合并中文输入法的解决方案,每次升级都要再做一遍,麻烦。 现在,准备使用fpcupdeluxe-x86_64-linux-qt5直接安装Qt5版。 问题 直接使用fpcupdeluxe-x86_64-linux-qt5安装,报错: 1 ./fpcupdeluxe-x86_64-linux-qt5: error while loading shared libraries: libQt5Pas.so.1: cannot open shared object file: No such file or directory 是因为缺少libQt5Pas.so.1,安装libqt5pas-dev包: 1 sudo apt install libqt5pas-dev 再次安装,又报错: 1 ./fpcupdeluxe-x86_64-linux-qt5: symbol lookup error: ./fpcupdeluxe-x86_64-linux-qt5: undefined symbol: QGuiApplication_setFallbackSessionManagementEnabled 先安装Gtk2版,再修改为Qt5版,编译还报错。编写普通的应用,编译为Qt5版,同样报错。 安装官方仓库的Lazarus 2.2,一切正常。 解决 去github上查看fpcupdeluxe相关的issues,说是要下载1.2.15版的libqt5pas,并安装: 1 sudo apt install ./libqt5pas1_2.15-1_amd64.deb ./libqt5pas-dev_2.15-1_amd64.deb 进一步查看,其实是因为Qt5删除/更新了部分接口导致不兼容了,而Debian 12的官方仓库中并未包含该更新,所以需要手动下载安装,Debian 13的官方仓库中已经包含了,像以前一样直接安装即可。...

2024-08-04 16:37:12 · 1 分钟 · 慢步道人

在Lazarus中分析Windows和Linux的延时

前言 之前用Delphi时,研究过延时及其时间精度,并写了篇文章——Delphi中的延时,当时是仅在windows平台下测试的,现在想用Lazarus分别在windows和linux平台测试对比下。 测试 测试环境 CPU:x86_64 win:win10 linux:debian 12.6 + xfce ide:Lazarus 3.4 + fpc 3.2.2 测试设计 整体与之前的设计保持一致,增加了GetTickCount64的测试,Timer的测试有改动,具体如下: 假定系统时间是足够精确的,因此使用Now分别在延时前后获取系统当前时间来进行耗时评估。 为使测试更具代表性,每个测试点连续测试100次,取算术平均值。 在1ms~100ms内,测试点步长为1ms,在100ms~1000ms内,测试点步长为10ms。 为尽可能减小干扰,测试过程中未使用并行,Sleep、GetTickCount和GetTickCount64未使用Application.ProcessMessages;,Timer绕不开事件触发,使用了Application.ProcessMessages;。 测试结果 windows 延时(ms) Sleep GetTickCount GetTickCount64 Timer 1 1.93 15.51 15.63 15.62 2 2.93 16.59 15.62 15.63 3 3.95 15.58 15.62 15.66 4 5.10 15.49 15.94 15.61 5 5.86 15.55 15.62 15.65 6 6.83 15.50 15.62 15.63 7 7.81 15.62 15.63 15.67 8 8.77 15.55 15.63 15.68 9 9.76 15....

2024-07-24 23:23:07 · 10 分钟 · 慢步道人

在Lazarus中使用ShareMem

问题 使用Delphi开发应用时,如果要在主程序和dll间传递字符串、对象等不兼容C的类型时,通常需要在主程序和dll工程的uses区第一个位置引用ShareMem单元,并将borlndmm.dll随应用一起发布使用。 Lazarus中也有ShareMem单元,说明也可以采用相同的策略解决同样的需求,但是并不是使用borlndmm.dll。 在源码sharemem.pp中可以看到const fpcmemdll = 'fpcmemdll.dll';,说明使用的是fpcmemdll.dll,但是这个dll实际上是并不存在的! 解决 全盘搜索并没有找到fpcmemdll.dll,但是却找到了fpcmemdll.pp。 打开fpcmemdll.pp后发现,其实这并不是一个普通的用于uses的单元,而是一个library工程单元,那么问题就好办了。 创建一个空白library工程,并命名为fpcmemdll 复制fpcmemdll.pp内容到fpcmemdll.lpr 构建fpcmemdll.lpr得到fpcmemdll.dll 其它 linux平台似乎并没有ShareMem单元,也不存在以上用法,应该是只有windows平台才要这样用。

2024-07-23 20:40:36 · 1 分钟 · 慢步道人

使用fpcupdeluxe安装配置Lazarus

前言 之前的配置Lazarus免重装和Lazarus开发环境配置主要是针对windows平台的,同样也适用于linux平台,只是需要稍微变通一些。当然,由于linux平台本身的多样性,自然也会有一些特殊的坑,例如:解决Debian上Lazarus输入法问题。 在交流中,群友们也多次提到并推荐用fpcupdeluxe,体验了下,直观的感受就是——非常适合折腾!当然,不用来折腾的话也是非常方便的。 准备工作 访问GitHub 该问题请自行解决。 安装Git fpcupdeluxe是直接从源码构建Lazarus的,而Lazarus及fpc的源码是托管在GitLab上的,其本身依赖的工具又是在GitHub上,因此Git是必不可少的。安装可参考Git的基本使用。 下载fpcupdeluxe 可以在fpcupdeluxe的Releases页选择合适的版本下载fpcupdeluxe。linux平台注意要给予执行权限。 安装 核心IDE安装 建议专门建一个目录用于安装,例如就叫fpcupdeluxe,然后把下载的fpcupdeluxe文件放入,并运行。首次运行会有是否记录日志的提示,依据自己情况选择。 配置界面语言及安装路径,建议与fpcupdeluxe安装文件相同。 点Stable/稳定版本(也可选择其它版本),确认后即可自动下载依赖,然后完成基本IDE的安装。如果安装失败了,排除问题后,再次操作3.即可。 对于linux,可能会提示缺少依赖,如: 1 2 3 4 5 6 7 8 9 10 [Info] Checking dev-libs for gtk2 LCLWidgetType. [Error] Missing library: [Error] libX11.so [Error] libgdk_pixbuf-2.0.so [Error] libpango-1.0.so [Error] libcairo.so [Error] libgdk-x11-2.0.so [Warning] You need to install at least libx11-dev libgtk2.0-dev libcairo2-dev libpango1.0-dev libxtst-dev libgdk-pixbuf2.0-dev libatk1.0-dev libghc-x11-dev to build Lazarus !! [Warning] Make, binutils and git are also required !...

2024-07-21 14:20:33 · 1 分钟 · 慢步道人

解决Debian上Lazarus输入法问题

从其它平台迁移而来 环境 项目 值 操作系统 Debian 12 桌面环境 xfce 4 输入法 fcitx 5 问题 在Lazarus的代码编辑器中,快速录入时会出现连击现象 在Lazarus的代码编辑器中无法输入中文 解决 问题1 安装fcitx5-frontend-gtk2即可,详见fcitx5 issues #1006 问题2 参照秋·风大佬中文输入法的解决方案。如果使用的是搜狗输入法的话,参照秋·风大佬搜狗输入法输入词组的解决方案。 补充1 使用fpcupdeluxe安装Lazarus,源码修改后使用git diff生成补丁文件,后期升级Lazarus后若输入法不能用可直接应用补丁,避免重复修改。 生成补丁文件 1 2 cd lazarus git diff > ~/linux_gtk2_ime.diff 应用补丁 如下图进行设置后,更新FPC+Lazarus即可。 以下是Lazarus 3.4的补丁文件内容。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 diff --git a/components/synedit/lazsyngtk2imm....

2024-04-03 20:41:30 · 2 分钟 · 慢步道人

让应用以单例运行

从其它平台迁移而来 有时候我们会期望所编写的应用只运行一个实例,比如监听网络端口或串口,用Lazarus有两种不同的实现方案。 SingleInstanceEnabled属性 SingleInstanceEnabled是TCustomApplication的属性,在初始化之前设置为True即可轻松实现单实例应用: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 program project1; {$mode objfpc}{$H+} uses AdvancedSingleInstance, //注意:必须引用该单元!!! Interfaces, Forms, ...; {$R *.res} begin Application.SingleInstanceEnabled := True; //注意:必须在调用 Initialize 前赋值!!! Application.Initialize; ... Application.Run; end. 特别注意 必须引用AdvancedSingleInstance单元,且该单元必须在Interfaces单元和Forms单元前 必须在调用Application.Initialize前赋值 该方法仅针对同一个可执行文件生效,并非系统全局生效 互斥对象 利用操作系统的互斥对象可实现系统级的单例。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var AppMutex: THandle; begin Application....

2024-01-26 22:24:01 · 1 分钟 · 慢步道人