Tuesday, March 1, 2016

Porting a VB.NET app to linux with Mono

I recently faced an interesting challenge : porting an existing VB.NET industrial application to linux. The code was running on Windows CE 5.0 until then with the compact framework 3.5.
This work was motivated by the desire of the company to reduce the costs of our future equipments. Knowing that my company sells no more than a hundreds of products per year, I came up with the raspberry pi solution.

Why the raspberry pi ?

Even though there are a lot of competitors now (banana pi, lemon pi, solidrun products...), the raspberry pi is still the most attractive:

  •  35 $/ unit
  •  Free to use for commercial purpose
  •  Huge community
  •  Lots of additional modules

What about the performances ?

The board that I try to replace is based on an ARM Cortex A5 (500 Mhz) with 256 Mb of RAM. It provides I²C, SPI, UART lines, a LAN interface, and comes with a 6" LCD touchscreen.

The raspberry pi 2 offers the same interfaces with a similar CPU clocked at a higher frequency (900 MHz). Recently, the raspberry pi has been offered an officiel 7" LCD touchscreen for only 80 $. Plus, the raspberry pi 2 has 1 Gb of RAM making it more than suitable for the repacement of the other board.

VB.NET

I am a big fan of C# .NET. I think it is an elegant and powerful language, very easy to learn, with a great documentation. Never used VB though. Fortunately, I found this:

http://www.harding.edu/fmccown/vbnet_csharp_comparison.html

and it is quite helpful for those coming from the C# syntax.

NET on linux

I've heard about Mono for many years but never experienced it. This was going to be the first time.
Recently, Microsoft started the .NET Foundation : an organization around an open-source, collaborative project for .NET technologies to make them portable.
One can now checkout the .NET source code and compile it on its machine. Does it mean that we can run .NET Core on the raspberry ? Not exactly. At this day, the .NET Core is only available with Windows 10 IoT Core. With linux, you only have one option: Mono.

For my porting work, I only used Mono.
The linux distribution I worked with is the now famous raspbian (Debian).

Install Mono

Installing Mono is straightforward as everything is explained on:

http://www.mono-project.com/docs/getting-started/install/linux/

Additionally,  I had to install the Mono VB compiler:

sudo apt-get install mono-basic

Note: I also tried to install Mono on my old raspberry pi 1, but the install failed (script error). After looking on the internet, it seems that the first version of raspberry pi is not compatible (or so they say...).

Porting : Step 1

Before checking out the project's repository on the rpi and trying to compile it, we need to ensure that it is portable. So I installed Xamarin Studio on my Windows desktop and I tried to compile the VB project for windows. I think everybody knows what happened : it failed... miserably.

The vs2008 vbproj file was not compatible with Xamarin studio so I had to create a new one from the IDE and inserted the existing code.
I added the same options (starting class, splash screen, ...).
I added the required references : fortunately, all of the assemblies were available for Windows but one (a third party assembly compiled for Windows CE used to display a fancy keyboard. Not critical).
I compiled .... about 1000 errors were signaled.

For most of them, they were due to missing Imports or non-declared variables (used in loops for example). The last ones were more annoying : OpenNetCF-dependent code, differences between Windows Forms and WinCE Forms, P/Invokes, .... It took me 2 days to correct them all. Once done, I found out that you can set some compiler options for VB : Explicit, Strict... Maybe turning them OFF immediately would have helped me. Anyway, it was too late and at least, the code gained in clarity.

At the end of this step, my code compiled successfully.

Porting : Step 2

The second step was to execute the code on my desktop computer. Once again.... failure ! Exceptions here and there.
Again, I found some parts of code where file paths were hard-coded (WinCE "/Hard Disk" root).
Following Mono advices, I used IO.Path.Combine wherever possible as well as IO.Path.DirectorySeparatorChar.
Other minor errors appeared but I corrected them easily and the application finally booted on my desktop. (Happy Face)

Note: Facing some unexplained NullExceptions, I had to debug my application with Visual Studio instead of Xamarin. VS gives you more details about the exception and stops right where they happened while Mono sometimes signals the exception from the main thread (this is particularly true when an exception occurs in a control handler or within invoked methods).

Porting : Step 3

This step is just an experiment really. Naively, I deployed the application on my raspberry pi with all the dependencies (I personnaly used scp for this) and ran:

mono myApp.exe

Well, the result was not disastrous as the splash screen appeared on the screen for a while. Unfortunately, the app stopped with a crash. The stack strace was not very explicit, indicating that a disposed object can not be used (somewhere in the internals, following Application.Run).


Porting : Step 4

The idea now, is to checkout the code on the raspberry pi and to compile it with Mono. For convenience, I used MonoDevelop:

sudo apt-get install monodevelop

Once checked out, open your vbproj project file with MonoDevelop. Normally, you should not see any error as we already clean them up in Step 1.
Make sure that you made a local copy of the third-party assemblies that the application needs. Reference them from MonoDevelop.

Ok, let's compile ! .... Failure again. But this time, the error is way too ambiguous to be interpreted :

Compiler crashed with code : 255



-_- ... I disabled Explicit, Stric, Infer options and tried again : same result. In MonoDevelop option, you can uncheck MSBuild option to force the compilation with vbnc instead of using xbuild, a clone of MSBuild. The build failed due to a bad syntax of the command, so I wrote my own vbnc command with all sources, references, resources specified and I experimented the same crash. The stacktrace was not referencing any file or part of code. And that was it, I could not get further due to a compiler limitation. I checked the vbnc compiler project and the last commit was 1-year old. Finally, with some basic console logtraces, I managed to find out that the crash was being caused by the database loading.

Porting : Step 5

The database, of course ! I did not think about it but working with SQL Server Compact database on linux is not possible. So I had to find a solution to convert the existing database (sdf file) to a more common and portable one. Check this post for a full tutorial.

After doing that, with a fresh and working SQlite3 database file, my application finally booted properly. However, another bug came out: the application was not responding to clicks (no event fired)...
I did not go through this one as I spent too much time already on what was supposed to be a demo project. If anyone has a solution for this bug, please comment.

Conclusion

Porting an existing VB.NET code to linux was a long journey that ended with a failure. The VB compiler provided by Mono is not rock-solid and can give you a bad time. C# seems to be better implemented and it is a shame that the existing code was not written with it.
Despite of the result, I consider Mono as an unbelievably complete solution. I think that the developers did a great job.
Now that the .NET Core is public and that a synergy exists with Mono/Xamarin, I am sure that someday, porting my code will be easier.

Porting the code to a linux environment was just a challenge really. I will now try to do the same with Windows IoT Core instead, the real successor of Windows CE. According to Microsoft, the Iot Core can run Win32 apps and knowing that my app was running on my desktop computer, I have good chances to see the app running on my rasperry pi.

EDIT: A few words on my quick test with Windows IoT Core. I overlooked a very important detail with .NET Core: the existing .NET Framework assemblies are incompatible with .NET Core which means that the third party assemblies that I was using in my application can not run on Windows IoT Core. As I don't have the sources of these assemblies, the porting of the application to Windows IoT Core is a dead-end.

0 comments:

Post a Comment