【问题记录】- 国产操作系统适配问题记录

一、起因

 由于国产化系统逐步的推广应用,需要将在 window 系统中实现的功能;迁移到国产系统(UOS(统信 OS)、麒麟操作系统等)中运行。

 在 windows 环境中主要采用 Framework4.0 开发的后台运行程序。主体思路采用将 Windows 程序功能迁移成 Linux 系统后台服务运行。

 特记录在适配过程中相关问题记录

二、问题记录

 1、System.Draw.Common 包使用问题:

  由于后台服务需要生成 pdf 报告功能;需要用到 gdi 相关类型:如 Font、Pen、Image、Bitmap 等类型;在跨平台的处理中微软提供了:System.Draw.Common 包在跨平台中使用。

  注意点:

  a)System.Draw.Common 在 linux 环境中依赖 gdip 包;需要在环境中安装 gdip 包:

//安装 libgdiplus:
apt-get install -y libgdiplus

  b)在.NET6 以后 System.Draw.Common 被归为 Windows 特定的库。 在为非 Windows 操作系统编译时,平台分析器会在编译时发出警告;运行时会出现以下异常:

System.TypeInitializationException : The type initializer for 'Gdip' threw an exception.
      ---- System.PlatformNotSupportedException : System.Drawing.Common is not supported on non-Windows platforms. See https://aka.ms/systemdrawingnonwindows for more information.

  更改原因:

由于 System.Drawing.Common 被设计为 Windows 技术的精简包装器,因此其跨平台实现欠佳。

libgdiplus 是本机端 System.Drawing.Common 跨平台实现的主要提供程序。 libgdiplus 实际上是对 System.Drawing.Common 所依赖的 Windows 部分的重新实现。 该实现使 libgdiplus 成为一个重要的组件。 它大约有 30,000 行 C 代码,大部分未经测试,而且缺少很多功能。 libgdiplus 还具有许多用于图像处理和文本呈现的外部依赖项,例如 cairopango 和其他本机库。 这些依赖项使得维护和交付组件更具挑战性。 自从包含 Mono 跨平台实现以来,我们已将许多从未得到修复的问题重定向到 libgdiplus。 相比之下,我们采用的其他外部依赖项,例如 icu 或 openssl,都是高质量的库。 使 libgdiplus 的功能集和质量与 .NET 堆栈的其余部分相媲美是不可行的。

通过对 NuGet 包的分析,我们观察到 System.Drawing.Common 主要用于跨平台的图像处理,例如 QR 代码生成器和文本呈现。 由于我们的跨平台图形支持不完整,我们还没有注意到大量的图形使用。 System.Drawing.Common 在非 Windows 环境中的使用通常得到 SkiaSharp 和 ImageSharp 的良好支持。

System.Drawing.Common 将仅在 Windows 窗体和 GDI+ 的上下文中继续演变。 

   解决办法:可通过将 runtimeconfig.json 文件中的 System.Drawing.EnableUnixSupportSystem.Drawing.EnableUnixSupport设置为 true 来启用对 .NET 6 中非 Windows 平台的支持

{
   "configProperties": {
      "System.Drawing.EnableUnixSupport": true
   }
}

    可以在项目中添加:runtimeconfig.template.json 设置以上内容;编译时会自动添加到对于配置文件中。   

 2、打印机信息获取:

  在国产系统中打印机管理主要使用 CUPS 管理打印机:

  a)安装 CUPS:(如果未安装改服务)

sudo apt-get update && sudo apt-get install cups cups-client lpr

  b) 获取打印机列表:安装后可以使用以下命令获取打印机列表

//输出打印机信息
lpstat -p
//输出信息:
打印机 HP-Color-LaserJet-MFP-M281fdw 目前空闲。从 2022 年 03 月 15 日 星期二 17 时 49 分 10 秒 开始启用
打印机 PDF 目前空闲。从 2022 年 03 月 17 日 星期四 19 时 09 分 47 秒 开始启用

  c)获取打印机基本信息:如纸张信息、纸张来源、双面打印等

//打印机相关信息
lpoptions -p HP-Color-LaserJet-MFP-M281fdw -l 
//输出信息

//纸张信息
PageSize/Media Size: *Letter Legal Executive FanFoldGermanLegal 4x6 5x8 A4 A5 A6 B5 B6 102x152mm Oficio 195x270mm 184x260mm 7.75x10.75 Postcard DoublePostcardRotated Env10 EnvMonarch EnvISOB5 EnvC5 EnvDL Custom.WIDTHxHEIGHT
//双面打印
Collate/Collate: True False
//双面打印设置
Duplex/Two-Sided: *None DuplexNoTumble DuplexTumble
//纸张来源
InputSlot/Paper Feed: *Auto Tray1 Tray2 ManualFeed
HPOption_Duplexer
/Duplex Unit: True *False
MediaType
/Media Type: *Unspecified Plain HPEcoFFICIENT HPLaserJet90 HPColorLaserMatte105 HPPremiumChoiceMatte120 HPBrochureMatte150 HPCoverMatte200 HPMattePhoto200 HPPremiumPresentationGlossy120 HPBrochureGlossy150 HPTrifoldBrochureGlossy150 HPBrochureGlossy200 Light6074 Intermediate8595 MidWeight96110 Heavy111130 ExtraHeavy131175 HeavyGlossy111130 ExtraHeavyGlossy131175 CardGlossy176220 ColorLaserTransparency Labels Letterhead Envelope HeavyEnvelope Preprinted Prepunched Colored Bond Recycled Rough HeavyRough OpaqueFilm
HPPJLColorAsGray
/Print Color as Gray: True *False
HPRGBEmulation
/RGB Color: *DefaultSRGB PhotoSRGB Adobe VividSRGB HPRGBEmulationNone
HPEdgeControl
/Edge Control: HPEdgeControlOff Light *Normal Max
HPGeneralHalftone
/Halftone: *Smooth Detail
HPTextNeutralGrays
/Text Neutral Grays: *Black ProcessBlack
HPGraphicsNeutralGrays
/Graphics Neutral Grays: *Black ProcessBlack
HPPhotoNeutralGrays
/Photo Neutral Grays: Black *ProcessBlack

  有了前面的命令就可以封装获取打印机名称及打印机信息的方法了:

//命令执行帮助类
public static class ShellHelper
{
    public static string RunCommand(string? command, string? args)
    {
        if (string.IsNullOrWhiteSpace(command))
            return string.Empty;
        var process = new Process()
        {
            StartInfo = new ProcessStartInfo
            {
                FileName = command,
                Arguments = args,
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                UseShellExecute = false,
                CreateNoWindow = true,
            }
        };
        process.Start();
        string output = process.StandardOutput.ReadToEnd();
        string error = process.StandardError.ReadToEnd();
        process.WaitForExit();
        if (string.IsNullOrEmpty(error)) { return output; }
        else { return error; }
    }
}

  获取打印机名称:

public static object GetAllPrinterNames(ILogger? logger = null)
{
    object result = null;
    if (printerCache.TryGetValue(PrinterName, out result)
                && (result is PrinterSettings.StringCollection
                && (result as PrinterSettings.StringCollection).Count == PrinterSettings.InstalledPrinters.Count))
    {
        result = printerCache[PrinterName];
    }
    else
    {
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
        {
            string cmdResult = ShellHelper.RunCommand("lpstat", "-p");
            string[] strArray = cmdResult.Split('\n');
            List<string> strList = new List<string>();
            int index1, index2;
            for (int i = 0; i < strArray.Length; i++)
            {
                index1 = strArray[i].IndexOf(' ');
                if (index1 <= 0) continue;
            index2 </span>= strArray[i].IndexOf(<span style="color: rgba(128, 0, 0, 1)">'</span> <span style="color: rgba(128, 0, 0, 1)">'</span>, index1 + <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">);
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (index2 &lt;= <span style="color: rgba(128, 0, 128, 1)">0</span>) <span style="color: rgba(0, 0, 255, 1)">continue</span><span style="color: rgba(0, 0, 0, 1)">;

            strList.Add(strArray[i].Substring(index1 </span>+ <span style="color: rgba(128, 0, 128, 1)">1</span>, index2 - index1 - <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">));
        }
        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">获取打印机信息</span>
        result =<span style="color: rgba(0, 0, 0, 1)"> strList;
    }
    </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">
    {
        result </span>=<span style="color: rgba(0, 0, 0, 1)"> PrinterSettings.InstalledPrinters;
    }
    printerCache[PrinterName] </span>=<span style="color: rgba(0, 0, 0, 1)"> result;
}
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> result;

}   

  获取打印机信息:需注意不同打印机获取出来的设置信息不同,需要获取通用的打印机信息:纸张来源、纸张、双面打印等

public static List<PrinterInfo> GetAllPrinters(ILogger? logger = null)
{
    List<PrinterInfo> result = null;
    if (printerCache.TryGetValue(PrinterInfo, out object objresult)
                    && (objresult is List<PrinterInfo>
                    && (objresult as List<PrinterInfo>).Count == PrinterSettings.InstalledPrinters.Count))
    {
        result = objresult as List<PrinterInfo>;
    }
    else
    {
        var resultPrinterInfos = new List<PrinterInfo>();
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
        {
            string cmdResult = ShellHelper.RunCommand("lpstat", "-p");
            string[] strArray = cmdResult.Split('\n');
            List<string> strList = new List<string>();
            int index1, index2;
            for (int i = 0; i < strArray.Length; i++)
            {
                index1 = strArray[i].IndexOf(' ');
                if (index1 <= 0) continue;
            index2 </span>= strArray[i].IndexOf(<span style="color: rgba(128, 0, 0, 1)">'</span> <span style="color: rgba(128, 0, 0, 1)">'</span>, index1 + <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">);
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (index2 &lt;= <span style="color: rgba(128, 0, 128, 1)">0</span>) <span style="color: rgba(0, 0, 255, 1)">continue</span><span style="color: rgba(0, 0, 0, 1)">;

            strList.Add(strArray[i].Substring(index1 </span>+ <span style="color: rgba(128, 0, 128, 1)">1</span>, index2 - index1 - <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">));
        }
        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">获取打印机信息</span>
        <span style="color: rgba(0, 0, 255, 1)">foreach</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> item <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> strList)
        {
            cmdResult </span>= ShellHelper.RunCommand(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">lpoptions</span><span style="color: rgba(128, 0, 0, 1)">"</span>, $<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">-p \"{item}\" -l</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
            </span><span style="color: rgba(0, 0, 255, 1)">string</span>[] strArray2 = cmdResult.Split(<span style="color: rgba(0, 0, 255, 1)">new</span> <span style="color: rgba(0, 0, 255, 1)">string</span>[] { <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)"> }, StringSplitOptions.RemoveEmptyEntries);
            </span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">
                PageSize/Media Size: *Letter Legal Executive FanFoldGermanLegal 4x6 5x8 A4 A5 A6 B5 B6 102x152mm Oficio 195x270mm 184x260mm 7.75x10.75 Postcard DoublePostcardRotated Env10 EnvMonarch EnvISOB5 EnvC5 EnvDL Custom.WIDTHxHEIGHT
                Collate/Collate: True False
                Duplex/Two-Sided: *None DuplexNoTumble DuplexTumble
                InputSlot/Paper Feed: *Auto Tray1 Tray2 ManualFeed
                HPOption_Duplexer/Duplex Unit: True *False
                MediaType/Media Type: *Unspecified Plain HPEcoFFICIENT HPLaserJet90 HPColorLaserMatte105 HPPremiumChoiceMatte120 HPBrochureMatte150 HPCoverMatte200 HPMattePhoto200 HPPremiumPresentationGlossy120 HPBrochureGlossy150 HPTrifoldBrochureGlossy150 HPBrochureGlossy200 Light6074 Intermediate8595 MidWeight96110 Heavy111130 ExtraHeavy131175 HeavyGlossy111130 ExtraHeavyGlossy131175 CardGlossy176220 ColorLaserTransparency Labels Letterhead Envelope HeavyEnvelope Preprinted Prepunched Colored Bond Recycled Rough HeavyRough OpaqueFilm
                HPPJLColorAsGray/Print Color as Gray: True *False
                HPRGBEmulation/RGB Color: *DefaultSRGB PhotoSRGB Adobe VividSRGB HPRGBEmulationNone
                HPEdgeControl/Edge Control: HPEdgeControlOff Light *Normal Max
                HPGeneralHalftone/Halftone: *Smooth Detail
                HPTextNeutralGrays/Text Neutral Grays: *Black ProcessBlack
                HPGraphicsNeutralGrays/Graphics Neutral Grays: *Black ProcessBlack
                HPPhotoNeutralGrays/Photo Neutral Grays: Black *ProcessBlack
            </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
            PrinterInfo pinfo </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> PrinterInfo();
            pinfo.name </span>=<span style="color: rgba(0, 0, 0, 1)"> item;
            pinfo.color </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
            pinfo.isvalid </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
            </span><span style="color: rgba(0, 0, 255, 1)">foreach</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> opItem <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> strArray2)
            {
                </span><span style="color: rgba(0, 0, 255, 1)">string</span>[] kvArr = opItem.Split(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">:</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">.ToArray(), StringSplitOptions.RemoveEmptyEntries);
                </span><span style="color: rgba(0, 0, 255, 1)">if</span> (kvArr.Length == <span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)">)
                {
                    </span><span style="color: rgba(0, 0, 255, 1)">string</span> key =<span style="color: rgba(0, 0, 0, 1)"> kvArr.First();
                    </span><span style="color: rgba(0, 0, 255, 1)">string</span>[] values = kvArr.Last().Split(<span style="color: rgba(128, 0, 0, 1)">"</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">.ToArray(), StringSplitOptions.RemoveEmptyEntries);
                    </span><span style="color: rgba(0, 0, 255, 1)">int</span> nIndex = key.IndexOf(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">/</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">);
                    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (nIndex &gt; <span style="color: rgba(128, 0, 128, 1)">0</span>) key = key.Substring(<span style="color: rgba(128, 0, 128, 1)">0</span>, nIndex);<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">todo:判断规则可能只取/前面的部分</span>
                    <span style="color: rgba(0, 0, 255, 1)">switch</span><span style="color: rgba(0, 0, 0, 1)"> (key)
                    {
                        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">纸张</span>
                        <span style="color: rgba(0, 0, 255, 1)">case</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">PageSize</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> /Media Size  /Page Size </span>
                            List&lt;PaperSize&gt; psList = <span style="color: rgba(0, 0, 255, 1)">new</span> List&lt;PaperSize&gt;<span style="color: rgba(0, 0, 0, 1)">();
                            </span><span style="color: rgba(0, 0, 255, 1)">foreach</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> ps <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> values)
                            {
                                PaperSize paperSize </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> PaperSize();
                                paperSize.PaperName </span>= ps.Trim(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">*</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">);
                                psList.Add(paperSize);
                            }
                            pinfo.paper </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> PaperSizeCollection(psList.ToArray());
                            </span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">;
                        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">纸张来源</span>
                        <span style="color: rgba(0, 0, 255, 1)">case</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">InputSlot</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> /Paper Feed  /Media Source</span>
                            List&lt;PaperSource&gt; psourceList = <span style="color: rgba(0, 0, 255, 1)">new</span> List&lt;PaperSource&gt;<span style="color: rgba(0, 0, 0, 1)">();
                            </span><span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = <span style="color: rgba(128, 0, 128, 1)">0</span>; i &lt; values.Length; i++<span style="color: rgba(0, 0, 0, 1)">)
                            {
                                </span><span style="color: rgba(0, 0, 255, 1)">string</span> ps =<span style="color: rgba(0, 0, 0, 1)"> values[i];
                                PaperSource pSource </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> PaperSource();
                                pSource.SourceName </span>= ps.Trim(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">*</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">);
                                pSource.RawKind </span>=<span style="color: rgba(0, 0, 0, 1)"> i;
                                psourceList.Add(pSource);
                            }
                            pinfo.papersource </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> PaperSourceCollection(psourceList.ToArray());
                            </span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">;
                        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">双面打印
                        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">Duplex/Two-Sided: *None DuplexNoTumble DuplexTumble</span>
                        <span style="color: rgba(0, 0, 255, 1)">case</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Duplex</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> /Two-Sided   /Double-Sided Printing</span>
                            <span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = <span style="color: rgba(128, 0, 128, 1)">0</span>; i &lt; values.Length; i++<span style="color: rgba(0, 0, 0, 1)">)
                            {
                                </span><span style="color: rgba(0, 0, 255, 1)">string</span> ps =<span style="color: rgba(0, 0, 0, 1)"> values[i];
                                </span><span style="color: rgba(0, 0, 255, 1)">if</span> (ps.Contains(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">*</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">))
                                {
                                    pinfo.duplex </span>=<span style="color: rgba(0, 0, 0, 1)"> i;
                                    </span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">;
                                }
                            }
                            pinfo.canduplex </span>= values.Length &gt; <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">;
                            </span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">;
                    }
                }
            }
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (pinfo.paper == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
            {
                pinfo.paper </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> PaperSizeCollection(<span style="color: rgba(0, 0, 255, 1)">new</span> PaperSize[<span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">]);
            }
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (pinfo.papersource == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
            {
                pinfo.papersource </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> PaperSourceCollection(<span style="color: rgba(0, 0, 255, 1)">new</span> PaperSource[<span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">]);
            }
            resultPrinterInfos.Add(pinfo);
        }
    }
    </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">
    {
        </span><span style="color: rgba(0, 0, 255, 1)">var</span> printerNames =<span style="color: rgba(0, 0, 0, 1)"> PrinterSettings.InstalledPrinters;
        </span><span style="color: rgba(0, 0, 255, 1)">if</span> (printerNames.Count &gt; <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">)
        {
            </span><span style="color: rgba(0, 0, 255, 1)">foreach</span> (<span style="color: rgba(0, 0, 255, 1)">string</span> printName <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> printerNames)
            {
                </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">
                {
                    PrinterSettings ps </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> PrinterSettings();
                    ps.PrinterName </span>=<span style="color: rgba(0, 0, 0, 1)"> printName;
                    </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (ps.IsValid)
                    {
                        PrinterSettings.PaperSourceCollection psSources </span>=<span style="color: rgba(0, 0, 0, 1)"> ps.PaperSources;
                        resultPrinterInfos.Add(</span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> PrinterInfo(
                            printName,
                            ps.PaperSources,
                            ps.PaperSizes,
                            ps.SupportsColor,
                            (</span><span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)">)ps.Duplex,
                            ps.IsDefaultPrinter,
                            ps.IsValid,
                            ps.CanDuplex
                        ));
                    }
                }
                </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception ex)
                {
                    logger</span>?.LogError(ex, $<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">获取打印机【{printName}】信息失败-请检查打印机是否正常访问</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
                }
            }
        }
    }
    result </span>=<span style="color: rgba(0, 0, 0, 1)"> resultPrinterInfos;
    printerCache[PrinterInfo] </span>=<span style="color: rgba(0, 0, 0, 1)"> result;
}
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> result;

}

 3、打印文件:

  前面解决获取打印相关信息,接下来需要解决打印文档的问题:

/// <summary>
/// 打印内容
/// </summary>
/// <param name="pdfBytes">打印内容 byte 数组</param>
/// <param name="psi"></param>
public static void Print(byte[] pdfBytes, PrintSettings psi)
{
    string tmpFileName = Path.GetTempFileName();
    try
    {File.WriteAllBytes(tmpFileName, pdfBytes);
        ShellHelper.RunCommand("lp", BuildLpArgs(tmpFileName, psi));
    }
    finally
    {
        try
        {File.Delete(tmpFileName);
        }
        catch {}}
}
/// <summary>
/// 构建打印参数
/// </summary>
/// <param name="filePath"></param>
/// <param name="psi"></param>
/// <returns></returns>
private static string BuildLpArgs(string filePath, PrintSettings psi)
{
    StringBuilder str = new StringBuilder();
    if (psi != null && psi.PrinterSetting != null)
    {
        //打印机名称
        if (!string.IsNullOrWhiteSpace(psi.PrinterSetting.PrinterName))
        {
            str.Append($"-d {psi.PrinterSetting.PrinterName} ");}
        //双面打印
        if (psi.PrinterSetting.Duplex != Duplex.Default)
        {
            switch (psi.PrinterSetting.Duplex)
            {
                case Duplex.Simplex:
                case Duplex.Vertical:
                    str.Append($"-o sides=two-sided-long-edge ");
                    break;
                case Duplex.Horizontal:
                    str.Append($"-o sides=two-sided-short-edge ");
                    break;
            }
        }
        //打印份数
        if (psi.PrinterSetting.Copies > 1)
        {
            str.Append($"-n {psi.PrinterSetting.Copies} ");}
    }
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (psi != <span style="color: rgba(0, 0, 255, 1)">null</span> &amp;&amp; psi.PaperSetting != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
{</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">纸张名称</span>
    <span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">.IsNullOrWhiteSpace(psi.PaperSetting.PaperName))
    {
        str.Append($</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">-o media={psi.PaperSetting.PaperName} </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
        </span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">
        全部模板及尺寸如下:
        Letter- US Letter (8.5×11 inches, or 216x279mm)
        Legal- US Legal (8.5×14 inches, or 216x356mm)
        A4- ISO A4 (8.27×11.69 inches, or 210x297mm)
        COM10- US #10 Envelope (9.5×4.125 inches, or 241x105mm)
        DL- ISO DL Envelope (8.66×4.33 inches, or 220x110mm)
        Transparency- Transparency media type or source
        Upper- Upper paper tray
        Lower- Lower paper tray
        MultiPurpose- Multi-purpose paper tray
        LargeCapacity- Large capacity paper tray
        也可以自定义尺寸,比如想输出照片6寸(15.2cmx10.2cm) 只需要输入指令lp -o media=Custom.15.2×10.2cm filename
            </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
    }
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">是否横行</span>
    <span style="color: rgba(0, 0, 255, 1)">if</span> (psi.PaperSetting.Landscape == <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">)
    {
        str.Append($</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">-o landscape </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
    }
}

str.Append(filePath);
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> str.ToString();

}

三、参考

 仅在 Windows 上支持 System.Drawing.Common

 lp 操作命令说明