# Wednesday, 23 March 2011
« IKVM.NET 0.46 Release Candidate 1 | Main | IKVM.NET 0.46 Released »
IKVM.Reflection Update

Missing Members

To support mcs I previously added some limited support for missing members. This has now been extended substantially to allow full fidelity reading/writing of assemblies. Normally IKVM.Reflection behaves like System.Reflection in how it tries to resolve member and assembly references, but now it is possible to customize this to enable reading a module without resolving any dependencies.

Roundtripping

To test this version of IKVM.Reflection, I've roundtripped 1758 (non-C++/CLI) modules from the CLR GAC plus the Mono 2.10 directories on my system. Roundtripping here means reading in the module and writing it back out. The original module and resulting module are both disassembled with ildasm and the resulting IL files compared (after going through two dozen regex replacements to filter out some trivial differences).

The code I used for the roundtripping is an (d)evolution of the linker prototype code. It is available here. Note that it is very far from production quality, but does make for a good case study in how to use the advanced IKVM.Reflection functionality.

Unmanaged Exports

A cool CLR feature is the ability to export static managed methods as unmanaged DLL exports. The only languages that support this, AFAIK, are C++/CLI and ILASM, but now you easily do it with IKVM.Reflection as well. Below is a Hello World example. Note that while this example exports a global method, you can export any static method. Note also that the string passed in must be ASCII, may be null and that the ordinal must be a 16 bit value that is unique (in the scope of the module).

using IKVM.Reflection;
using IKVM.Reflection.Emit;

class Demo
{
  static void Main()
  {
    var universe = new Universe();
    var name = new AssemblyName("ExportDemo");
    var ab = universe.DefineDynamicAssembly(name, AssemblyBuilderAccess.Save);
    var modb = ab.DefineDynamicModule("ExportDemo", "ExportDemo.dll");
    var mb = modb.DefineGlobalMethod("HelloWorld", MethodAttributes.Static, null, null);
    var ilgen = mb.GetILGenerator();
    ilgen.EmitWriteLine("Hello World");
    ilgen.Emit(OpCodes.Ret);
    mb.__AddUnmanagedExport("HelloWorld", 1);
    modb.CreateGlobalFunctions();
    ab.Save("ExportDemo.dll", PortableExecutableKinds.Required32Bit, ImageFileMachine.I386);
  }
}

The resulting ExportDemo.dll can now be called from native code in the usual ways, for example:

    typedef void (__stdcall * PROC)();
    HMODULE hmod = LoadLibrary(L"ExportDemo.dll");
    PROC proc = (PROC)GetProcAddress(hmod, "HelloWorld");
    proc();

Creating unmanaged exports is supported for I386 and AMD64. Itanium (IA64) support has not been implemented.

What's Missing

It should now be possible to write an ILDASM clone using IKVM.Reflection, however to be able to write ILASM there are still a couple of things missing:

  • Function pointers (used by C++/CLI).
  • API to create missing type.
  • Preserving interleaved modopt/modreq ordering.
  • Various C++/CLI quirks (e.g. custom modifiers on local variable signatures).
  • Ability to set file alignment.

Changes:

  • Added support for using missing members in emitted assembly.
  • If you build IKVM.Reflection with the STABLE_SORT symbol defined, all metadata sorting will be done in a stable way, thus retaining the order in which the items are defined.
  • When target is .NET 1.x adjust the type name writing algorithm to match the quirks of the Microsoft .NET 1.x metadata writer.
  • Fixed Type.StructLayoutAttribute to be (bug) compatible with .NET.
  • Don't map unspecified pinvoke calling convention to winapi.
  • Don't needlessly read the contents of a resource module.
  • Automatically emit .NET 1.x compatible declarative security when appropriate.
  • Added support for having multiple mscorlib versions in a universe.
  • Added Assembly.__AssemblyFlags property.
  • Added CustomAttributeBuilder.__FromBlob().
  • Added CustomAttributeBuilder.__MakeTypedArgument() to make it possible to box an enum or create an array of an enum type in a location typed as object.
  • Added ILGenerator.__MaxStackSize property.
  • Added EventInfo.__GetMethods().
  • Added FieldBuilder.__SetReadOnlyDataAndRVA().
  • Added FieldInfo.__FieldRVA property.
  • Added FieldInfo.__GetFieldOnTypeDefinition().
  • Added ManifestResourceInfo.__ResourceAttributes property.
  • Added MethodBase.__GetMethodOnTypeDefinition().
  • Added MethodBuilder.__AddUnmanagedExport().
  • Added MethodBuilder.__SetCallingConvention().
  • Added Module.__ImageBase property.
  • Added Module.__GetCustomAttributesFor().
  • Added Module.__GetExportedTypes().
  • Added Module.__GetReferencedModules().
  • Added Module.__GetReferencedTypes().
  • Added Module.__GetSectionInfo().
  • Added Module.__ModuleHash property (for resource modules).
  • Added Module.__ReadDataFromRVA().
  • Added Module.__ResolveReferencedAssemblies().
  • Added Module.__StackReserve property.
  • Added ModuleBuilder.__AddModule().
  • Added ModuleBuilder.__AddModuleReference().
  • Added ModuleBuilder.__AddAssemblyReference(AssemblyName, Assembly) overload.
  • Added ModuleBuilder.__AddUnmanagedExportStub().
  • Added ModuleBuilder.__AddVTableFixups().
  • Added ModuleBuilder.__SetCustomAttributeFor().
  • Added ModuleBuilder.__SetModuleVersionId().
  • Added ModuleBuilder.__SetStackReserve().
  • Added PropertyInfo.__CallingConvention property.
  • Added Type.__GetLayout().
  • Added Type.__CreateMissingMethod(), Type.__CreateMissingField() and MissingGenericMethodBuilder.
  • Added Type.__GetArraySizes() and Type.__GetArrayLowerBounds().
  • Added Type.__MakeArrayType() overload that takes sizes and lower bounds.
  • Added TypeBuilder.__SetLayout().

Binary available here: IKVM.Reflection-0.47.4098.zip

Wednesday, 23 March 2011 08:04:24 (W. Europe Standard Time, UTC+01:00)  #    Comments [6]
Wednesday, 23 March 2011 10:19:40 (W. Europe Standard Time, UTC+01:00)
Wow! Well, none of us knew that. So - does that imply with the shiny goodness of IKVM.Reflection that it would be possible to write a tool that, given an existing assembly, would write out an assembly that has native exports that call the static methods in the original assembly?

And maybe emitted a C and C++ header too?

That's a whole new slant on embedding the CLR in a host process.
James Mansion
Wednesday, 23 March 2011 10:30:52 (W. Europe Standard Time, UTC+01:00)
Yes, if the methods are public. You could also use the roundtripping code to modify the original assembly to add the unmanaged exports. Of course, this can also be done by using ildasm/ilasm and some text processing.

If you want to "statically" link, you'll also need to create an import library, but generating a dummy C file and a .DEF file that exports the same methods shouldn't be hard.
Wednesday, 23 March 2011 13:35:37 (W. Europe Standard Time, UTC+01:00)
Delphi for .NET also supported unmanaged DLL exports - reverse P/Invoke, since its release in 2003.
Thursday, 24 March 2011 16:10:31 (W. Europe Standard Time, UTC+01:00)
I've been working with C#/.NET/Mono since practically day 1, how have I never heard of this?! Will Mono define an "official" way of automagically doing this such as DllExportAttribute from here http://sites.google.com/site/robertgiesecke/Home/uploads/unmanagedexports for example?
Joe
Thursday, 24 March 2011 16:35:01 (W. Europe Standard Time, UTC+01:00)
AFAIK, Mono doesn't support this feature.
Saturday, 26 March 2011 01:28:56 (W. Europe Standard Time, UTC+01:00)
I tried to gather all the info I could on this topic here:

http://stackoverflow.com/questions/5170019/is-it-possible-to-execute-a-net-dll-without-an-exe-to-load-it/5252516#5252516
Name
E-mail
Home page

I apologize for the lameness of this, but the comment spam was driving me nuts. In order to be able to post a comment, you need to answer a simple question. Hopefully this question is easy enough not to annoy serious commenters, but hard enough to keep the spammers away.

Anti-Spam Question: What method on java.lang.System returns an object's original hashcode (i.e. the one that would be returned by java.lang.Object.hashCode() if it wasn't overridden)? (case is significant)

Answer:  
Comment (HTML not allowed)  

Live Comment Preview