Using MoMA to Port from Windows .NET to Mono on Linux
Using MoMA to Port from Windows .NET to Mono on Linux
When Microsoft originally released its .NET run-time and framework, programming for Windows became much easier. You got to choose from some favorite languages (C++, a Java-similar language called C# and Visual Basic), but by choosing .NET you were effectively locking yourself into the Windows platform. If you were thinking about portability, you had to look to such tools as C++ and wxWidgets.
That said, Microsoft did something right when it developed the .NET system. It devised a standard called the CLI (Common Language Infrastructure) that was accepted by both Ecma International and ISO (International Organization for Standardization). This allowed developers to create their own versions of .NET for other platforms, or even for Windows.
A few groups of developers have worked on implementing CLI for other platforms, but the one that has clearly taken the lead is the open-source Mono Project. Mono is now available on Windows, the various breeds of Linux and Solaris, and even on Mac OS X-which means that Windows developers can port their software to any of these platforms.
Porting is still not perfect, however, as Microsoft continues to barrel forward with new APIs and technologies under the .NET umbrella. If you have an existing .NET project, you very well may run into problems trying to run it under the Mono run-time. And, indeed, you can run your .NET software directly under Mono, as the binaries are compatible-assuming you haven't used any features not yet implemented under Mono.
That's where the MoMA (Mono Migration Analyzer) tool comes in. MoMA is available free of charge, as is the entire Mono project.
A MoMA Background
The MoMA tool is actually pretty small; there isn't much to it. You simply specify a list of .NET binaries, and the tool will analyze them to look for calls into classes and functions that aren't implemented by Mono, as well as calls directly into the operating system.
This might surprise you, as it did me. The tool analyzes the binaries. At first this might seem a little odd that it's doing some reverse engineering. But, in fact, the so-called binaries that run under the .NET CLI run-time are actually P-code stored in resources of executable files; these files are in turn known as assemblies.
Microsoft includes tools with its SDKs (software development kits) that make reverse-engineering your binaries a snap. The ILDASM (Intermediate Language Disassembler) tool easily opens up any CLI binary file and looks at the metadata describing the file, as well as at the actual assembly code.
However, this "assembly" code isn't Intel assembly; rather, it's the P-code, which is an assembled form of what is called IL (Intermediate Language), and this language is a much higher-level language than plain old assembly. This means it's easy for the MoMA tool to open up your binary and do a full inspection of it-noting what libraries it calls and so on-without touching your source code.
Of course, if the tool finds problems, you'll have to make the changes yourself, which means going back to your original source code and recompiling it. But if you think about it, thanks to the immediate code compiled to P-code, which follows a strict standard, it's a lot easier for a tool such as MoMA to analyze binaries than to analyze source code that could be quite messy.
But that's not the end of the story. The run-time that ships with Mono can process binaries that were compiled under .NET. If the tool detects no problems, then you don't need to recompile it under Mono (although you can if you want to). Instead, you can launch the Mono run-time, passing the .NET program's name as a command-line parameter, and your .NET program will run (hopefully) as is under Mono-even on Linux.
Don't believe me? Try it. Create a simple Form application in Visual Studio and compile it. Then boot to Linux. From the Linux command prompt, type mono, and pass the full path and file name to the form1.exe file, or whatever you called it. I kid you not-it will run under Linux, provided you installed all the Mono libraries, including the Forms library. Note, however, that by default you might not have the Forms library. If not, Google the error message you see and you'll find instructions on how to install it. Then watch your program run under Linux.
eWEEK Labs Takes MoMA for a Spin
Here's a rundown of what happens in MoMA. The program gets installed in its own directory, as it includes several DLLs and is itself a .NET binary.
Since MoMA is a .NET binary, I figured I could use it to analyze itself. Indeed, I was able to run MoMA under MoMA (out of sheer curiosity), and expected that it would come up clean.
The program runs as a wizard with several steps.
Step 1 shows an introductory screen explaining how MoMA works.
In Step 2, you choose your binaries and specify the version of Mono you're testing against. The screen includes a listbox that you fill with the binaries you want to test (you can test multiple binaries.) This step lets you choose individual files or an entire directory. For entire directories, you click through a tree until you get to the directory containing the binaries you want to test. Then you select the directory. The tool's listbox will fill up with all the DLL and .exe files it finds in that directory; you then have to manually remove the ones you don't want to test.
If you have a lot of binaries, this works great; otherwise, just click the button with a plus sign and add individual binaries. (I also discovered a little trick: The File-Open box where you can select individual files lets you select multiple files. So there are many ways to choose your binaries.)
For this particular test, I filled the listbox with the three DLLs and single executable file found in its directory. Then I clicked Next to move to the next screen in the wizard.
Step 3 in the wizard starts the scanning. The first thing the tool does is make sure the binaries are all indeed CLI binaries. If you choose, for example, a plain old Win32 DLL or .exe, the tool will alert you that it's skipping that file.
The screen then shows a summary of the problems found. The test on the tool's own binaries found no problems, as I suspected would be the case. It tested four categories, and each passed:
--All methods called exist in Mono, which means you're not calling methods that the Mono project hasn't implemented.
--No P/Invokes are called, which means you're not calling directly into the operating system.
--No methods that throw NotImplementedException are called, which means you're not calling methods that technically exist as a stub in Mono but haven't yet been coded. (Remember, Mono is an ongoing project.)
--No methods marked with [MonoTodo] are called, which is similar in nature to the previous category.
Step 4 is pretty interesting, as long as you don't abuse it: It lets you submit your results directly to the Mono project. Since I'm using MoMA to test itself, and since it passed with flying colors, I didn't click the "Submit Report" button. Instead, I just clicked Next to move to Step 5, which is really just three buttons that let you download Mono and some other tools.
More Serious Work
Testing the MoMA tool itself didn't yield much information, so I wanted to also test a bigger application. I wrote a quick little form-based application, but that didn't yield much either; it passed with no problems. I needed a bigger application to test.
While writing my own million-line, production-quality software to use as a test in this article certainly sounds exciting, I suspect my boss wouldn't be thrilled about the decade or so it would take to me to do so when I should be writing articles. So I decided to use the tool to analyze some existing programs written in .NET.
One such tool is Paint.NET, which is an excellent Paint tool that has evolved from a .NET version of the old die-hard Paint tool that comes with Windows to a full-featured tool that some say rivals Adobe Photoshop.
I used MoMA to process all the binaries that come with Paint.NET. In this case, the tool didn't pass with flying colors. (That's not a knock against Paint.NET, however; please keep reading.) For starters, the tool included several non-.NET assemblies, which were skipped. But the several other binaries were .NET assemblies; these were scanned.
This time the results were:
--All methods called exist in Mono. So far, so good.
--P/Invokes called: 157
--Methods called that throw NotImplementedException: 7
--Methods called marked with [MonoTodo]: 58
All of this meant that I couldn't run the program as is under the Mono run-time; the code would need to be changed. But what's nice about MoMA is that when it finds errors, it will give you a detailed report of what's wrong in an HTML file, offering up a View Detail Report link that opens a report in your default browser when you click on it.
The report is broken down by assembly in the left column, with columns to the right showing the problems from each of the four categories. Assemblies that have problems can be clicked on for full details, showing exactly the function calls that are an issue. In the case of Paint.NET, I saw a sprinkling of NotImplementedException and MonoTodo problems throughout the assemblies, but nothing major. I could also see that almost all the P/Invokes were within a single assembly. That's the assembly that would likely have to be overhauled to get the software to run under Mono.
Now, I don't know the developers of Paint.NET, and so I can't speak for them. But they do make it very clear in their FAQ that they have no intention of porting the software to Mono. Yet, interestingly, the MoMA tool shows they did an excellent job of isolating the system calls into a single assembly. I suspect that was intentional-not for the purpose of future porting, but rather the result of simply being good developers. (That's no surprise if you read their blog at http://blog.getpaint.net/. For example, they have an entry on using parameter validation in C# to ensure robust software.)
Of course, the DLL that contains these system calls is itself very large and clearly has a huge amount of code. So porting it would be no small job. But the MoMA does make it clear to me where the porting would need to be done if anybody cares to take on the job.
But Do We Need MoMA?
In a perfect world, you shouldn't need a tool like MoMA. I know that I have a tendency to raise the ire of some programmers by being a perfectionist; I've been accused of being overly idealistic in my goal of teaching programmers how to build perfect software. And, indeed, our industry has come a long way-users are no longer required to type cryptic commands into the command prompt, and software houses have realized that blaming the user every time something goes wrong really isn't good for business.
But part of creating perfect software means creating robust software. Creating robust software involves many factors, such as isolating your system calls into a single library (at least with the kind of GUI-oriented, user-friendly software we want on Windows and Linux). Creating code like this should come naturally to a good developer. If you unintentionally break the basic rules of sound software design, then you should fix them early on, and not after your software is finished and suddenly a client asks you to port it from Windows to Linux.
In other words, by the time you run MoMA, you should already have done whatever the tool ends up saying you need to do, without any intentions to port your software-just as the Paint.NET developers did.
But I need to qualify that statement. In fact, Mono isn't a 100 percent port of .NET. There are parts that are missing, and the MoMA results include a column for "Not Implemented" and another for "Missing". I, for one, am not about to refrain from using aspects of the .NET framework simply because I "might" want to port my software someday. (Case in point: The Mono project has no plans to implement the WPF, or Windows Presentation Foundation, API.)
So much for the perfect world I dream of.
Perhaps, then, the best thing to do is to isolate the calls-again, as in the Paint.NET software-so that if you do eventually need to port, you can. Or, if that's not possible, then you'll want to make sure the requirements don't require porting, and that the client understands that your software will not port.
The bottom line: I would use the MoMA tool only for projects where I'm quite sure I'm going to be porting the software. You know the routine: Use early, use often. After all, who would want to have a completed, solid software package with millions of lines of code, and only then discover that you have a thousand calls into unsupported functions? If you use MoMA, you probably won't find yourself in that situation.
In a perfect world, anyway.