Giora Ron Genender
Giora Ron Genender

Reputation: 817

Calling c dll in C#

Introduction to the problem:

I've got to control a certain device through API provided with a DLL file, LIB file, and c header files whose functions are declared as dllimport.

When I use the API in a C++ project everything worked just fine - I included the headers, lib , dll, and called the functions as declared in the header files.

The problem begins when trying to call those functions from a C#.NET project, using [DllImport] attribute: The functions were declared with the exact name and parameters, and running the code did not throw any exception. And yet the device did not respond at all, like the functions had never been actually called.

How it is declared in the C header:

int __declspec(dllimport) Init_Card_RTx(unsigned short device_num, unsigned short enabled_channels, unsigned short xmt_channels);

How it is declared in C#:

[DllImport(Path, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Init_Card_RTx")]
public static extern int Init_Card_RTx(UInt16 device_num, UInt16 enabled_channels, UInt16 xmt_channels);

The questions:

Upvotes: 8

Views: 2659

Answers (4)

user180326
user180326

Reputation:

Try declacring it like this (you only do this once, in a CPP file, NOT a header)

 extern "C" __declspec(dllexport) int __stdcall Init_Card_RTx(unsigned short device_num, unsigned short enabled_channels, unsigned short xmt_channels)
 {
    //
 }

and in C#:

 [DllImport("MyDll.dll")]
 int Init_Card_RTx(ushort device_num, ushort enabled_channels, ushort xmt_channels);

you need to export it and declare with extern "C". __stdcall is convenient as it's the default on the .net side. I have a macro in my C++ project to make sure I type it the same everytime.

And yes, get dependencywalker....

Upvotes: 0

ichramm
ichramm

Reputation: 6642

The reason the functions are declared as dllimport is (correct me if I'm wrong) the the DLL is provided by your card vendor, that's ok because dllexport is only needed when building the DLL.

[DllImport(Path, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Init_Card_RTx")]

The first argument Path is there only for ilustration purposes, am I rigth?

Can you open the DLL with depends.exe or PE Explorer(http://www.heaventools.com/download-pe-explorer.htm) and see the DLL's export table?

I'm asking this because the function name maybe decorated, that could happen if the DLL was compiled as C++ instead of plain C.

Upvotes: 0

Orion Edwards
Orion Edwards

Reputation: 123662

Is that because the functions in the headers are declared dllimport?

Possibly. The functions need to be exported. Whether they are exported or not depends on how they DLL was compiled by whoever gave it to you. I'd guess that they should be though (or the C++ code would try import them and fail).
The first thing I do when troubleshooting this kind of thing is to load the DLL in Dependency Walker which will show you all the exported functions. If they're not showing up, they're not exported, and C# can't call them. If they are showing up, then you're ok and you don't need to change any of your C code or create any wrapper functions.

In that case, do I have to wrap the DLL with C++ functions declared as dllexport?

No. C# can only call C functions using DllImport. C++ does name mangling when exporting functions, which makes things a mess and generally not workable.

What you need to do is make your functions exported somehow. My guess is that they are already exported, but if not, you could make some wrapper functions like this.

int __declspec(dllexport) Init_Card_RTx(unsigned short device_num, unsigned short enabled_channels, unsigned short xmt_channels) {
    // just call the other function here
}

These should make the function exported and you should see it show up in dependency walker.

What are the steps required for a C DLL to be accessible from C#?

The CLR has to be able to find the dll (usually you just put it in the same directory as the C# exe), and the functions have to be exported as C functions. That's pretty much it.

In the C# project, do I have to include the LIB file as well? not just the DLL file itself?

You don't need to include anything in the C# project at all, just the public static extern wrapper functions with their DllImport attributes on them. So long as the CLR can find the dll at runtime, that's all you need. If it can't find it at runtime, you should get an exception when you call the first imported method.

PS: Get Dependency walker. I can't recommend it more highly for this kind of thing

Upvotes: 6

Xavier J
Xavier J

Reputation: 4728

This kind of thing is always a pain in the butt. You have to make sure that the byte structure of the data being passed matches what your DLL is expecting, and exactly. The native structures in C# don't know that they are being passed externally, so you have to structure the code more rigorously.

See here for more info:

http://msdn.microsoft.com/en-us/library/awbckfbz(v=vs.110).aspx

Upvotes: 0

Related Questions