ITypeResolutionService for constructed / generic .net types

If you are writing your own customized form designer, one of the services you need to provide is ITypeResolutionService.
Given a name of type, this service is responsible for returning corresponding "System.Type".

A "Type" can be one of following :

1. default type (defined in one of the standard .net assemblies e.g. mscorlib.dll or system.dll etc provided by .net framework)
"System.Type" for such types can be retrieved using Type.GetType() API.
2 custom type defined in user / 3rd party assemblies:
For such types, you need to know the assembly name in which this type is defined.
First you have to load the Assembly and then look up for that type name in loaded assembly as below:

Assembly asm = Assembly.Load("referenced assembly name");
asm.GetType(strTypename);


Now let's see how .net generic types are represented.
Let's say we create a generic class ABC < T,U > in "XYZ" assembly.
Internal representation for ABC class would be ABC`2
(i.e. classname followed by backtick followed by number of generic type parameters).

To constuct an object of this class we have to provide 2 types such as
ABC < string,int > obj;
Here type of obj is a constructed type and is internally represented as "ABC`2[string,int]".

Now if you try to load Type for "ABC`2[string,int]" from XYZ assembly, it returns null because assembly knows about generic type "ABC`2". It doesn't know about its constructed types.
So to return System.Type for such constructed types, you need to first get its generic type from assembly and then on that type call Type.MakeGenericType() passing a Type array(Type[ ]) for its type arguments.

Here is a sample implementation of ITypeResolutionService:


Using System;

MyTypeResolutionService : ITypeResolutionService
{
public Type GetType(String name)
{
Type t = Type.GetType(name);
if(t == null)
{
// type is defined in some assembly.
//name must include name of assembly.

String assemblyName = GetAsmName(name);//helper method
if(String.IsNullOrEmpty(assemblyName))
return null;

String typeName = GetTypeName(name); //helper method

// check if this is a consturected type
String typeArgs;
bool isConstructed = false;
// look for backtick.
// if present means a generic type
int pos = name.IndexOf('`');
if(pos != -1)
{
// look for square bracket.
// if present means a constructed type.
pos = name.IndexOf('[',pos);
if(pos != -1){
isConstructed = true;
// get "abc'2" from "abc`2[string,int]"
String genericTypeName = typeName.SubString(0,pos);
// get "string,int" from "abc`2[string,int]"
typeArgs=
typeName.SubString(pos+1,typeName.Length - 1);

typeName = genericTypeName;
}
}


Assembly asm = Assembly.Load(assemblyName);
t = asm.GetType(typeName);

if( t != null && isConstructed)
{
Type[] types = getArgTypes(typeArgs);
//TODO: if types is null, return null
return t.MakeGenericType(types); .
}
}

return t;
} // end of GetType()

// here comma separated typeArgs passed is likely to contain
// again some other constructed types
// In such case GetType() is called recursively for all nested
// constructed typeArgs ..
private Type[] getArgTypes(String name)
{
// first split type argument names;
List < String > argList = new ArrayList < String > ();
int brackets;
int argStartpos = 0;
for(i =0;i < name.Length;i++)
{
if(name[i] == '[')
brackets++;
else if(name[i] == ']')
brackets--;
else if(name[i] == ',' && brackets == 0)
{
argList.Add(name.SubString(argStartpos,i));
// point to start of next type argument
argStartpos = i+1; .
}
}
// add the last type arg
argList.Add(name.SubString(argStartpos);

//now call GetType() for each typeArg in argList
Type[] retTypes = new Type[argList.Count];
for(int i=0;i < argList.Count;i++)
retTypes[i] = GetType(argList[i]);

return retTypes;
} // end of getTypeArgs()
} // end of MyTypeResolutionService

A tale of eclipse bundle classpath, jre6 and managed Assemblies

Recently I ported an eclipse RCP application from jre 5 to jre6.
The application worked fine with jre 5 but failed when run with jre6.
Problem was in the native dll that tried to access code in manged assembly.

My Eclipse application invoked a native method which in turn attempted to access code in managed code in a .net assembly. .NET CLR runtime loads an assembly when it is referenced first time. CLR runtime looks for that assembly in the APPDOMAIN base directory. With eclipse 3.4.2 and jre5, APPDOMAIN base directory happens to be eclipse bundle class path (".metadata/.plugins/org.eclipse.pde.core/yourlaunchConfigName/org.eclipse.osgi/bundles/someIntegerbundleID/1/.cp").

So RCP application copied all required managed assemblies in this eclipse bundle class path before invoking native method. And CLR runtime happily loaded them.

However when RCP application was run with JRE6 under eclispe 342, the APPDOMAIN base directory was JRE_HOME/bin. AND CLR runtime didn't find required assemblies here. So that was the reason for failure.

Fix for this is to use something called AssemblyResolver in .net terminolory. If CLR runtime can not find an assembly in APPDOMAIN base directory, it looks for any regirstered AssemblyResolvers. If one found, it notifies this resolver to load the required assembly. All this resolver did is to check the name of assembly to be loaded if it is the one used by RCP application, it loaded this assembly from its installed location.

I had to write Assembly Resolver in another mixed mode dll and register this resolver from native dll before accessing managed code.

Nightmares of nightly builds

If you are writing a dll for nt and nt64 bit platform here are some tips:

1. /clr:oldsyntax works on "nt" but fails to build on "nt64" bit platform. Use /clr:pure syntax instead.

2. /clr and /MTD options work fine in Debug mode. For release mode make sure you use /MD option.

3. when you add a resource file (.rc) to your vc project, it also generates resource.h. So make sure you checkin resource.h as well in your source control to avoid nightly build errors.

4. if you are using .vcproj file for "nt" platform, you may also need to create vcproj_9 for nt64 and you need to create this using visual studion 2008 or higher.

5. Make sure that you build your dll manually on both platforms before source control checkin.