Click here to Skip to main content
Click here to Skip to main content

Exporting functions in C#/VB.NET to native code

By , 19 Nov 2012
 

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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

roylawliet
Student
Canada Canada
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.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionUsing C# classmemberDavide Zaccanti24-Aug-12 7:04 
With your suggestion (and tool) I've build a c# class that is called from a C++ client, and it work very fast passing also fixed lenght strings embedded into structure.
 
My problem is that as long as I try to use this dll with commercial assembly referenced (I've tested chilkat, aspose, ecc) to execute some jobs using managed code 3rd parties tools this error appear:
 
An unhandled exception of type 'System.IO.FileNotFoundException' occurred in Unknown Module.
 
Additional information: Could not load file or assembly 'xxxxxx, Version=x.y.z.0, Culture=neutral, PublicKeyToken=xxxxxxxxxxxxx' or one of its dependencies. Impossibile trovare il file specificato. (unable to fins specified file

 
On output window the is also:
A first chance exception of type 'System.IO.FileNotFoundException' occurred in Unknown Module.
An unhandled exception of type 'System.IO.FileNotFoundException' occurred in Unknown Module.
 
With all the logic, but without reference, using xxxx and calls to 3rd party assembly all works without problems, for example before calling aspose I made some tests (file exists, file dimension and so on).
 
My dll is compiled as AnyCPU .NET 4.0
 
Do you have any idea ?
 
Thank you and my 5 for easy to use tool.
AnswerRe: Using C# classmemberroylawliet24-Aug-12 7:57 
I'm not sure as to what the problem could be. I would suggest checking the disasm.il file produced and seeing if the IL is being inserted properly. A known problem with InlineILCompiler is that it cannot recompile Managed C++ assemblies because of embedded native code (it's lost in the disassembling -> reassembling process).
 
Another thing I would suggest is try making a simple wrapper assembly (*.dll) which utilizes the commercial assembly and have the export in the wrapper assembly.
 
I think I previously mentioned this in the article but the method must be static as well as public.
GeneralRe: Using C# classmemberDavide Zaccanti26-Aug-12 20:09 
Done, I've saved original IL (dll working and debuggable), add reference to an assembly, declared with using and just allocated class. I've rebuild all and compared the two disassembly output.
 
corflags 0x00000002 and .vtfixup [1] int32 fromunmanaged at VT0 are preserved
 
Differences:
 
1) added .assembly extern ChilkatDotNet4 after mscorlib and system
2) Image base and code size are different
3) In .locals init added class is in the mid, between passed params and declared object
4) Added code
 
I dont kwow where the problem can be, I'm starting C# after 25 years of C++ and I'm not so expert about IL..
 
Thank you for your kind support, if this is a limitation I think you'd better insert a warning in your article.
QuestionNicememberCIDev16-Aug-12 4:34 
An interesting little article.
Just because the code works, it doesn't mean that it is good code.

GeneralMy vote of 5memberbitterskittles15-Aug-12 6:56 
very cool.
I have a question tho. If you have unmanaged code that uses DllExport'ed functions from 2 different assemblies, and they target different .NET framework versions, ie 1 targets v4 and the other targets v2, what would happen then?
 
Another case could be where a .NET 4.5 assembly DllImport's DllExport'ed code from an assembly which targets a pre-4.0 .NET framework.
GeneralRe: My vote of 5memberroylawliet15-Aug-12 15:53 
This is actually a very interesting question, I'm not sure I would have to try it out. Perhaps you could and let me know the results?
GeneralCool Tool, but ...memberernie100013-Aug-12 23:56 
The opposite one of "DLLImport" was done a long time ago and it's called "COM/Interop". Most of former development tools like C/C++ and VB6 an previous can work with ActiveX and so there is no problem developing .NET DLL's and use them outside.
I had this problem during an enhancement of some legacy (COBOL) code. Fortunately the old COBOL-Compiler "understands" ActiveX and communication was no problem.
By the way: If you are going to pass a parameter by out or ref to .NET the marshalling will copy the parameter to managed code and after finishing the parameter is copied back. This leads to memory leaks because the GC in .NET cannot free the copied memory. If such a function is called repeatedly and make use of large strings (this was my case) my 4GB machine was killed Cry | :(( .
A disadvantage of using "COM/Interop" is that you have to register the DLL's via regasm.exe on every client with an administrator account.
GeneralRe: Cool Tool, but ...memberroylawliet14-Aug-12 2:13 
Passing the parameter as ref/out is no different than had I passed it as Foo* in C#. Take a look at the IL that gets produced. I'm aware of COM/Interop however, it's usually a hassle to use. Much more of a hassle than if there was simply an attribute called DllExport like there is DllImport.
GeneralRe: Cool Tool, but ...memberernie100014-Aug-12 3:01 
I agree, that COM/Interop isn't easy to use. But when .NET was designed, MS thought, they could leave the "old world" behind. Maybe I'm a little outdated, but i have to maintain about 1 million lines of Cobol and this was easiast way to enhance this code with GUI, Printing, Webservices and so on.
 
Cobol has a lot of restrictions to deal with memory and the lack of pointers is only one of them. So when I'm passing strings from Cobol to .NET I have to take care, what's passed by reference and what's passed by value. The GC isn't collecting the copied memory that's created when a string is passed by reference. When it's passed by value the (copied) memory is released when the function exits. The only way I've found to release referenced strings inside .NET is to call the kernel with "SetProcessWorkingSetSize".
GeneralRe: Cool Tool, but ...memberroylawliet14-Aug-12 8:46 
A string is a special case. The .NET marshal has to temporarily move it to memory if it's being marshaled as an ANSI string, otherwise if it's being marshaled as a Unicode string it's no different than fixing it down with a pointer. StringBuilder is another example of a special case where the Marshal just works it's magic.
 
Structures containing primitive-only types are different. Like fixing down a unicode-string, when you pass by ref/out a structure is simply pinned down (if on the heap, otherwise if it is a local -- on the stack you can obtain the address of it directly without needing to pin it down).
 
No real "magic" happens when passing primitive based structures via ref/out like it does with strings.
GeneralRe: Cool Tool, but ...memberernie100014-Aug-12 20:40 
My special case is named "Cobol ANSI '85". Inside Cobol everything you are defining is string (on OS, that are popular now Smile | :) ). Thank you very much for the detailed info.
GeneralMy vote of 5memberbartolo13-Aug-12 0:46 
Cool hack, very useful when connecting with legacy systems. Cheers.
QuestionA notice for viewers on August 12 2012memberroylawliet12-Aug-12 13:51 
There was a small bug with the initial release of InlineILCompiler.exe today. Codeproject is yet to publish the updated version and I'm currently waiting on them to. At the moment, there's a restriction of only exporting one function. This was fixed about an hour ago, but I'm still waiting.
 
So, if you happen to download this early, please re-download it tomorrow. You'll know when the new version is posted because at the moment the download link for InlineILCompiler is at the bottom of the article, once it has been updated it will be at the top.
 
Thanks,
Roy.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130617.1 | Last Updated 19 Nov 2012
Article Copyright 2012 by roylawliet
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid