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.
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
SamplesBasic sampleShowing 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 sampleYou 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 [return: MarshalAs(UnmanagedType.BStr)] get; // this is how to add attributes to a setter's value parameter [param: MarshalAs(UnmanagedType.BStr)] set; } int DoSomething(int value); } public class Sample : ISample { public String Name{ get; set; } 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
License and stuff... I haven't settled for a license, but 2 things to keep in mind:
Download hintsThe file below is not the project template. If that is what you are looking for, go right here. |
