SharpZipLibの日本語対応

SharpZipLib 1.2.0以前でtarファイルを作成すると日本語が文字化けしていたけど、何時の間にか日本語に対応していたんですね。TarOutputStreamのConstructorでEncodingを指定できるではないですか。

github(https://github.com/icsharpcode/SharpZipLib)から最新のソースコードをダウンロードしてきて、自らビルドしましょう。

.NET COREでLinuxで動作するサービスを作成する

.NET Core + C#でLinuxのDeamon作れないかなと思ったら、思ったより簡単に作れるようになっていたので覚え書き。Program.csとDaemonService.csの中味はほぼ定型なのでサンプルからそのまま流用します。

以下のサンプルは.NET Core 3.1で記述しています。

// Program.cs

using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace DaemonSample
{
    class Program
    {
        public static async Task Main(string[] args)
        {
            var builder = new HostBuilder()
                .ConfigureAppConfiguration((hostingContext, config) =>
                {
                    config.AddEnvironmentVariables();

                    if (args != null)
                    {
                        config.AddCommandLine(args);
                    }
                })
                .ConfigureServices((hostContext, services) =>
                {
                    services.AddOptions();
                    services.Configure<DaemonConfig>(hostContext.Configuration.GetSection("Daemon"));

                    services.AddSingleton<IHostedService, DaemonService>();
                })
                .ConfigureLogging((hostingContext, logging) => {
                    logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
                    logging.AddConsole();
                });

            await builder.RunConsoleAsync();
        }
    }
}
// DaemonService.cs

using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace DaemonSample
{
    class DaemonService : IHostedService, IDisposable
    {
        private readonly ILogger _logger;
        private readonly IOptions<DaemonConfig> _config;
        public DaemonService(ILogger<DaemonService> logger, IOptions<DaemonConfig> config)
        {
            _logger = logger;
            _config = config;
        }

        public Task StartAsync(CancellationToken cancellationToken)
        {
            _logger.LogInformation("Starting daemon: " + _config.Value.DaemonName);
            _logger.LogInformation("arg1:{0}", _config.Value.arg1);
            _logger.LogInformation("arg2:{0}", _config.Value.arg2);

            // ここでサーバー本体の処理をTaskとして起動します。

            return Task.CompletedTask;
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            _logger.LogInformation("Stopping daemon.");
            return Task.CompletedTask;
        }

        public void Dispose()
        {
            _logger.LogInformation("Disposing....");
        }
    }
}
// DaemonConfig.cs

using System;
using System.Collections.Generic;
using System.Text;

namespace DaemonSample
{
    class DaemonConfig
    {
        public string DaemonName { get; set; }
        public string arg1{ get; set; }
        public string arg2{ get; set; }
    }
}

起動時のコマンドラインは下記の通り。コマンドライン引数の受け渡し方が非常に独特です。

/usr/bin/dotnet run --project deamonsample --configuration Release --Daemon:DaemonName="Sample Daemon" Daemon:arg1="argument 1" Daemon:arg2="argument 2"

Demonとして起動するにはsystemdを使用します。/etc/systemd/systemにdeamonsample.serviceファイルを作成し、下記の様に記述します。

[Unit]
Description=deamon sample
After=network.target

[Service]
ExecStart=/usr/bin/dotnet run --project atozipsrv --configuration Release --Daemon:DaemonName="deamonsample " Daemon:arg1="argument 1" Daemon:arg2="argument 2"
WorkingDirectory=/usr/src/deamonsample
Restart=always
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=atozip
Environment=DOTNET_CLI_HOME=/var/src/deamonsample

[Install]
WantedBy=multi-user.target

後は通常のsystemdのDeamon同様に以下のコマンドで起動します。

$ systemctl start deamonsample

参考:Creating a Daemon with .NET Core (Part 1)

Linux上のClosedXMLで画像データを扱うときにSystem.DllNotFoundExceptionが発生する

Linux(Ubuntu 18.04)環境に.NET Core SDK 2.2をインストールし、ClosedXmlで画像を含むExcelファイルを扱おうとしたところ、下記の様な例外が発生して実行出来ません。

System.TypeInitializationException: The type initializer for 'Gdip' threw an exception. ---> System.DllNotFoundException: Unable to load shared library 'libdl' or one of its dependencies. In order to help diagnose loading problems, consider setting th
 e LD_DEBUG environment variable: liblibdl: cannot open shared object file: No such file or directory at Interop.Libdl.dlopen(String fileName, Int32 flag)
     at System.Drawing.SafeNativeMethods.Gdip.LoadNativeLibrary()
     at System.Drawing.SafeNativeMethods.Gdip..cctor()
     --- End of inner exception stack trace ---
     at System.Drawing.SafeNativeMethods.Gdip.GdipLoadImageFromDelegate_linux(StreamGetHeaderDelegate getHeader, StreamGetBytesDelegate getBytes, StreamPutBytesDelegate putBytes, StreamSeekDelegate doSeek, StreamCloseDelegate close, StreamSizeDelegate size, IntPtr& image)
     at System.Drawing.Image.InitFromStream(Stream stream)
     at ClosedXML.Excel.Drawings.XLPicture..ctor(IXLWorksheet worksheet, Stream stream)
     at ClosedXML.Excel.Drawings.XLPictures.Add(Stream stream)
     at ClosedXML.Excel.XLWorkbook.LoadDrawings(WorksheetPart wsPart, IXLWorksheet ws)
     at ClosedXML.Excel.XLWorkbook.LoadSpreadsheetDocument(SpreadsheetDocument dSpreadsheet)
     at ClosedXML.Excel.XLWorkbook.LoadSheets(Stream stream)

.NET Core Runtimeインストール時に依存関係が処理されていないことが原因です。下記のコマンドを実行してGDI+関連のライブラリをインストールすると解決します。

sudo apt install libc6-dev 
sudo apt install libgdiplus
cd /usr/lib
sudo ln -s <a rel="noreferrer noopener" target="_blank" href="http://libgdiplus.so/">libgdiplus.so</a> gdiplus.dll

環境によってはLD_LIBRARY_PATHの設定も必要になるようです。

ASP.NET COREを使ったWEBサービス作成(習作)

習作としてASP.NET COREを使用したWEBサービスを作成してみた。
もちろんLinux(Ubuntu 18.04)上で動作している。

Excelファイルをアップロードすると、適当な文字列部分をバーコード画像に置き換えてくれる。

https://barcode.code-lab.net/

.NET Coreで実行可能ファイルを作る

最近はちょっとしたプログラムを作るときに.NET Coreコンソールアプリケーションを選択するようにしている。今のところ実際にLinuxで動かすような機会はないが、クロスプラットフォームに出来るからだ。ただ通常はDLLとしてビルドされるため、PCに詳しくない人には起動手順を説明しにくい。

第三者向けにインストーラを作成して使用手順を書こうとした時に、ふと.NET Core 2.2からはDLLではなくて、実行ファイル(EXEファイル)としてビルド出来ることを思い出した。第三者向けに配布するなら実行ファイルとしてビルドしておいた方が便利そうだ。

コマンドラインから実行ファイルを生成するのは以下のように”-r win-x64 –self-contained true”指定するだけである。

dotnet publish -c release -r win-x64 --self-contained true

win-x64はRID(Runtime Identifier)でビルドするターゲットとなるプラットフォームを示します。linux-x64とかwin-arm64などと指定すれば異なるOSやCPUアーキテクチャをターゲットとしたバイナリを簡単に作れて便利です。

URL:
https://docs.microsoft.com/ja-jp/dotnet/core/deploying/deploy-with-cli
https://docs.microsoft.com/ja-jp/dotnet/core/rid-catalog