Sicos1977 / MSGReader

C# Outlook MSG file reader without the need for Outlook
http://sicos1977.github.io/MSGReader
MIT License
478 stars 168 forks source link

.NET 6 Support on Linux #288

Closed nate-kis closed 2 years ago

nate-kis commented 2 years ago

Describe the bug .MSG files are not able to be read/parsed on .NET 6 when the code is running on linux.

One of the .NET 6 breaking changes is that it now no longer supports the system.drawing.common on linux. (see .net 6 breaking change here)

The exception you get is: Error Message: 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. Stack Trace: at System.Drawing.SafeNativeMethods.Gdip.GdipGetGenericFontFamilySansSerif(IntPtr& fontfamily)

For .NET 5, you are able to install the linux package libgdiplus to have it work, but this no longer is a workaround.

To Reproduce Steps to reproduce the behavior:

  1. Try processing a .msg email in a .NET 6 application on linux

Expected behavior The .msg email will be able to be parsed.

Desktop (please complete the following information):

Sicos1977 commented 2 years ago

I'll try to look into this after next week because next week I'm on skiing holiday

MichaelPeter commented 2 years ago

Hi would also be highly interested in removing the System.Drawing depedency since I want to use the reader in an Azure Function and there it is not supported.

Will maybe try myself to replace System.Drawing with ImageSharp in a seperate branch, since there are little references. Are pull requests supported? https://stackoverflow.com/questions/51904125/azure-function-gives-error-system-drawing-is-not-supported-on-this-platform

Also very interested in a .NET6 target, since this way the dependencies to .NET Standard don't have to be deployed with .NET 6.

Sicos1977 commented 2 years ago

I'm always open for pull requests.

MichaelPeter commented 2 years ago

So I made a quick attept to port the library to ImageSharp (Apache Licence)

https://github.com/MichaelPeter/MSGReader/commit/f66a85b315c27de6345a9cc0c8aacc5eb4c1b131

Essentially System.Drawing is barely used, BUT at a few key places.

The Loading and Saving of PNGs I could be quickly replace with ImageSharp,

but two things would need to be solved:

I did not find a library yet to extract files from Icons yet without using the winApi yet.

EDIT Regarding Icon Support:

TL;DR: FreeImage-dotnet-core or with ImageMagick.NET seemed the most interesting to me event if no commits since 2017, but ico is also not changing.

Soooo

Here is btw the article from Microsoft where they recommend switching to ImageSharp, Skia or Maui Graphics https://docs.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/6.0/system-drawing-common-windows-only#recommended-action

Maui Graphics: Only .NET6 but have ico support - maybe starting with .NET6? https://github.com/dotnet/Microsoft.Maui.Graphics/blob/0e9a4cdadbc077cc29103c0a12a5b24b5beb6e7e/src/Microsoft.Maui.Graphics.SharpDX.Shared/D2BitmapExtensions.cs#L101

ImageSharp: No support for ico but pure .NET: https://github.com/SixLabors/ImageSharp/discussions/1315

SkiaSharp - seems to support Ico but probably overkill.

This post was very intersting https://stackoverflow.com/a/44428480

CoreCompat: Autor recommends to move to now also obosolete System.Drawing.Common https://stackoverflow.com/a/47208937/3181933

Image Magick: Supports extracting icos but kind of huge overhead. https://github.com/dlemstra/Magick.NET/blob/46a352cdb431935f35af59b8125fa45f1c50a1a9/src/Magick.NET/Optimizers/IcoOptimizer.cs#L224

Using FreeImage-dotnet-core directly: The wrapper is last updated 2017, but ico also doesn't change anymore: Since image magick uses this package for images we could also use it directly: https://github.com/matgr1/FreeImage-dotnet-core/blob/f9ede8251be907ebf3c469995fe1c6a0f7ccb534/src/Samples/Sample%2001%20-%20Loading%20and%20saving/Program.cs#L104

Using libgdiplus directly Possible for this purposes, but microsoft dropped support because makers didn't respond anymore.

Sicos1977 commented 2 years ago

At the moment I'm busy with porting another project to .NET 6, when this is done I'll start with MSGReader

SteAmeR commented 2 years ago

hi, @Sicos1977 any progress on this issue?

MichaelPeter commented 2 years ago

So the issue with linux/net6 support is actually just the SystemFont.DefaultFont for fnil. If the call is replaced with string empty it works.

Even if it is not clean replacing it with string empty

The part with the ImageLibrary is actually just used if Messages are written.

I was not yet deep enough in the code to make a proper fix

SteAmeR commented 2 years ago

Hi @MichaelPeter,

I fixed the issue by assigning arial to FNIL as below,

case "fnil":
                            name = "Arial"; // SystemFonts.DefaultFont.Name;
                            nilFlag = true;
                            break;

Thx for hint

Sicos1977 commented 2 years ago

@SteAmeR : Not yet but I'm finished with porting the other project so I'll try to pick this one up next week.

Sicos1977 commented 2 years ago

name = "Arial"; // SystemFonts.DefaultFont.Name;

Used your solution when compiled for .net5 or 6 --> 20295b97bf82adef8f360acb3bcc269396ad9dec

SteAmeR commented 2 years ago

Yes I used for .net6. Also test on linux and worked it :)

Sicos1977 commented 2 years ago

I check microsoft but var icon = Icon.ExtractAssociatedIcon(fileInfo.FullName) should work on .net 6

https://docs.microsoft.com/en-us/dotnet/api/system.drawing.icon.extractassociatedicon?view=dotnet-plat-ext-6.0

So no need for a replacement

Sicos1977 commented 2 years ago

Added support for .net 5 and 6 (also on Linux) --> https://github.com/Sicos1977/MSGReader/releases/tag/4.4

You can get a new package from nuget

Sicos1977 commented 2 years ago

Describe the bug .MSG files are not able to be read/parsed on .NET 6 when the code is running on linux.

One of the .NET 6 breaking changes is that it now no longer supports the system.drawing.common on linux. (see .net 6 breaking change here)

The exception you get is: Error Message: 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. Stack Trace: at System.Drawing.SafeNativeMethods.Gdip.GdipGetGenericFontFamilySansSerif(IntPtr& fontfamily)

For .NET 5, you are able to install the linux package libgdiplus to have it work, but this no longer is a workaround.

To Reproduce Steps to reproduce the behavior:

  1. Try processing a .msg email in a .NET 6 application on linux

Expected behavior The .msg email will be able to be parsed.

Desktop (please complete the following information):

  • OS: Linux
  • .NET 6

I replaced system.drawing.commen with microsoft.maui.graphics .. it is still a preview version but works for me without any problems.... and you probably won't need it that much anyway.

MichaelPeter commented 2 years ago

Yay thanks for fixing!!!

What would also be really awsome, if the .NET 6 support would allow at least reading of mails in WebAssembly / under Blazor.

Since the whole library and its dependencies are C# this way the mails could be parsed on Client side in the Browser. This way even a client side webpage could be build, which extracts the mail data and renders it into a sandboxed IFrame (security concerns ignored for now) without any calls to the server side or any server side performance or security issues.

EDIT: Or should I create a seperate issue for WebAssembly support?

If Maui works that is really nice. Not sure if it is also based on on Skia, but skia also works in the browser. But writing mails in the browser is right now not so interesting.

Sicos1977 commented 2 years ago

At the moment I'm not going to add support for webassembly, never did anything with that and I don't feel anything for it to invest time in it because most of the times this library is used on a server.

Sicos1977 commented 2 years ago

But feel free to add support yourself and sent me a pull request ;-)

MichaelPeter commented 2 years ago

Thanks for the offer :) I will at least check it out in a POC and tell about the roadblocks.

Will not overpromise on pull requests, but lets see ;-)

MichaelPeter commented 2 years ago

So Update regarding Blazor/Webassembly: I tried it in a Poc interestingly reading EML and MSG works so far without problems!

Sicos1977 commented 2 years ago

Meaby it has something todo with making it C# .NET 6 compatible.

MichaelPeter commented 2 years ago

Well sadly it is not that easy. Blazor Webassembly can also run .NET Standard Code or .NET5 code.

Principally Blazor/Webassembly can run any .NET Code (Keyword One-Dotnet) but if certain features are used, like here the windows api interop call, it will fail during runtime since in the browser you cannot access the windows API. Like for SystemFonts the GDI+ call to GdipGetGenericFontFamilySansSerif) fails.

But it is is not immediately obvious that System.Drawing relies still heavily on operating system functions (GDI+).

Microsoft is starting to introduce [UnsupportedOSPlatform("browser")] or [SupportedOSPlatform("browser")] Attribute so it can fail during compile time, but this is not yet consistent. https://docs.microsoft.com/en-us/dotnet/standard/analyzers/platform-compat-analyzer

The calls to write Mails will logically also fail, since there you create temporary files - but latter is logically not possible in the browser. Or things like the C# SMTP Send will fail, since there is no access to the TCP/IP layer in the browser.

Also there are more less obvious apis. Like many C# Cryptography APIs don't work in the browser, since in the OS they are hardware accelerated and provided by the OS.

Btw in writing mails, have you considered writing the images as base64 instead of linking external files? Like here stackoverflow.com/a/8499716/10019244

  <img src="data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAAUA
    AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO
        9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Red dot" />

But since we removed SystemFronts, reading works very well :D And thankfully your dependenies RtfPipeand openmcdfare pure C# and don't rely somewhere on a windows interop call or simular! 👍

Sicos1977 commented 2 years ago

Just curious, what is your use case with MSGReader? Ofcourse reading MSG files but in what context?

MichaelPeter commented 2 years ago

My personal projects is writing a document management system - optionally cloud hosted - in the browser. When I click on a document I want a live preview. For PDFs there is PDF.js, but especially .MSG Documents are more difficult to display in browser, since they use this propretary microsoft format.

My first idea was extracting the Html in an AWS Lambda or Azure Function. Second idea was rendering a PDF of the mail on the server. But both idea adds costs since I either need to render a pdf every time a user views a document or store the cached rendition or extracted html somewhere.

But if MsgReader runs directly in the browser, I can just download the .MSG or .EML document and then display the extracted html directly in an sandboxed Iframe, without processing costs on the server. There is a security risk, in case of tracking pixels in the mails and that I render any HTML in the users browser, but even microsoft sharepoint does it that way.

Google had libraries to display mail html savely, but in best google fashion the projects got abandoned.

I send you a personal mail if you are more interested :)

m7md7sien commented 1 year ago

@Sicos1977 Thank you for making MSGReader works on Linux. But why does it still refrence System.Drawing.Common v7 even after migrating to Maui? Is it possible to git red of it inteirely since it doesn't work on Linux?

Sicos1977 commented 1 year ago

Could be a left over from the past. Have to check if it is still needed and if not I will remove it. Thanks for mentioning.

m7md7sien commented 1 year ago

Thank you. I had a quick look and foun that it's used only in Windows for Imaging. but there is other code that's used for Linux. If the Linux code can run on Windows or if there is a cross platform alternative it will be very nice to remove System.Drawing.Common. Or at least downgrade it to v6, because v7 doesn't work on Linux.

Sicos1977 commented 1 year ago

I just checked the code and I made it in such a way the it is only used when the code is run on Windows. When the code is run on Linux it uses MUAI

m7md7sien commented 1 year ago

I see. But because the Nuget references System.Drawing.Common v7, it forces projects that uses it to upgrade to this version which isn't supported on Linux. i.e. one can't use MSGReader and System.Drawing.Common v6 (the latest version supported on Linux). Is it possible to get rid of Ssytem.Drawing.Common entirely and use MUAI on both Windows and Linux? or atleast downgrade to System.Drawing.Common v6? Thank you for your prompt response.

Sicos1977 commented 1 year ago

I didn't know that this exists as a side effect of this choice. I'll downgrade the version back to 6. Also MUAI does not work in the .NET framework that is why I did have to make this choice.

Sicos1977 commented 1 year ago

I changed the visual studio project file so that it only uses System.Drawing.Comment when the .net462 framework is used

This should get rid of the problem on Linux since the .net framework does not work overthere and you at least need .net 5 or 6 and that one uses MUAI. Also changed the code so different assemblies are build on different .net versions

image

Sicos1977 commented 1 year ago

If there is still an issue than please open a new issue instead of reusing this one. I always get lost in issues that are already closed.

I just published version 5.4.0 that should get rid of the System.Drawing.Common problem on Linux because it is now only tied to the .NET 4.6.2 framework that only works on Windows

m7md7sien commented 1 year ago

Wonderful! Thank you very much for implementing this fix that fast.

Sicos1977 commented 1 year ago

Always find it funy (in a good way) that I have so much contact with people that work for Microsoft and use MSGReader (that is build by me that does not work for Microsoft).... because Microsoft invented the MSG format