I recently spent some time searching for a good open-source .NET HL7 parser. The pickings were pretty slim, and the best option appears to be NHapi--which is a fairly new port of the popular Hapi Java HL7 parser. I had to dig surprising deep into the search results to find NHapi, and though the code seems solid, the project is not extremely active. Another big weakness is the complete lack of API documentation and code samples. You can figure out the essentials from the Hapi Javadoc and sample code, but that's a bit of a pain so I've generated MS Help API docs for each HL7 version supported by NHapi. They are available in a single zip file here (80MB).
Wednesday, September 05, 2007
Monday, November 07, 2005
Buy this book!
I haven't written much here lately, but I wanted to mention that my friend Tom Copeland, founder of the PMD project, has just published his first book! It's called PMD Applied and you can buy a copy here. What most impresses me about Tom is that he could write this book with five small children in the house! I can barely find enough quiet time to blog with just four!
If you care about code quality why aren't you using PMD?!
Wednesday, August 24, 2005
Ant Humor
Wednesday, August 17, 2005
Running OpenNETCF 1.3 On NET PC
The hardest part about this was getting OpenNETCF 1.3 to run on the desktop. Here's why: The OpenNETCF.Configuration.ConfigurationSettings class uses System.IO.Path internally when figuring out where to look for config files. It passes a Pocket PC compliant path string to Path.Combine() at one point, and, since the code is actually running on the NET PC framework, Path.Combine() throws an ArgumentException.
The only solution (short of rebuilding the OpenNETCF library) is to pre-install a different implementation of OpenNETCF.IConfigurationSystem before the default implementation is installed. There's no public API for installing your custom IConfigurationSystem; you must install it via reflection calls which bypass the restrictive access modifiers. (Obviously, this is a major hack not suitable for production code.) Here's an example of such an IConfigurationSystem implementation which simply passes configuration requests through to the standard NET PC ConfigurationSettings class:
using System;
using System.Reflection;
using ONETConfig = OpenNETCF.Configuration;
using MSConfig = System.Configuration;
namespace Test.PCUtil
{
public class PassThruConfigSystem : ONETConfig.IConfigurationSystem
{
public static void InstallForOpenNETCF()
{
Type configSettingsType = typeof (ONETConfig.ConfigurationSettings);
lock (configSettingsType)
{
// setting them again will cause an exception
Object existingConfig = configSettingsType.InvokeMember(
"configSystem",
(BindingFlags.Static |
BindingFlags.NonPublic |
BindingFlags.GetField),
null,
null,
null);
if (existingConfig == null)
{
configSettingsType.InvokeMember(
"SetConfigurationSystem",
(BindingFlags.Static |
BindingFlags.NonPublic |
BindingFlags.InvokeMethod),
null,
null,
new object[] {new PassThruConfigSystem()});
}
}
}
public PassThruConfigSystem()
{
}
public object GetConfig(string configKey)
{
return MSConfig.ConfigurationSettings.GetConfig(configKey);
}
public void Init()
{
}
}
}
In my next post I'll show you how to automatically install the PassThruConfigSystem when running unit tests with NUnit, which presents its own unique challenges!
Saturday, August 13, 2005
OpenNETCF 1.3 Reinstall Bug
- Open this file: 'C:\Documents and Settings\[user profile]\Application Data\Microsoft\visualstudio\devices\7.1\conman_ds_package.xsl'. Make sure you open the right one as there may be copies under each user profile on your PC as well as under the 'All Users' profile.
- Save a backup copy.
- Find the following PACKAGE element: '<PACKAGE Name="OpenNETCF.dll" InvariantName="OpenNETCF.dll" ReadOnly="true" Protected="true" xmlns="">'
- Delete the entire PACKAGE element and all of its contents. (It's quite long--about 400 lines.) You may also need to restart Visual Studio if it's open.
Friday, July 29, 2005
PropertyInfo.GetHashCode() Generates an InvalidCastException on NET CF
PropertyInfo[] properties = myType.GetProperties();
Hashtable propertyMap = new Hashtable();
foreach (PropertyInfo propInfo in properties)
{
if (propInfo.GetCustomAttributes(true).Length > 0)
{
propertyMap.Add(propInfo, "Something Important");
}
}
At line 7 where the Hashtable is populated the code was blowing up with an InvalidCastException. With very little NET experience under my belt at that point it was a bit of a headscratcher. I knew that C# allowed extensible type casting and conversion (one of the areas where it improves on Java). Had our initial port changes broken some magic type conversion code that was called from deep inside the Hashtable class?
Type.GetProperties() actually returns an internal subclass of PropertyInfo called RuntimePropertyInfo. I fired up Reflector and started digging around in the mscorlib dll (version 1.0.5000.0 from NET CF 1.1). I figured the best place to start would be the GetHashCode() and Equals() methods since those are all the Hashtable implementation should be calling. (At that point I hadn't yet figured out that I could expand the non-user code part of the stack trace in the debugger to see where the exception originated--and of course NET CF doesn't include programmatic access to stack traces.) Sure enough there was something suspicious in the RuntimePropertyInfo class:
private IntPtr _pData;
private RuntimeType _pRefClass;
<snip>
public override int GetHashCode()
{
return (((int) this._pData) + ((int) this._pRefClass));
}
Notice that _pRefClass is cast to an int in GetHashCode() even though it's declared as a RuntimeType! I opened up the NET PC version of mscorlib and the RuntimePropertyInfo code was almost exactly the same--except that _pRefClass was declared as an IntPtr. I was still a little suspicious as I've learned that it's usually a bad idea to blame bugs on library code before you take a hard look at your own. So I ran the simplest snippet of code I could think of to test my theory:
PropertyInfo[] propsInfo = "".GetType().GetProperties();
foreach (PropertyInfo propInfo in propsInfo)
{
propInfo.GetHashCode();
}
Sure enough, the call to propInfo.GetHashCode() threw an InvalidCastException! But now I was even more puzzled. How could RuntimePropertyInfo have been compiled with an invalid cast from RuntimeType to int? RuntimeType does not define an explicit cast to int; moreover the cast actually does fail at runtime as you would expect. The best answer I've come up with so far is this post which points out that there may be some CLR magic going on with RuntimeType as it also throws a NotSupportedException in its only constructor. That's not a very satisfying explanation, but I'll have to let it stand for now.
Wednesday, July 20, 2005
Porting from NET PC to NET CF
Here's the situation: The NET PC version of the framework was written by a large development team with no consideration for the NET CF port. Just two of us are porting the framework, and we don't have enough time to rewrite it from scratch, so we've developed some processes that will help us merge-in future features and fixes added by the PC team. To make matters worse the CF and PC versions of the framework must reside in separate version control systems (NET PC in CVS, NET CF in Starteam) so we can't even leverage the branching and merging capabilities of those tools to manage the differences between the two products.
Thirty-to-forty percent of the way through the port, we've run into a number of interesting issues and incompatibilities between NET PC and NET CF which I'll begin writing about in my next post. Besides the obvious limitations of the compact framework (many, many of the NET PC classes and methods simply don't exist on NET CF), there are major performance considerations and internal differences (bugs?) between the two .NET API implementations that cause some very hard-to-find bugs (many, many thanks to Lutz Roeder for his .NET decompiler!) when you simply move code from one platform to the other. The two of us doing the port have many years of Java development experience but had just a few months experience with C# and .NET (PC or CF) on one small mobile application, so it has been a quite rapid learning experience.