There a few examples out there on how to call Win32 methods from user.dll in C# for example but I didn’t find any articles that dealt explicitly with creating a DLL in straight ‘C’ then importing the methods in the ‘C’ dll from C#.
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
This begs the obvious question: why would you want to write a Dll in ‘C’ in the first place?
Well, in my case (I’ll try to keep the story short) I have a fifteen year old ISA card that supports eight electronic relays. These relays can switch electronic appliances on or off from computer control through the IO bus. This is where the fun begins because you cannot simply use the ANSI ‘C’ method outportb() to send a value out directly to an IO port while running various flavors of XP like you could do in the past with old versions of Windows where all processes shared the same memory heap (thus causing all of those fun access violations).
I found a ready made device driver written in ‘C’ that allows a process access to IO ports in XP but in order to make a call outportb() from C#, still required that I write an ‘adaptor’ .Dll in ‘C’.
I knew how to use the [DllImport()] attribute when importing native Win32 methods from user.dll etc. but I found that that very technique did not work with the Dll that I had written myself in ‘C’.
The first problem was that the C# compiler kept throwing an error that said “Unable to find an entry point named OpenIO in DLL PortIO.dll. After looking carefully at the method signature in OpenIO – I could find nothing wrong but then I remembered that there was something you had to do with a .DEF file or something. I scoured with Google searches where I found bits and pieces but no articles that addressed my problem explicitly.
In summary here are the lessons learned (some re-learned):
- When using the .Net compiler (or the VC6 compiler as well) with a ‘C’ project where the source file extension is *.cpp – you must either turn name mangling off by enclosing your public (__declspec(dllexport) = public in ‘C’) methods in a block defined as ‘extern “C”’ or use a .DEF file. I chose to use ‘extern “C”’. Name mangling is how C++ compilers distinguish overloaded methods with the same name. Note that you cannot overload any methods enclosed in ‘extern “C”. I believe with VC6 you can turn off mangling by simply changing the source extension to *.c but I have not tried this with VS.Net.
- You can import mangled or any other public method by using the EntryPoint parameter for DllImport which will take either an ordinal index or the method name. I use both method name and ordinal position in the sample code to illustrate this point.
- Dumpbin.exe which comes with VS.Net is indispensable when troubleshooting Dlls written in ‘C’. An example of how to use this and sample output is in the comments of the source code.
- If you do not include the modifier __declspec(dllexport) in your ‘C’ Dll function definition (prototype in header [.h] file) and declaration (function body in *.c or *.cpp file) the method will not be exported as ‘public’ and the entry point will not show up in the binary dump of the Dll.
Here's an example of a properly defined native Dll method written in 'C':
//Note: must use __declspec(dllexport) to make (export) methods as 'public'
__declspec(dllexport) void DoSomethingInC(unsigned short int ExampleParam, unsigned char AnotherExampleParam)
printf("You called method DoSomethingInC(), You passed in %d and %c\n\r", ExampleParam, AnotherExampleParam);
}//End 'extern "C"' to prevent name mangling
Here's what the import declaration in C# looks like:
<FONT color=#0000ff size=2>public
<FONT color=#0000ff size=2>static
<FONT color=#0000ff size=2>extern
<FONT color=#0000ff size=2>void DoSomethingInC(
<FONT color=#0000ff size=2>ushort ExampleParam,
<FONT color=#0000ff size=2>char AnotherExampleParam);
Here's what the Dll dump looks like:
<FONT color=#008000 size=2>
File Type: DLL
Section contains the following exports for C_DLL_with_Csharp.dll
409557E6 time date stamp Sun May 02 13:19:50 2004
1 ordinal base
2 number of functions
2 number of names
ordinal hint RVA name
1 0 00011596 ?DoSomethingInMangledC@@YAXPAD@Z
2 1 0001110E DoSomethingInC