19

We have a thumbnail generator lambda function which I'm trying to update to .NET Core 2.0, but I've encountered the following error when using Microsoft's System.Drawing.Common NuGet package:

TypeInitializationException

The type initializer for 'Gdip' threw an exception. at System.Drawing.SafeNativeMethods.Gdip.GdipCreateBitmapFromScan0(Int32 width, Int32 height, Int32 stride, Int32 format, HandleRef scan0, IntPtr& bitmap) at System.Drawing.Bitmap..ctor(Int32 width, Int32 height, PixelFormat format) at TestFailExample.Function.FunctionHandler(String input, ILambdaContext context) in C:\work\graphics\TestFailExample\Function.cs:line 25 at lambda_method(Closure , Stream , Stream , LambdaContextInternal )

caused by

DllNotFoundException

Unable to load DLL 'libdl': The specified module or one of its dependencies could not be found.\n (Exception from HRESULT: 0x8007007E) at Interop.Libdl.dlopen(String fileName, Int32 flag) at System.Drawing.SafeNativeMethods.Gdip.LoadNativeLibrary() at System.Drawing.SafeNativeMethods.Gdip..cctor()

I've seen this question, but there was no resolution.

The minimum code to reproduce the issue is this:

public string FunctionHandler(string input, ILambdaContext context)
{
    using (var bmp = new Bitmap(100, 100))
    {
        return bmp.Width.ToString();
    }
}

Simply create a .NET Core 2.0 Lambda function project, add a reference to the System.Drawing.Common NuGet package, and replace the function handler with the above code. Chuck it on AWS and run it to get the error. I've noted that referencing the package doesn't cause a problem until you try to actually use it, but this could be down to compiler optimizations.

I've packaged the MCVE into a project and uploaded it to GitHub here for the sake of simplifying the steps people have to go through to reproduce the issue.

I can see that /lib64/libdl.so.2 exists, but /lib64/libdl.so does not. Since symlinking doesn't seem to be possible (read-only file system), I'm not sure how I can resolve this. I've tried using the LD_LIBRARY_PATH environment variable by creating a folder in /tmp and symlinking the file there as the first thing the function does. Unfortunately, it seems to look here for all libraries so the function doesn't run at all. I've also tried setting LD_LIBRARY_PATH to /var/lang/lib:/lib64:/usr/lib64:/var/runtime:/var/runtime/lib:/var/task:/var/task/lib:/tmp and, although I could now run the function again, this still didn't help and I just get the same Gdip error.

I noted that /var/task/lib is already included in the LD_LIBRARY_PATH, so I tried packaging libdl.so and libgdiplus.so with my function, but this also failed, this time stating that entry point GdiplusStartup wasn't found in libdgiplus.so. These files weren't from an Amazon Linux instance, so I've now tried installing Mono and obtaining them from an Amazon Linux instance. This has not helped.

I've tried with the CoreCompat drawing library but this also reports problems pertaining to libgdiplus.so, even if I try and bundle that with the function.

I've tried since on my own Linux instance and can confirm that System.Drawing.Common works.

Is there some clever solution that will allow me to use System.Drawing.Common on AWS Lambda? Is there another way I can fudge my lambda function to have libdl and work?

Update:

Our latest attempt involved using AWS Lambda Layers and carefully extracting all the packages installed by apt within the Docker Amazon Linux image, and then applying those to their own layer. Still we ultimately came down to the "libdl" issue, so we gave up.

A lot of the issues with libraries people suggested are that they didn't render Japanese text correctly, which is important for us. This seems to be an issue which isn't going to get better on AWS Lambda it didn't help, and ultimately it was easier to rewrite our function in Go than continue using C# for this.

Since the libraries mentioned by the answers below are seemingly suitable for general use - and may indeed support Japanese text now - I've chosen to accept the answer that I'm sure will work on AWS Lambda.

Llama
  • 25,925
  • 5
  • 49
  • 68
  • It seems that's because `libdl.so` is missing on the O.S. Check this: https://github.com/dotnet/corefx/issues/25102 – Gusman May 31 '18 at 02:30
  • @Gusman I kind of gleaned as much from the question I linked in my question, but I can't seem to find a way to successfully work around this to get my function working. – Llama May 31 '18 at 03:05
  • @John did you find a way? Im having the same problem – VAAA Aug 20 '18 at 16:19
  • @VAAA I did not. My latest attempt has been with ImageSharp, a library with no external dependencies, which seems to work well enough unless you need non-ASCII text drawing. Unfortunately I do :-( – Llama Aug 21 '18 at 00:17
  • Thank @John. I hope System.Drawing.Common fix that, or AWS Lambda let you upload DLLs. Thanks – VAAA Aug 21 '18 at 02:52
  • 2
    Having the same problem using PdfSharp / MigraDoc. This really sucks – Frederik Nygaard Svendsen Aug 21 '18 at 11:11
  • 3
    Having the same problem using ClosedXML – Roman Marusyk Nov 01 '18 at 20:06

4 Answers4

3

I found a solution for this issue which worked for me:

At first i removed the System.Drawing.Common library from the project, then i installed the library you can find here. It uses the same classes.

using System.Drawing
...
var bmp = new Bitmap(100,100);

At last I installed this other library which contains all the dll's necesary for using drawing libraries on Linux and Lambda as well. By doing this steps the code can be uploaded to AWS without any problem.

Alex HG
  • 31
  • 3
  • 1
    Are you saying that the v2 CoreCompat library works now? Because last time I tried it didn't. If it does, I'll have to give it another shot. – Llama Mar 26 '19 at 07:04
  • Yes, I tried it a few hours ago, just include the linux runtime – Alex HG Mar 26 '19 at 07:05
3

I had the same issue after uploading my application on Ubuntu 18 server running dotnet core 2.1.500 version. I resolved this issue with this solution https://github.com/dotnet/dotnet-docker/issues/618 using MichaelSimons suggestions.

I ran

#sudo apt-get update
#sudo apt-get install -y --allow-unauthenticated \
        libc6-dev \
        libgdiplus \
        libx11-dev \ 
#sudo rm -rf /var/lib/apt/lists/*

This resolved the issues.

Dijkgraaf
  • 9,324
  • 15
  • 34
  • 48
Fidelis Kalu
  • 31
  • 1
  • 3
  • 1
    Yeah, this resolved it in a Docker image for me. Unfortunately the code I'm having trouble with runs on AWS Lambda :( – Llama Apr 09 '19 at 00:45
  • Could you have a look at my question as it is similar to this one?: https://stackoverflow.com/questions/55889999/using-system-drawing-inside-docker . Thanks. – w0051977 Apr 28 '19 at 11:54
  • @John why dont you try CoreCompat.System.Drawing ? – din Jul 03 '19 at 18:56
  • @din I addressed that in my question. Either way our Golang version of our function works well enough to not need the C# one. – Llama Jul 03 '19 at 22:39
  • 1
    @John I had the same problem, but i got away with CoreCompat.System.Drawing and runtime.linux-x64.CoreCompat.System.Drawing, Glad Go Lang worked for you. – din Jul 09 '19 at 19:30
0

If one uses centOS, then below command helps.

  • sudo yum install libgdiplus
Rohil Patel
  • 151
  • 2
  • 4
-1

For image processing in .NET Core Lambda I use the SixLabors.ImageSharp

Here is the code I used in my recent AWS re:Invent talk that did a log if image processing:

var imageBuffer = new MemoryStream();

var resizeOptions = new ResizeOptions
{
    Size = new SixLabors.Primitives.Size { Width = this.TileSize, Height = this.TileSize},
    Mode = ResizeMode.Stretch
};
image.Mutate(x => x.Resize(resizeOptions));
image.Save(imageBuffer, new SixLabors.ImageSharp.Formats.Jpeg.JpegEncoder());

imageBuffer.Position = 0;
Norm Johanson
  • 2,484
  • 12
  • 11
  • +1 because it might solve other people's problems, but unfortunately it didn't work with Japanese text when I tried it. – Llama Dec 06 '18 at 00:25
  • 5
    If you can't solve their problem, you can't just tell them to try a different paradigm entirely. He asked how to make System.Drawing.Common work on AWS Lambda - you didn't even attempt to answer that. – R. McManaman Dec 06 '18 at 22:10
  • While this didn't help me because of issues rendering Japanese text using ImageSharp (and I've ended up re-writing the whole thing in Go instead of C#), I believe this is likely the best general option. – Llama Jun 27 '19 at 00:22