Home‎ > ‎Uploads‎ > ‎

Unmanaged Exports

posted Jul 9, 2009 9:23 AM by Robert Giesecke   [ updated Jun 1, 2011 12:36 AM ]

What is this about?

I just got around uploading this little project of mine.

It is an MSBuild task that essentially allows you to export static functions from your .Net assemblies to be consumed as ordinary native DLL exports.

I've written this months ago and since it did what I needed it to do quite flawlessly, I never found the reason to change the IL changing part from ILDasm/ILAsm to Mono.Cecil.
I already use Cecil to easily find the attributes, I just don't use it to emit the changed IL. The result is, that it requires the .Net SDK, as it's the only redist that comes with ILAsm.exe.
However, since this is obviously only required during compile-time, it doesn't really pose a limitation when you already use Visual Studio or have the SDK installed on your build machine.
The resulting assembly will be a plain .Net assembly, but it will also look like a real DLL for consuming processes. (.Net Assemblies aren't really DLLs in the traditional sense.)



How does it work? 

If you want to create a new project that has exports, go grab  the C# project template. There's no reason to go through the manual process described in this section if you don't have to.

If you are not using C# or want to extend an existing project, you can follow these steps:

The guide is using C# and Visual Studio, but it works with any language that compiles using MSBuild and supports attributes.
The numbers in brackets refer to the ones in the image.
  • First extract the archive into your project dir, so that the files reside in a folder "DllExport"
  • Now tell solution explorer to show files that are not part of your project(1).
  • Then unload your project so that you can edit the project file directly inside VS(3).
    • Put this line directly under the <Project> root element
      <Import Project="DllExport\RGiesecke.DllExport.targets" />
  • Now, reload the project and include the DllExportAttribute.cs to your project by showing non-project files first(2).
    • You can also reference RGIesecke.DllExport.DllExportAttribute.dll, which contains this attribute as well.
      This is particularly handy if you don't use C#. It does not have to be redistributed along you library, though.
    • This reference will be removed from your assembly And the attribute will be removed from your methods when my build task is finished.

That's pretty much it.
You can write any kind of static method, decorate it with [DllExport](4) and use it from native code.

Restrictions

  • What you cannot do is use overloaded exported methods inside the same class, even if you provide them with different export names.
  • Another restriction is that you cannot call an exported method recursively. This seems to be a limitation of the runtime.

Samples

Basic sample

Showing how to use provide an export alias (the name which will be seen from consuming processes) and calling convention. (Default is stdcall)

class Test
{
  [DllExport("add", CallingConvention = CallingConvention.StdCall)]
  public static int Add(int left, int right)
  {
     return left + right;
  } 
}


Marshalling sample

You can also use the MarshalAsAttribute to control how the marshaller translates your .Net types into native types.
The example below shows how to mark a parameter to be returned to native code as an IUnknown-compatible interface reference.

btw, this is also how to pass objects between native and .Net ;-) 

[ComVisible(true)] 
[Guid("Some GUID"), 
 InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ISample
{
   // without MarshalAs(UnmanagedType.BStr), .Net will marshal these strings as single-byte Ansi!
   // BStr is equivalent to Delphi's WideString
   String Name
   {
     // this is how to add attributes to a getter's result parameter
     [returnMarshalAs(UnmanagedType.BStr)]
     get;
     // this is how to add attributes to a setter's value parameter
     [paramMarshalAs(UnmanagedType.BStr)]
     set;
   }

   int DoSomething(int value);
}

public class Sample : ISample
{
   public String Name{ getset; }

   public int DoSomething(int value)
   {
     return value + 1;
   }
}

static class Exports
{
   [DllExport]
   public static void CreateSampleInstance([MarshalAs(UnmanagedType.Interface)]out ISample instance)
   {
     instance = new Sample{ Name = "Test" };
   }
}


Changes

  • Version 1.1.1

    • I created a C# project template to make it very, very easy to setup the target and build task.
    • I also fixed some issues that could result in extreme slowdowns.
    • When you use the same export name for multiple methods, it will now create a warning which uses the pdb debug infos to point to the exact source locations.
  • Version 1.1.2

    • When provided with the location of lib.exe, it will create the .lib and .exp files that are required my MSVC
      • either autom. inside the VS IDE
      • having an environmental variable LibToolPath pointed to its directory
      • or adding a property LibToolPath to the project file
  • Version 1.1.3

    • In VS2008 and VS2010, the correct version of ILAsm/ILDasm will be used according to the TargetFramework. Only important for 2010 due to 4.0 / 2.0 runtime.
    • Temporary .def file will be removed as it isn't really required after having the .lib and .exp file.
    • Fixes to the .lib creation (export names had the real method name in the .lib, not the one that DllExport contained, while the DLL itself exported it correctly)
    • nicer Icon
    • TargetFramework is no longer set to 3.5 but left empty for the IDE to decide what makes sense (VS2005 obviously has no clue about this setting).
    • Autom. packaging of the C# project template and the archive on this page during build, to prevent uploading older versions.



License and stuff...
I haven't settled for a license, but 2 things to keep in mind:
  1. You can use it for whatever you want it to use for and give it to whoever you want to give it.
    In this regard it is as free as it can be.
  2. I will not be held responsible for anything that happens from using it.
    You use this build extension and something blows up, then you're on your own pal. ;-)

Download hints

The file below is not the project template. If that is what you are looking for, go right here.
Č
ċ
ď
Robert Giesecke,
Mar 7, 2011 1:50 PM