F# on Mono

I learned something cool yesterday - F# runs on Mono! That means I can mess with it without having to use VMware!

I got interested in F# from hearing about it on Hanselmintes and .NET Rocks. For those who haven't heard of it, F# is a functional programming language, similar to OCaml, built on .NET. It's actually not an "official Microsoft product," but rather a project out of Microsoft Research, which is pretty cool.

Incidentally, working Microsoft Research is on my list dream jobs. I mean, how many organizations can boast of having had two Turing Award winners on staff? How could you not want to work someplace like that?

Anyway, the thing that excites me about F# is the combination of functional programming and its status as a first-class .NET language. I've been meaning to get up to speed on functional programming for a few years now, but I've just never gotten around to it. Learning LISP or ML always seemed on par with refreshing my Prolog and Ada skills - an interesting exercise, but not profitable in terms of marketability. I mean, how many Standard ML listing have you ever seen on Monster?

However, it looks like functional programming may start pushing more into the main stream. The current trend in hardware is that CPU speeds are flattening out and performance gains are being made by adding more processors or cores. However, most code today is not written to use more than one core/proc at a time. So we're going to have to start parallelizing our code to fully take advantage of the hardware. That's where functional programming comes in. Pure functions, by definition, have no side effects. So if you're writing pure functional programs, parallelism becomes much easier, as you have no worries about thread safety and whatnot.

So with F# I can now learn functional programming while using .NET. This means that I can leverage some of my existing knowledge while learning the new language, which always makes things go faster and smoother. It also means that this learning experience has some vague marketability, i.e. I can at least count it as .NET experience. In other words, it's not one of those "off in left-field" learning ventures like if I took up Intercal or APL. I'm not going to feel (as much) like I could be making better use of my time.

Anyway, it turns out that installing F# on Ubuntu 8.04 wasn't quite as painless as I had hoped. On the up side, the F# site does supply a ZIP archive with generic Mono-compatibile binaries and full source (under the MS shared-source license). However, it seems the binaries don't quite work right with Mono 1.2.6. That's fixable, though, thanks to Laurent Le Brun's article on using F$ 1.9.4.17 on Mono. Basically, the important thing is to remember to pass mono the --runtime=v2.0.50727 option when running the F# compiler or F# binaries.

I haven't been blogging much lately, but hopefully I'll be posting back in the coming months with tid-bits on F#. It's been a while since I tried to learn a new language, especially a non-procedural one, so I'm looking forward to it.

Class of the Day: System.IO.FileInfo

Today I learned about a class in the .NET framework that I've never used: System.IO.FileInfo. I came across it while looking up the appropriate method to determine the size of a file for a unit test. I had expected that there would be a method in the System.IO.File class for this, but I was mistaken. It turns out the proper way is to use the Length property of a FileInfo instance.

FileInfo is kind of a weird class. In fact, I'm not really sure what the design impetus behind it was. It actually duplicates a lot of the behaviour of the File class, which I use all the time. In fact, it duplicates so much so that I would have expected the two to be a single class. It appears that the distinction is primarily that File is static while FileInfo is the instance equivalent, with some of the methods moved to properties. Although it's still not clear to me why they couldn't just have it all in one class, with methods overloaded for static and instance calls.

By way of summary, the FileInfo class inherits from FileSystemInfo. Some of the interesting properties include Directory, Exists, and Length, as well as both "regular" and UTC variants of properties for file access and creation time. Interesting methods include Encrypt() and Decrypt(), CopyTo() and MoveTo(), and SetAccessControl to set ACLs on the file. I wonder how Mono implements that last one. It's supported, but undocumented.

Playing with Monodevelop

This week I've been looking at Mono a bit more. I've been trying to spend more time on one technology at a time rather than jumping back and forth between a bunch of different things. So for this week, I'm putting off Ruby on Rails and doing Mono.

To me, being both a Windows and Linux programmer, Mono is a very good thing. If nothing else, it has the benefit of making mainstream Windows programming knowledge usable on Linux. And really, C# and the .NET framework are pretty good in their own right. Mono means that we have another good language and framework that brings with it a high degree of source- and binary-compatibility with Windows. It even has a GUI that isn't horrifyingly ugly like Java's Swing. What's not to like? Unless you're a rabid Microsoft hater, nothing.

So far, my complaints are mostly with the development tools. I'm using Monodevelop 0.12 from the Ubuntu Feisty repositories. While Monodevelop is a fork of SharpDevelop, it is, sadly, not quite as good. It's missing many of the nice little features #Develop has. However, my main complaint is those damn panels. You can't easily minimize groups of them and the positioning gets screwed up when you close them. And if you leave them open, you get stuck writing in a tiny code window.

It's also somewhat annoying that there is no WinForms designer for Mono. Apparently this is because appropriate designer surfaces haven't yet been implemented in the framework, but it's still annoying. After all, if you want to write a cross-platform GUI, WinForms is the choice.

My other complaint is gnunit2 version 1.2.3 that's included in Ubuntu. It has no options, doesn't reload the assembly between test runs, and includes a "preferences" menu item that doesn't do anything. Of course, there's always the NUnit plugin for Monodevelop, but the aforementioned panel annoyance means that isn't significantly better.

On the up side, a new version of Monodevelop came out today. I'll have to try it out and see if it's any better. I only hope that I don't end up having to recompile huge numbers of packages in order to get it working.

Sybase + ADO.NET = pain

I've been wrestling with Sybase again lately. Both last Thursday and today, I had to write a couple of small programs to update and reformat values in some Sybase tables at work. The programs themselves were very simple and took maybe half an hour to write. However, due to the pain inherent in connecting to Sybase, this quick update turned into a several-hour ordeal.

The database in question is running on , which is about three major versions out of date.

While I wrote the original client program in VB6 with ADO 2.6, I have now switched to C#. This is partly because VB6 sucks by comparison to C# and partly because I'm trying to make my skillset more marketable. So when I wrote those little update programs, I did it in C# with .NET 2.0. However, that didn't go so well.

Method 1 for connecting to database in question, which runs Sybase SQL Anywhere 7, was to use the System.Data.OleDb classes and just use the provider that worked with ADO 2.6. But, of course, it didn't work with ADO.NET. I even tried following the Sybase how-to on ADO.NET, but the ExecuteDataReader always resulted in a "no such interface supported" COM exception. The only difference was that I was using the ASAProv.70 provider instead of ASAProv.80, which I don't have. Presumably the older provider doesn't do ADO.NET.

The second attempt was to use the .NET 2.0 System.Data.Odbc classes. My reasoning was that, since ODBC is an industry standard supported by virtually everyone, using straight ODBC should work. And it did...sort of. I was able to connect to the database and read records without incident. The problem was with updating using OdbcCommand objects.

The code I used was pretty unremarkable, which is why I was a little surprised when it failed. It looked something like this:

OdbcConnection dbconn = new OdbcConnection("DSN=SomeDSN);
dbconn.Open();
/* Lots of unrelated code.... /*
string sql = "update MyTable set Bar = ?, Baz = ? where ID = ?;";
OdbcCommand cmd = new OdbcCommand(sql, dbconn);
cmd.Parameters.Add("Foo", OdbcType.Char, 15).Value = someVar;
cmd.Parameters.Add("Baz", OdbcType.Char, 15).Value = someOtherVar;
cmd.Parameters.Add("Foo", OdbcType.Int).Value = someIDVar;
int result = cmd.ExecuteNonQuery();

There shouldn't really be much to go wrong there. However, executing the command resulted in the following exception:

Exception System.Data.Odbc.OdbcException was thrown in debuggee:
ERROR [HY000] [Sybase][ODBC Driver][Adaptive Server Anywhere]General error: Host variables may not be used within a batch

Googling this message came up with this page from Sybase website, which helpfully tells me:


You have attempted to execute a batch which contains host variable references. Host variables are not supported within a batch.


That's it. Just a restatement of the error message. I find this to be fairly typical of Sybase documentation.

At this point, I was faced with two questions:
1) What the heck is a "host variable?"
2) Why am I getting a message that they aren't allowed?

The first question was answerable with a little Googling. As explained by this page from the IBM iSeries manual, a host variable is just what the name suggests: an external variable that gets used in an SQL statement. You can use them for input and output parameters when writing more complicated SQL programs.

For the second question, Google failed me, so I can only speculate as to the answer. My guess is that ADO.NET is translating the parameters into host variables rather than inserting the values directly into the SQL. Presumably my version of the ODBC provider doesn't support host variables, hence the error. It seems to make sense, but as I said, that's only a guess.

My final solution? After spending way too much time on fruitless searching and experimentation, I finally decided to just go the quick and dirty route of string concatenation. Instead of parameters, I just used a String.Replace to escape quotes and spliced the new field values directly into the SQL. It's ugly, and I wouldn't want to use it in a "real" program, but you know what? It works.

Crystal and XML DataSets

I've had some more Crystal Reports hatred today. I had to revisit an old program to add some reports to it. Due to our crappy development process and the cheapness of my boss, I did it in .NET 2.0, but with .NET 1.0 bundled Crystal Reports. Basically, I ended up with my main .NET 2.0 program and a separate .NET 1.0 executable to do the reports. I just shell out to the report program and pass it paths to the Crystal Report file and an ADO.NET XML file for the data. It's not pretty, but it could be a lot worse.

Given this setup, I thought adding a couple of simple reports would be a piece of cake. The reports were basically just table listings - putting "select * from foo;" into a user-friendly format. I figured it would take me an hour tops. Little did I know....

My problem was simply that the report didn't work. I had my report file and my ADO.NET dataset, I ran the report program, and the Crystal Report preview control threw up a "query engine error" message. If you're not familiar with Crystal, that pretty much just means that something bad happened when Crystal was trying to populate the report. It could be a problem connecting to the database, it could be a malformed query, or it could be that the alignment of the planets has raised evil spirits that are interfering with your computer. Nobody can say for sure and Crystal doesn't really give you any easy way to figure it out.

After some testing, it turned out that the problem was the xml:space attribute. Apparently the ADO.NET DataSet.WriteXML() method adds xml:space="preserve" to fields that contain only whitespace. However, Crystal Reports for Visual Studio .NET 2002 really doesn't like this and bombs out with a query engine error when it sees this. Just having whitespace-only fields is no problem, it's only when this attribute is present that Crystal freaks out.

So now at least I know what the problem is. The immediate fix is easy - just get rid of the spaces in the database and keep the user from putting in more. It still galls me a little that that's necessary, though. I would have thought Crystal would just ignore unrecognized attributes. But at this point, I shouldn't be surprised by the crappy things I find in Crystal.

MonoDevelop depends on Lynx?

I just installed MonoDevelop for the first time and I have a question: why does the Ubuntu MonoDevelop package depend on Metacity and Lynx? It makes no sense.

The Lynx part really gets me. What possible use could a graphical IDE have for a text-mode browser? Especially given that it depends on Firefox as well.

And what's worse, I can't even see where Lynx is getting pulled in. I don't see it in MonoDevelop's list of dependencies, but Adept doesn't want to install MonoDevelop without it. What's the deal?

Time to try Mono

Good news: Mono now has a working Visual Basic compiler. Oddly enough, it and the runtime are actually both implemented in VB.

Well, I guess that's not so odd actually. It would be odd if it were VB6, but VB.NET is really the bastard child of VB6 and C#. And it takes after C# more than VB6, so it's actually a pretty capable language. In fact, after moving to .NET after years of VB6, I'd say that the resembalence to VB6 is mostly superficial. Not that this is a bad thing - it's just that the guys who advocated calling VB.NETVisual Fred had a good point.

In other good news, I understand that these days, Mono also has support for Windows.Forms and a decent subset of .NET 2.0 now. It's really coming along! I guess it's time to download MonoDevelop and give .NET under Linux a try.

Crystal Reports deployment somewhat fixed

It seems I've sorta kinda fixed my Crystal Reports problem from yesterday. I say "sorta kinda" because I now have it working on other machines, but I don't know why it's working yet.

After getting close to the point of desparation yesterday, the solution process was simple: start adding things until it works or I reach the same configuration as my development workstation. Thus, I grabbed the Visual Studio .NET 2002 Professional CDs and got to work.

I started from a more or less default Windows XP Pro system running in VMware. I ran the setup package I had built yesterday, but still got the "query engine error" message. So I did a full .NET Framework 1.0 install, but still got the same message. Then I tried the .NET 1.0 service pack 3, but no change.

Next was Visual Studio. Of course, I didn't really want to install VS.NET on the end-user's PC, so I decided to run my test application after every step of the installation process.

It turns out I only needed one step. After the initial "update windows components" step, my test program worked. I don't know why. The only thing it installed were the FrontPage 2002 client stuff and the setup runtime files, whatever they are. I'm guessing the magic was in the latter, as I can't imagine what Crystal Reports has to do with FrontPage, but so far I'm just guessing.

Now I just need to figure out what magic library made the difference. Does anybody have any ideas? I'm pretty sure I didn't miss any of the libraries mentioned in the Crystal documentation. If only there were some kind of diff tool I could use for Windows installations....

Crystal Reports is pure evil

I've had a rotten day. Just rotten. The kind that makes you want to curl up on the couch for a week, doing nothing but eating ice cream and watching old movies. Except that we just started a diet and don't have a TV in the living room.

My trial today was Crystal Reports with Visual Studio .NET 2002. I have a bunch of reports and a handy little application that pass two file names (the Crystal report and an XML data set) and have it pop up a preview window. It does basically what I want and works pretty well. But only on my machine.

No matter what I try, I can't get it to work anyplace else. I've gone through the documentation and tried every suggestion that was even remotely applicable, and it still doesn't work.

My initial problem was with the CR registration. That was easily fixed by adding the regwiz.msm merge module to my setup project and setting my registration code in the properties. Rebuild and reinstall the setup package. Problem one down.

After that, I discovered that I needed a few other merge modules. The full list is managed.msm, database_access.msm, database_access_enu.msm, and regwiz.msm. OK, fair enough. So I add those, rebuild my setup project, and reinstall. Problem two down.

At this point, the good news is that I'm no longer getting weird DLL exceptions. The bad news is that now I'm getting nebulous "Query engine error" messages when I try to set the report viewer's reportsource.

This is really bad, because my research leads me to believe that there are just about a bazillion different causes for this error. I tried every remotely applicable solution I could find, but so far nothing is working. I've double checked that msvcr70.dll and msvcp70.dll are present, that crqe.dll is registered, and I've even tried reinstalling the entire .NET 1.0 runtime, which is supposed to be included with XP in the first place.

On top of that, I tried changing my reports and my code. I read a suggestion somewhere that reports using data sets with multiple unlinked tables can cause problems, so I changed the report to use a single table. That didn't work, so I changed the data set to contain only a single table. That didn't work either, so confirmed that the data set schema matched the one used to create the report.

At this point, I have now created a minimal report viewer application as a test. I grabbed my single-table XML file and stuck it on a network drive. Then I used the report expert (what a crappy name) to create a simple report using that file as the data source. Last, I created a simple one-form application with a report viewer control. It just loads the XML file into a data set, creates a report document and loads it with the simple report, sets the data source, and then sets the report document as the viewer's report source.

The result? It still doesn't work, even with all the complications removed. On the up side, this seems to confirm my suspicion that there is some kind of runtime dependency that I'm missing. On the down side, I have no idea what it could be....

More .NET gotchas

I ran into a couple more little "gotchas" in my .NET adventures this afternoon.

First, an ADO.NET gotcha. In my ignorance, while writing a parameterized insert query for a DataAdapter, I assumed that if a certain database field was automatically generated, such as an auto-number primary key, that I didn't need to specify it in the insert query. However, it doesn't work that way. When omitting those fields, on calling the DataAdapter.Update() method, I got a nice error saying I didn't supply enough fields, or something like that (I didn't write it down). Of course, this could be provider-specific (I was using the Jet 4.0 OLEDB provider), but I didn't feel like experimenting with it at the time.

The other gotcha was with the Combobox.AutoCompleteMode property. I had a combobox with AutoCompleteMode set to SuggestAppend with the source set to the drop-down list contents. I couldn't figure out why, for one of the entries, the appended text was cutting off half way through the string. It turns out that this was because the string in question had a slash in it. According to the documentation, "automatic completion appends all characters only up to and including the slash." I'm not sure why, though.

Configuring .NET 2.0 security

I made a rather annoying discovery today: the .NET framework 2.0 redistributable doesn't come with the configuration tool. Versions 1.0 and 1.1 both included the mscorcfg.msc configuration panel that allowed you to do things like change the security settings on assemblies and zones. But not version 2.0. If you want that, you have to install the .NET 2.0 SDK. This makes no sense to me, but that's the way they did it.

Fortunately, this isn't a deal-breaker, because the .NET framework does come with a command-line utility named caspol.exe, which allows you to change the .NET security policies. Unfortunately, the syntax used by caspol isn't exactly obvious. In fact, it's kind of obscure, especially if you just want to do a quick security boost on a perticular zone.

Luckily, a Google search brought up the exact command I needed:
caspol -machine -chggroup LocalIntranet_Zone FullTrust
This one simply elevates the Intranet zone to full trust. That allows my program to do little things like read it's configuration file from the network share directory. It's kind of hard to query the database when you can't read the file with the connection string and SQL statements.

Crystal and #Develop

It's report time in my current .NET project at work. By that, I mean it's time to write the code to generate paper reports on the data. And yes, they will be paper. For some reason, nobody around here ever wants to read anything off their monitor.

Currently, my plan is to push the data into a Crystal Report. I prefer the push model because I think it cuts down slightly on the deployment headaches. I prefer Crystal because ... - OK, I don't actually like Crystal Reports that much, but it's basically all we've got. There are other options, but they all suck a lot worse than Crystal Reports.

Actually, I should correct that last statement. As a matter of fact, we don't have Crystal Reports - we have Visual Studio .NET 2002, which comes with a version of Crystal Reports. This means that I'm now back to using VS.NET to write my reports and SharpDevelop to write my code.

Probably as a result of this, I'm having some problems with adding a CrystalReportViewer to a form in the SharpDevelop forms designer. I'm able to customize the sidebar to add the Crystal controls, but when I drop a CrystalReportViewer on the form, it doesn't work properly. In particular, SharpDevelop isn't displaying it - it shows up at the bottom of the form designer, but not on the actual form.

The fix for this is to simply add the following to the form's designer file:
Me.Controls.Add(Me.myCRViewerName)
That at least shows the control on the form, but it still doesn't function properly. I can't resize the control or even select it on the form. However, I can still access all the properties by clicking the object's icon at the bottom of the designer (what the hell is that area called, anyway?).

I guess we'll see how this goes. Perhaps it's due to using the .NET 1.0 Crystal libraries with the .NET 2.0 application. Or maybe SharpDevelop just doesn't like the Crystal controls. Or maybe I just screwed something up. Hopefully I'll figure it out relatively soon.

Bindings are fun

I can't believe it: I'm actually using data bound controls. Will wonders never cease?

This is a surprise because I always hated data bound controls in VB6. They worked well enough for simple things, but they just didn't have a lot of flexibility. I always found them to be one of those features that's great if you're willing to buy into the one true method of application development described in Microsoft's tutorials, but not to nice otherwise. Thus I tended not to use them too much. In fact, I pretty much only used data controls bound to data grids as a way of browsing data sets. I handled everything else in code.

However, I was pleasantly surprised by data binding with ADO.NET. For starters, the fact that ADO.NET data sets are disconnected eliminates the inflexibility over control of updates. This was a big improvement in and of itself.

The big one, though, was when I discovered the System.Windows.Forms.Binding class, which is what actually handles binding a control property to a data set field. It turns out that this class has two handy events: Format and Parse. These are raised when a control is populated from the data set and when the control value is propogated back to the data respectively.

Using these two events allowed me to do one of my favorite tricks with bound controls: condensing an object into a combo box. So, for example, I can have an EmployeeID field and bind a combo box to that, but have the box display the employee's full name, department, etc. It's actually quite nice. Things weren't nearly this nice in the bad old days of VB6, and so I am once again a happy Windows programmer.

Corollary: Be careful about Form.Load

A corollary to yesterday's abstract base class problem: be careful what you put in the Form.Load event. Apparently the form designer raises that too.

Basically, I had a VB.NET data input form and I wanted to create a specialized version of it, so I subclassed it. The problem is, I added some code to the base class's Load event handler. The code just retreived some comfiguration data from an XML file and used it to populate some combo boxes from a database. The intention was to just let the child class inherit this, since the boxes are all the same.

But, of course, it couldn't be that easy. As soon as I switched over to the child class and tried the form designer, SharpDevelop showed me a nice error message and backtrace. Apparently it was trying to open the XML file and couldn't find it. The path was relative to the application's directory, and at design time, "the application" is apparently the IDE.

I ended up just moving this code to a method that's only called in the child class's Load event handler. That seemed to fix the problem.

Seems like a bit of a hack, though. I would think that if it works at run-time, then it shouldn't give you problems at design-time. But apparently I'm alone on that.

Abstract classes and visual inheritance

You know what sucks? In .NET IDEs, including Visual Studio and SharpDevelop, the IDE needs to create an instance of a class to edit it in the form designer. The upshot of this is that you can't have both an abstract base class and visual inheritance.

By way of overview, the situation is basically this: I have a bunch of VB.NET data entry/update forms that are essentially all the same. They edit different database tables and so have different fields, but the underlying data access code is almost exactly the same. So, being an object-oriented kind of person, I figured I'd create a generic data entry form and inherit from that. That saves me having to do a copy-and-paste job for all the boiler-plate ADO.NET code.

Since this class only exists for other classes to inherit from it, it only makes sense mark it as abstract. Since VB.NET doesn't support multiple inheritance, if I want the derived classes to inherit from Windows.Forms.Form (which I do), then my abstract class has to inherit from it. But that breaks the forms designer because you can't create an instance of an abstract class.

The solution? Well, don't make the class abstract. Just keep it as a regular class and don't ever instantiate it in code. It's not "pure" object-oriented solution, but as far as I can tell, it's the only one that works.