Introduction
The following article discusses how one would achieve exporting a function from C#/VB.NET code to be used in native code (such as C/C++). I've seen a few methods around the internet about doing this, but most of them require you to tediously add things to your project to identify methods that will be exported, such as this. With that, these articles inspired this project which is an extension of the IL-compiler from one of my previous articles.
How it works
As I previously mentioned, this article is built upon one of my previous articles (link above). This required the InlineILCompiler.exe tool from the previous article.
The way this works is I have modified InlineILCompiler.exe to add a new directive which I called .exportnative. This directive marks methods so that the tool knows what to export.
Here's how the magic works: Find out how many methods are being invoked. Once this is done, we add
.vtfixup directives and .data directives for each function. With only one method being exported, here's a sample:
.vtfixup [1] int32 fromunmanaged at VT1
.data VT1 = int32(0)
According to this, it's also important to note that the
.corflags directive needs to be changed to 2. I haven't actually tested if it works without this, but as far as I could see, Delphi .NET uses this
corflag as well when exporting functions so I assumed it was required.
.corflags 0x00000002
According to what I saw in Delphi.NET as well, a method must also apply a modopt() declaring a calling convention. I chose to always use
stdcall as a standard. So, we end up with an exported method having the following definition:
.method public hidebysig static void modopt([mscorlib]System.Runtime.CompilerServices.CallConvStdcall) DllMain() cil managed
Inside of that method, we need to declare the virtual table entry it is associated with as well as the export entry and name.
.vtentry 1 : 1
.export [1] as DllMain
This is done for every method being exported which is all handled automatically by InlineILCompiler.exe.
Implementation
Exporting a method is incredibly easy. I'm not going to provide any elaborate guides about marshalling between native code and managed code as I think you
can find other articles which cover this fairly well. As a rule of thumb, your methods shouldn't return anything greater than 4 bytes (int),
anything larger than that should be passed using a ref or out parameter.
It's important to note
that the name you use with .exportnative must be the same as the .NET function's name and that method must be
static.
Here's an example of an exported function (C#):
public struct Foo
{
public int A;
public int B;
public int C;
}
public static void CreateFoo(out Foo foo)
{
#if IL
.exportnative CreateFoo
#endif
foo = new Foo();
foo.A = 22;
foo.B = 44;
foo.C = 55;
}
And now, here's using the code (C):
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <Windows.h>
struct Foo_t
{
int32_t A;
int32_t B;
int32_t C;
};
typedef void (__stdcall *LPFCREATEFOO)(struct Foo_t *foo);
int main(void)
{
HMODULE mod;
LPFCREATEFOO CreateFoo;
struct Foo_t foo;
mod = LoadLibraryA("ExportNativeTest.dll");
CreateFoo = (LPFCREATEFOO)GetProcAddress(mod, "CreateFoo");
CreateFoo(&foo);
printf("%d %d %d\r\n", foo.A, foo.B, foo.C);
return 0;
}Closing Remarks
I find it kind of disappointing Microsoft has a standardized DllImport attribute but not a standardized a
DllExport attribute and such hacks need to be developed to achieve it. I expected .NET 4.5 to have such an attribute,
but alas it did not which was disappointing as it doesn't seem like they will be adding one anytime soon. I currently have not tested the limitations of using these natively
exported methods, so if anyone would like to provide any feedback about bugs, etc., it would be greatly appreciated. I hope you enjoyed the article and learned
something new or useful.
History
- Sunday, August 12, 2012: Initial release.
Hey guys, my name is Roy. I was born March 15th 1994 and I am currently a student at the University of Waterloo. I have been programming for about 5 years now and working with C# for about 3 years. I am experienced in a variety of programming languages such as C/C++, Delphi, VB.NET, F#, and Python.