简介:Kylin环境下LibGdiPlus的安装与配置指南,主要面向需要在国产操作系统中运行依赖Windows GDI图形接口应用的开发者、运维人员及测试人员。文档以清晰的分步流程,从修改apt软件源地址(如使用sudo pluma编辑sources.list)开始,依次讲解libtiff-dev、libjpeg-dev、libgif-dev依赖库的安装,再介绍下载源码、解压、configure配置、make编译与make install安装的完整过程。针对安装中可能遇到的编译错误,给出了将解压目录下.src/.libs中的库文件复制到/usr/lib的备用处理方法,并补充了sudo ldconfig更新系统库路径、设置环境变量等验证与收尾步骤,能帮助用户独立完成LibGdiPlus部署并解决典型问题。资源为单个docx文档,共1个文件,压缩包大小约807KB,以文字步骤和命令示例为主,便于随时查阅。目前已有571人学习下载,内容完整且面向实操,适合刚接触国产平台适配或正在为Kylin配置跨平台图形应用支持的读者参考。
1. 在 Kylin 上装 libgdiplus:.NET 程序部署到这里,十有八九卡在这一步
Kylin 上跑 .NET 程序的人,十有八九在 libgdiplus 安装与配置上栽过跟头。最常见的一幕是:程序在 Windows 上好好的,部署到 Kylin 后一启动就报 DllNotFoundException,或者 System.Drawing 相关的调用直接抛异常,查半天发现是 gdiplus 这个原生库没装上、装错版本、或者装了没生效。这个标题看着简单,背后其实牵扯到系统源、编译工具链、动态库路径、字体环境四件事,任何一环断了,程序就当面翻车。这篇按我在 Kylin 上处理这个库的实际顺序,把概念、安装、配置和坑一次讲透,适合正在把 Windows 桌面程序或老项目迁到 Kylin 的开发者照着操作。
2. 先搞清楚 libgdiplus 是什么:它和 Windows GDI+ 的差异决定了你的装法
2.1 libgdiplus 补的是 .NET 的哪块缺
.NET 程序里凡是用了 System.Drawing 命名空间——Bitmap、Graphics、Pen、Brush、Font、Image.Save——这套 API 在 Windows 上由系统自带的 GDI+ 实现,对应的动态库叫 gdiplus.dll。Linux 上没有这套东西,于是 Mono 生态用 C 语言基于 Cairo 图形库重写了一个兼容实现,就是 libgdiplus,编译产物是 libgdiplus.so。System.Drawing 的 P/Invoke 层按名字去找 gdiplus,在 Linux 上实际映射到 libgdiplus.so,所以这个库装不上、加载不了,所有图形相关调用立刻全崩。
很多入坑的人以为它是个可选组件,其实对依赖 System.Drawing 的老程序来说它是硬依赖。我接手过某公司的一个老报表系统,服务端大量使用 Graphics 画图表再导出图片,迁到 Kylin 时没装 libgdiplus,结果一启动就报找不到 gdiplus,整个服务起不来。换句话说,要不要装这个库不由你选择,由你的代码用没用 System.Drawing 决定。识别方法很简单:源码里搜一下 using System.Drawing 或者 System.Drawing.Common 的引用,有就绕不开它。
2.2 安装前先确认三件事:系统版本、CPU 架构、软件源
我一般动手前先执行三条命令,把环境摸清楚,避免装完才发现架构或源不匹配:
cat /etc/os-release uname -m apt-cache policy libgdiplus- cat /etc/os-release 看 Kylin 的版本号。Kylin 的软件源基于 Debian 系基线,不同版本能拿到的包不一样,版本号直接决定你走 apt 还是走源码编译。
- uname -m 确认 CPU 架构。x86_64 和 aarch64(ARM64)在 Kylin 服务器版和桌面版上都很常见。装错架构的 .so,程序加载时照样报错,而且报错信息不会直接告诉你架构不对,只会说找不到库,容易被带偏。
- apt-cache policy libgdiplus 是最关键的一条。源里有这个包会直接显示候选版本号;什么都不显示,说明当前源里没有,得考虑加源或源码编译,也就是第四章的路径。
注意 apt-cache policy 显示的是源里的版本,不是已安装的版本。已安装版本要看 dpkg -l libgdiplus。两者不一样,排查时别弄混。我见过有人看到 policy 有输出就以为装好了,结果程序还是报错,最后发现根本没装。
2.3 运行时决定安装方式:Mono 还是 .NET
Kylin 上跑 System.Drawing 代码的人实际分两拨。一拨是老人:老项目还在用 Mono 运行时,mcs 编译出 exe 后用 mono 命令跑,这种场景下 libgdiplus 版本最好和 Mono 配套,避免绘制行为不一致。另一拨已经切到 .NET 6 及以上,用 dotnet 命令跑,这时 libgdiplus 只需要提供 gdiplus 的 P/Invoke 入口即可,版本要求宽松些,但要额外注意 .NET 6 以后 System.Drawing.Common 在非 Windows 平台默认禁用,细节放到第五章踩坑清单里细说。
判断自己属于哪一拨,两条命令:
mono --version dotnet --info哪条有输出,说明机器上装的是哪个运行时;都没输出,说明程序根本没部署到这个环境,先装运行时再回来折腾 libgdiplus。如果是帮别人排查一台完全陌生的机器,我一般先跑这两条,再决定后续命令的写法,省得按错误路线白查半天。
3. 用 apt 装 libgdiplus:最快路径与版本订选
3.1 一条命令装完,依赖让系统自己拉
如果 Kylin 的软件源配置正常且 libgdiplus 在源里,apt 安装是最省事的路径:
sudo apt update sudo apt install -y libgdiplus执行完顺便确认实际装了什么,尤其是关联的图形库:
dpkg -l | grep -E "libgdiplus|cairo"apt 会自动把 libcairo2、libjpeg、libgif、libtiff、libpng、freetype 这些依赖一起拉进来,这就是包管理器的价值:依赖树不用自己盯。注意 apt 装出来的 libgdiplus.so 一般位于 /usr/lib/x86_64-linux-gnu/ 或 /usr/lib/aarch64-linux-gnu/ 下,具体路径以 dpkg -L 为准,这个路径后面排查时会用到。
参数说明:-y 表示跳过安装确认,脚本化部署时必加。apt update 之前先确认源里能正常解析 Kylin 的仓库,否则 update 阶段就报 404,后面怎么装都白搭。我在某台服务器上遇到过源列表里残留了旧系统版本的地址,apt update 一直报错,处理方式是先修正 /etc/apt/sources.list 里的地址,再回来装。
3.2 装完立刻做三件事验证:ldconfig 缓存、so 文件、运行时
很多同事装完 libgdiplus 就直接跑程序,报错后回头怀疑到底装没装上。我习惯装完马上做三条命令:
ldconfig -p | grep gdiplus dpkg -L libgdiplus | grep "\.so" mono --version- ldconfig -p | grep gdiplus:确认动态库缓存里能看到 libgdiplus。看不到,程序运行时 ld.so 找不到它,就会出现典型的 DllNotFoundException。
- dpkg -L libgdiplus | grep ".so":列出这个包实际安装的文件,确认 so 文件的真实路径。不同架构路径不一样,记住它,后面排查时对照着看。
- mono --version:如果走 Mono 路线,这条顺带确认运行时是否正常。Mono 启动时对 libgdiplus 有隐式依赖,Mono 能起来说明环境至少没大问题。如果是 dotnet 环境,换成 dotnet --info 即可。
3.3 源里没有?先别急,按顺序试这三条路
如果 apt-cache policy libgdiplus 没有输出,先别一头扎进源码编译。我一般按这个顺序尝试:
- 确认 sources.list 里是不是只开了主源。有些 Kylin 版本默认没开完整归档,把相关源打开后 apt update 再看一次,可能就有包了。
- 从同源系归档里直接下载 .deb 安装。风险是依赖版本可能对不上,apt 装的时候可能报依赖冲突,能用但心里要有数。
- 如果机器装了 Mono 的官方源,优先试它提供的 libgdiplus 包。Mono 源里的版本通常和 Mono 运行时配套,适合 Mono 用户。
这三条都走不通,才进入第四章源码编译。另外,即使源里有包,如果版本明显落后于你的 Mono 运行时,我也会选源码装一个指定版本,避免因 API 行为不一致导致绘制结果诡异。
4. 从源码编译安装 libgdiplus:源里没有时的兜底方案
4.1 编译依赖清单:少一个 configure 就失败
源码编译 libgdiplus 最难受的不是编译本身,而是依赖。configure 阶段缺一个开发包就当场报错。我在这类机器上常用的依赖安装命令如下:
sudo apt install -y build-essential autoconf automake libtool \ pkg-config libglib2.0-dev libcairo2-dev \ libjpeg-dev libgif-dev libtiff-dev libpng-dev \ libfontconfig1-dev libfreetype6-dev libx11-dev逐项说明:build-essential 提供 gcc 和 make;autoconf、automake、libtool 是 autotools 工具链,libgdiplus 的构建系统靠它们生成 configure 脚本;pkg-config 用于在 configure 阶段探测依赖库;libglib2.0-dev 提供 GLib 类型系统,libgdiplus 底层数据结构依赖它;libcairo2-dev 是核心,libgdiplus 本质上是包在 Cairo 上的一层 GDI+ 兼容层;jpeg、gif、tiff、png 四个开发包对应图像编解码器,缺哪个,System.Drawing 里对应格式的编解码就失败;fontconfig 和 freetype 负责字体管理与文字渲染,中文场景下直接影响文字能否正常绘制;libx11-dev 是 X11 客户端库,部分绘制路径会用到。
这里有个常见坑:某些 Kylin 最小化安装里 Cairo 相关库版本偏旧,libgdiplus 新版本要求更高,configure 时会报 cairo version too old。这种情况优先看源里有没有更新的 libcairo2-dev;没有的话,我一般先试装旧版 libgdiplus 而不是去源码编译 Cairo,因为 Cairo 牵连更广,为它趟浑水不值当。
4.2 configure、make、install 三步与常见报错
依赖齐了之后,编译安装本身是标准三步:
git clone https://github.com/mono/libgdiplus cd libgdiplus ./autogen.sh --prefix=/usr/local make -j4 sudo make install- git clone 从官方仓库拉源码到本地。网络正常时一般几十秒到几分钟,看仓库大小。
- ./autogen.sh --prefix=/usr/local:autogen 会先调用 autotools 生成 configure 脚本再自动执行,--prefix 指定安装目录。我习惯用 /usr/local,和系统包管理安装的文件分开,来源清楚,日后卸载也方便。
- make -j4:-j4 表示 4 个并行编译任务,机器核多的调成 -j8 能快不少。
- sudo make install:把编译产物复制到 /usr/local/lib 下。
configure 阶段最常见的报错是缺头文件,报错信息里会明确写 missing xxx.h。看到这种直接 sudo apt install xxx-dev 补上再重新 autogen。另一个容易遇到的是提示文件行尾符异常或 /bin/bash^M,这种通常是源码在 Windows 上解压过导致换行符被改,重新在 Linux 上 clone 一遍即可,别手动去改文件,改了也容易遗漏。
4.3 装完必须做的链接配置:ldconfig 不跑,等于白装
源码装到 /usr/local/lib 之后,系统动态库加载器默认不一定搜这个目录。很多人卡在这一步:make install 明明成功了,程序还是报找不到 gdiplus。原因就是 ldconfig 不知道新库存在。正确做法是把它加进装载器搜索路径:
echo "/usr/local/lib" | sudo tee /etc/ld.so.conf.d/libgdiplus.conf sudo ldconfig ldconfig -p | grep gdiplustee 把 /usr/local/lib 写进 /etc/ld.so.conf.d/libgdiplus.conf,ldconfig 重新生成缓存,最后一条 grep 能看到 libgdiplus.so 才算数。如果你不想动系统级配置,也可以 export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH,但这种方式只对当前终端会话有效,程序以服务方式启动时容易漏掉,生产环境不推荐。
提示:如果程序是 dotnet 跑的,个别情况下还要检查运行时相关的环境变量是否覆盖了库搜索路径。一般 ldconfig 做完就够,但确认一下没有坏处。
5. libgdiplus 装完后的常见问题排查:四个坑的现象、原因、解决
5.1 明明装好了,程序还是报找不到 gdiplus
现象:libgdiplus 已安装,ldconfig -p 也能查到,但 .NET 程序启动时抛 System.DllNotFoundException: gdiplus.dll。
原因主要有三种。第一种是 ldconfig 缓存确实没更新,程序跑在另一个终端或服务进程里,该进程启动时库路径还没写入缓存。第二种是 32 位程序在 64 位系统上找 32 位的 libgdiplus,而系统只装了 64 位库。第三种是程序自带的 native 库搜索配置绕过了系统默认路径。
解决:先 file 看程序二进制和 so 文件的架构,确认位数匹配;再 ldd 看运行时能否解析到 libgdiplus。最直接的办法是在同一终端里执行 ldconfig 后重新起程序。我遇到过某公司的服务脚本在别处覆盖了 LD_LIBRARY_PATH,把系统路径挤掉了,去掉那个 export 后问题消失。这类问题最容易玄学化,实际八成是搜索路径被覆盖。
5.2 中文文字全部画成方块
现象:程序能运行,英文数字正常,但 DrawString 画出来的中文是整排豆腐块。
原因:不是 libgdiplus 的问题,是系统里没有可用的中文字体。libgdiplus 通过 fontconfig 找字体,字体列表里没有 CJK 字体时它只能用西文字体渲染中文,结果就是方块。
解决:装一个中文字体再刷新字体缓存:
sudo apt install -y fonts-noto-cjk sudo fc-cache -fv然后 fc-list :lang=zh 确认系统里已有中文字体。这解释了为什么同一套程序在 Windows 上正常、到 Kylin 上文字全方块:Windows 自带中文语言包和字体,Linux 服务器版往往不预装 CJK 字体,新部署的机器重点检查这一步。
5.3 .NET 6 之后 System.Drawing.Common 直接报平台不支持
现象:用 .NET 6 或更高版在 Kylin 上跑程序,一碰 System.Drawing 就抛 PlatformNotSupportedException,libgdiplus 装得好好的也没用。
原因:从 .NET 6 开始,System.Drawing.Common 默认只在 Windows 平台启用,非 Windows 上运行时默认禁用,这个行为由运行配置里的 System.Drawing.EnableUnixSupport 控制,默认关闭。
解决:在项目文件里加一个运行时配置项:
<ItemGroup> <RuntimeHostConfigurationOption Include="System.Drawing.EnableUnixSupport" Value="true" /> </ItemGroup>重新发布部署。注意这只是过渡开关,官方并不推荐在服务端长期依赖 System.Drawing,性能和稳定性都不如专用图像库。短期迁动能跑,长期建议评估换 SkiaSharp 或 ImageSharp 这类跨平台方案。
5.4 保存图片时抛 ExternalException 或图片内容损坏
现象:DrawString 正常,但 Bitmap.Save 要么抛 System.Runtime.InteropServices.ExternalException,要么保存出来的图片颜色错乱、文件打不开。
原因:libgdiplus 保存图片时依赖对应编码器库。保存 PNG 要 libpng,JPEG 要 libjpeg,GIF 要 libgif。编译时没带上某个库,或系统里对应库版本太旧,保存就会失败或损坏。另一个诱因是 Cairo 和 libgdiplus 版本差异过大,图形上下文操作异常。
解决:先确认编译时依赖的编解码库是否齐全,dpkg -l 检查 libpng、libjpeg、libgif 等,缺哪个装哪个,然后重新编译 libgdiplus。依赖齐全还出问题,就换 libgdiplus 版本,我遇到过旧版配新 Cairo 出错,换成匹配版本后保存恢复正常。血泪经验:这种问题先看编码器库,别看业务代码,业务代码无辜得很。
6. 配置到能出图之后:最小验证程序、字体缓存与句柄习惯
6.1 一个最小 C# 程序验证绘制、字体、保存全链路
我每次装完 libgdiplus,不直接拿业务程序去试,先跑一个最小验证程序,把创建画布、画文字、保存图片整条链路单独测一遍:
using System; using System.Drawing; using System.Drawing.Imaging; class GdiplusTest { static void Main() { using (var bmp = new Bitmap(800, 600)) using (var g = Graphics.FromImage(bmp)) { g.Clear(Color.White); g.DrawString("Kylin libgdiplus OK 中文测试", new Font("Noto Sans CJK SC", 18), Brushes.Red, 20, 20); bmp.Save("/tmp/gdiplus_test.png", ImageFormat.Png); } Console.WriteLine("PASS"); } }保存为 gdiplus_test.cs,用 Mono 编译运行时这样执行:
mcs gdiplus_test.cs -r:System.Drawing.dll -out:gdiplus_test.exe mono gdiplus_test.exe输出 PASS 后打开 /tmp/gdiplus_test.png 看两点:图片能正常打开(编码器工作正常);中文没有变方块(字体链路正常)。如果机器上 mcs 不存在,说明缺 Mono 编译工具,装 mono-devel 后再试。用 .NET SDK 的话,dotnet new console 建工程跑同一段代码效果一样。这个程序成本极低,能把八成环境问题挡在业务程序之前。
6.2 字体注册与缓存刷新:中文环境里必做的收尾
libgdiplus 不直接读 /usr/share/fonts 下的字体文件,而是通过 fontconfig 查询。所以手动拷了字体文件进系统后,必须执行 fc-cache -fv 刷新缓存,否则程序找不到新装字体。这解释了一个常见迷惑:Kylin 上装了中文字体还是方块,字体文件在,缓存没刷,fontconfig 感知不到。
我习惯把这条命令写进部署脚本,和 libgdiplus 装在一起,装完顺手验证:
fc-list :lang=zh | head -20能输出中文字体列表,字体链路就是通的。别省略这步,字体问题比库问题更难肉眼排查,等业务跑起来发现图片里全是方块再回头,浪费的时间够装三遍库。
6.3 一个容易被忽略的老毛病:GDI+ 对象的释放习惯
最后说一个跟配置无关但十个排查里八个会遇到的隐患——GDI+ 对象不释放。System.Drawing 的 Bitmap、Graphics、Pen、Brush 都实现了 IDisposable,不释放的话 libgdiplus 底层的原生资源持续堆积,程序跑几个小时内存曲线一路上涨,最后 OOM。这不是 Kylin 特有,但 Linux 环境下没有 Windows 那套资源回收兜底,表现更明显。
我的习惯是所有 Graphics 和 Bitmap 一律用 using 包裹,或 try/finally 里明确调 Dispose,绝不用完不管。程序上生产后观察内存曲线是否平稳,基本能判断有没有这个毛病。验证、配置、好习惯都到位之后,Kylin 上的 libgdiplus 才算真正配完,希望帮到你。
本文还有配套的精品资源,点击获取