So I haven’t worked on my WatiN Mac os x port for a few days, but I got back into it last night. The main hurdle at the moment is that the System.Diagnostics.Process class is not fully baked on Mono 2.4 on the Mac platform, because it doesn’t implement the /proc filesystem that linux does.

The correct thing to do would be to learn how the Mach kernel process structure works and fix mono, but I haven’t programmed in non managed C since 1990 and I would be very dangerous, especially at such a low level in mono.

So the first approach was to use the parts of mono that were working, namley Proces.Start() to call a shell script that I wrote to /tmp, then redirect standard out to a file, then parse that file and act accordingly.

StreamWriter sw = new StreamWriter("/tmp/watin-listprocess.sh",false);
sw.WriteLine (@"ps x | grep" + processname + " | grep -v grep > /tmp/watin-processlist.out");
sw.Flush();
sw.Close();

Process shScriptProcess = new Process();
shScriptProcess.StartInfo = new ProcessStartInfo("/bin/sh","/tmp/watin-listprocesses.sh");
shScriptProcess.Start();

A hack, yes, in fact a very messy hack. So messy there had to be a better way.

And there is …

Enter Monobjc. Monobjc is a managed wrapper around the Mac os x Cocoa API’s. A quick search pointed to the NSTask and NSPipe classes as a way to start a process with NSTask, and capture standard out with NSPipe, then parse the result.

A much more technically correct solution.

public static void Main(string[] args)
{
// spin up the objective-c runtime
ObjectiveCRuntime.LoadFramework("Cocoa");
ObjectiveCRuntime.Initialize();
NSAutoreleasePool pool = new NSAutoreleasePool();

// Create our process
NSTask task = new NSTask();
NSPipe standardOut = new NSPipe();
task.StandardOutput = standardOut;
task.LaunchPath = @"/bin/ps";

// add some arguments
NSString argumentString = new NSString("-ax");
NSArray arguments = NSArray.ArrayWithObject(argumentString);
task.Arguments = arguments;

// We have liftoff
task.Launch();

// Parse the output and display it to the console
NSData output = standardOut.FileHandleForReading.ReadDataToEndOfFile;
NSString outString = new NSString(output,NSStringEncoding.NSUTF8StringEncoding);
Console.WriteLine(outString);

// Dipose our objects, gotta love reference counting
pool.Release();
}

So this needs to be fully baked in and then put into my Watin on mac os x port and then it should work stand alone, without shell script assistance.

I didn’t think it would be this easy, but it’s working. I can now run a basic WatiN test on mac os x using mono.

The code is here http://code.google.com/p/mcwatin/. All credit to the WatiN guys, all I did was remove stuff :-)

Here’s what you need to get this running

Mono 2.4 and MonoDevelop preview for mac os x from here
The source code from http://code.google.com/p/mcwatin/
Firefox 3 with JSSH installed.

start firefox up with the following command line

/Applications/Firefox.app/Contents/MacOS/firefox -jssh

Then run the test. It will work but fail as exceptions are being thrown in the dispose method.

1. Get a subset of WatiN to compile with no PInvokes done

2. Get McWatiN to successfully start firefox. done

3. get McWatiN to run a basic test. done

4. Replace all the windows and IE stuff that was removed with code from Watir.

There is a little project called Monoobjc which is a mono to Objective C bridge that allows you to use the native Mac OS X libraries in .net code on the mac.

That is pretty cool, but one of the features is to allow the .app folder to contain the mono dll’s that it needs and execute with out the mono runtime installed.

So if you take that one step further, it should be possible to build an iPhone app, the same way and just bundle the mono code along for the ride.

So I wonder how long it will take until this happens, my bet is someone will do it in less than six months.

Dumbarton is an LGPL library that allows Objective C code to call in to a Mono hosted assembly. This falls into the “haven’t used it myself” category, but if it works as advertised, should allow a native Cocoa front end on top of the .net framework doing the heavy lifting. Nice.

Mono is getting very mature these days. I still wouldn’t want to run a real production application on it, but it just keeps getting better and better.

I was having a quick look through the windows forms page on the mono site, and I noticed that they now support :”a driver for native Mac OS X support (no X11 required).” Wohoo. Let’s give it a whirl.

The first thing you need to do is get the latest mono binary for Mac OS X. At the time of writing this was version 1.8.1.1, which is available here.

Once mono is installed, you should be able to create a new file called HelloWorld.cs which contains the following code hello world snippet from Charles Petzold’s: Programming Microsoft Windows with c#.

using System.Windows.Forms;

class HelloWorld
{
    public static void Main()
    {
	MessageBox.Show("Hello Windows Forms on Mac os X");
    }
}

Once you have saved the file you can compile it with the following:

mcs /t:winexe /r:system.drawing.dll /r:system.windows.forms.dll HelloWorld.cs

This will produce an exe called HelloWorld.exe. Once you have the exe then you need to wrap it up so it can execute on the macintosh. To wrap the exe you neeed to use the macpac utility to turn it into a stand alone Macintosh application. To wrap the main.exe we just built you need to use the following command:

macpack -a:Main.exe -o:. -m:1 -n:Main

If all goes well you should be able to click on the new main.app that was generated and enjoy the following.

Cool.

I have been tinkering with Mono on my PowerBook since the 1.0.1 release, and whilst I wouldn’t consider running any production software on it, it is great to be able to develop in C# and run simple ASP.NET pages on Mac OS X.

That said, despite the installer packages that are available, it definitely falls victim to the same problems as the majority of all open source projects; you need a lot of patience and smarts to get things working.

Take ASP.NET as an example. To get mod_mono working on my machine, I had to get the latest sources from CVS, the day after the 1.0.1 release was made. Whilst this is not a huge issue in itself, if this was a commercial product, the company releasing it would be lynched. Why, let me explain.

mod_mono and XSP not working on Mac OS X was a Severity 1 bug, with part of the intial mono release but they shipped it anyway. A Severity 1 bug is typically defined as a “Major crash or data loss with no workaround.” In the land of commercial software development, you simply do not ship with this type of bug present, because there is no point. The last thing that you customers want to hear is: “Dear Mr Customer, here is our product, sorry though it doesn’t work because of a Sev 1 bug.”

But Mono is open source, and no money changes hands, so we can let this slip and wait for the next release, and I’ll try and sort it myself. The next problem was that the bug was marked resolved in the bug tracking system, and yet (at the time of writing), the fix is still not yet released. There are no notes in the defect saying which version it was fixed in, and it required a: “Grab the latest release from CVS and see what happens” approach at solving the problem.

I don’t want to use this post as an attack on Mono, it is bloody brilliant what these guys have done. I just wanted to give an example of how open source projects could lift their game and follow the same conventions used in the commercial software world. And that is: if it’s broken it ain’t done, no matter how important your ship date is.