78

I have a C lib and want to call function in this library from C# application. I tried creating a C++/CLI wrapper on the C lib by adding the C lib file as linker input and adding the source files as additional dependencies.

Is there any better way to achieve this as am not sure how to add C output to c# application.

My C Code -

__declspec(dllexport) unsigned long ConnectSession(unsigned long handle,
                            unsigned char * publicKey,
                            unsigned char   publicKeyLen);

My CPP Wrapper -

long MyClass::ConnectSessionWrapper(unsigned long handle,
                                unsigned char * publicKey,
                                unsigned char   publicKeyLen)
    {
        return ConnectSession(handle, publicKey, publicKeyLen);
    }
Chinjoo
  • 2,595
  • 6
  • 24
  • 44
  • 2
    yeah its possible, and there are many examples if you google it, but no idea what your problem is from the limited information – Keith Nicholas Jul 11 '12 at 03:44
  • 1
    If it hurts when you do X, stop doing X. Rename your C++ wrapper method to rule that out as a source of the problem. And post your code :-) – Eric J. Jul 11 '12 at 03:48
  • I have a c library provided by a vendor which has functions like ConnectToMachine(unsigned char * psig, char apt). I want to cal this function from my c# class. – Chinjoo Jul 11 '12 at 03:48
  • 2
    Look into [PInvoke](http://msdn.microsoft.com/en-us/library/aa288468.aspx) – Mark Hall Jul 11 '12 at 03:53
  • 3
    Q: Is it possible to call a C function from C#.Net? A: Yes, obviously! Q: Is a C++/CLI wrapper the way to go? Personally, I consider C++/CLI an abomination. But people use it in scenarios just like this. I would prefer to use Interop directly (i.e. PInvoke) - all C#, right down to the native, unmanaged C/C++. There's LOTS of tutorials, and it's *not* difficult. Certainly no more difficult than calling C/C++ from VB6 ;) IMHO.. – paulsm4 Jul 11 '12 at 03:55

5 Answers5

106

The example will be, for Linux:

1) Create a C file, libtest.c with this content:

#include <stdio.h>

void print(const char *message)
{
  printf("%s\\n", message);
}

That’s a simple pseudo-wrapper for printf. But represents any C function in the library you want to call. If you have a C++ function don’t forget to put extern C to avoid mangling the name.

2) create the C# file

using System;

using System.Runtime.InteropServices;

public class Tester
{
        [DllImport("libtest.so", EntryPoint="print")]

        static extern void print(string message);

        public static void Main(string[] args)
        {

                print("Hello World C# => C++");
        }
}

3) Unless you have the library libtest.so in a standard library path like “/usr/lib”, you are likely to see a System.DllNotFoundException, to fix this you can move your libtest.so to /usr/lib, or better yet, just add your CWD to the library path: export LD_LIBRARY_PATH=pwd

credits from here

EDIT

For Windows, it's not much different. Taking an example from here, you only have yo enclose in your *.cpp file your method with extern "C" Something like

extern "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

then, compile, and in your C# file do

[DllImport("C_DLL_with_Csharp.dll", EntryPoint="DoSomethingInC")]

public static extern void DoSomethingInC(ushort ExampleParam, char AnotherExampleParam);

and then just use it:

using System;

    using System.Runtime.InteropServices;

    public class Tester
    {
            [DllImport("C_DLL_with_Csharp.dll", EntryPoint="DoSomethingInC")]

    public static extern void DoSomethingInC(ushort ExampleParam, char AnotherExampleParam);

            public static void Main(string[] args)
            {
                    ushort var1 = 2;
                    char var2 = '';  
                    DoSomethingInC(var1, var2);
            }
    }
Gonzalo.-
  • 11,500
  • 5
  • 44
  • 74
  • 7
    Good example! This is a very good example of using Interop. It's obviously Linux (not Windows), but the principle is the same. This is *EXACTLY* the approach I'd recommend to the OP. – paulsm4 Jul 11 '12 at 03:59
  • Could some one edit the answer? I got problems with the include, doesn't show stdio.h – Gonzalo.- Jul 11 '12 at 03:59
  • guess that should work.. but notsure how to get the output as .so from vs2010.. it only gives .lib and .dll, again .dll when set as dllimport given bad format excpetion – Chinjoo Jul 11 '12 at 07:22
  • as you can se here http://en.csharp-online.net/CSharp_FAQ%3A_How_do_I_get_DllImport_to_work%3F you could do the same using a .dll – Gonzalo.- Jul 11 '12 at 12:26
  • did compile to .so file and did same as mentioned in the reply, but still getting bad format exception. – Chinjoo Jul 12 '12 at 07:51
  • 1
    @Chinjoo consider this http://www.codeproject.com/Articles/6912/Calling-methods-from-Dll-compiled-in-C-from-C. Have you modify your C code, adding _declspec(dllexport) ? If this example works for you, I will add it to the answer – Gonzalo.- Jul 16 '12 at 17:32
  • @Chinjoo - Did you compile to a .so on Windows by renaming the file, or did you actually generate a dynamic library and you're using '.so' to mean that? If you're on Windows, you need to generate a DLL for this to work. – Kevin Vermeer Jul 18 '12 at 19:29
  • I had done both ways.. ie, compiled to a dll as well as used gnu compiler to generate an .SO file, in both cases, the runtime is giving bad format exception. – Chinjoo Jul 19 '12 at 12:04
  • @Chinjoo, as you can see in your code, and as I can see in this link http://www.codeproject.com/Articles/6912/Calling-methods-from-Dll-compiled-in-C-from-C that I've posted above, you have to enclose your method with `extern "C" {/*your code goes here*/ }`. Have you tried it ? If it works, let me edit my answer, and you can tick it as correct – Gonzalo.- Jul 19 '12 at 21:37
  • @ElVieejo, I have the method enclosed in extern "C", but not working. – Chinjoo Jul 21 '12 at 06:06
  • as I said above, can you show us your *completelly* code ? including C and C# code – Gonzalo.- Jul 21 '12 at 06:07
  • I added the code for windows in the answer. You can assign me the bounty if this works for you ! – Gonzalo.- Jul 21 '12 at 06:14
  • @Chinjoo can you please assign the bounty? It's ending! – Gonzalo.- Jul 21 '12 at 16:45
  • *the sound of furious upvoting* – Andreas Grapentin May 19 '15 at 05:04
  • i get: This is not a valid assembly or com-component if i try to import the c++ .dll :( – binaryBigInt Jan 27 '16 at 19:02
  • I have a .c file - and it don't recognize *extern "c"* what do I need to do? – STF Jul 26 '17 at 10:50
  • See https://stackoverflow.com/questions/36785157/how-does-extern-c-allow-c-code-in-a-c-file/36785171 or https://stackoverflow.com/questions/9499078/could-we-use-extern-c-in-c-file-without-ifdef-cplusplus – Gonzalo.- Jul 26 '17 at 12:43
  • I did what you said in windows but I got an exception: `System.EntryPointNotFoundException` – STF Jul 27 '17 at 05:19
  • you should ask a new question with your exact code and provide the compiler you're using – Gonzalo.- Jul 27 '17 at 12:39
  • This code works when I do a dotnet run but fails with sudo dotnet run. Any idea? – divyanshm Dec 18 '18 at 04:38
  • what if there are multiple methods in DLL now how to call them witll DLL entry ? – HaseeB Mir Oct 02 '19 at 21:09
38

UPDATE - Feb 22 2019: Since this answer has been getting quite a few upvotes, I decided to update it with a better way of calling the C method. Previously I had suggested using unsafe code, but the safe and correct way is to use MarshalAs attribute for converting a .NET string to a char*. Also, in VS2017 there is no Win32 project anymore, you'll probably have to create a Visual C++ dll or empty project and modify that. Thank you!

You can directly call C functions from C# by using P/Invoke.
Here's a short how-to on creating a C# lbrary that wraps around a C dll.

  1. Create a new C# Library project (I'll call it "Wrapper")
  2. Add a Win32 project to the solution, set application type to: DLL (I'll call it "CLibrary")

    • You can remove all the other cpp/h files since we won't need them
    • Rename the CLibrary.cpp file to CLibrary.c
    • Add a CLibrary.h header file
  3. Now we need to configure the CLibrary project, right-click it and go to properties, and select Configuration: "All Configurations"

    • In Configuration Properties > C/C++ > Precompiled headers, set Precompiled Headers to: "Not using Precompiled Headers"
    • In the same C/C++ branch, go to Advanced, change Compile As to: "Compile as C code (/TC)"
    • Now in the Linker branch, go to General, and change Output File to: "$(SolutionDir)Wrapper\$(ProjectName).dll", this will copy the built C DLL to the C# project root.

CLibrary.h

__declspec(dllexport) unsigned long ConnectSession(unsigned long   handle,
                                                   unsigned char * publicKey,
                                                   unsigned char   publicKeyLen);

CLibrary.c

#include "CLibrary.h"

unsigned long ConnectSession(unsigned long   handle,
                             unsigned char * publicKey,
                             unsigned char   publicKeyLen)
{
    return 42;
}
  • Right-click CLibrary project, build it, so we get the DLL in the C# project directory
  • Right-click C# Wrapper project, add existing item, add CLibrary.dll
  • Click on CLibrary.dll, go to the properties pane, set "Copy to output Directory" to "Copy Always"

It's a good idea to make the Wrapper project dependent on CLibrary so CLibrary gets built first, you can do that by right-clicking the Wrapper project, going to "Project Dependencies" and checking "CLibrary". Now for the actual wrapper code:

ConnectSessionWrapper.cs

using System.Runtime.InteropServices;

namespace Wrapper
{
    public class ConnectSessionWrapper
    {
        [DllImport("CLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
        static extern uint ConnectSession(uint handle,
            [MarshalAs(UnmanagedType.LPStr)] string publicKey,
            char publicKeyLen);

        public uint GetConnectSession(uint handle, 
                                      string publicKey,
                                      char publicKeyLen)
        {
            return ConnectSession(handle, publicKey, publicKeyLen);
        }
    }
}

Now just call GetConnectSession, and it should return 42.

Result:
Testing wrapper library in a console application

Shahin Dohan
  • 4,247
  • 1
  • 29
  • 46
  • This worked for me, but I also had to change my .NET project's `Properties->Build->Platform Target` to `x64`. – Joseph Feb 21 '18 at 21:47
4

Okay well, Open VS 2010, Goto File -> New -> Project -> Visual C++ -> Win32 -> Win32 Project and give it a name (HelloWorldDll in my case), Then in the window that follows under Application Type choose 'DLL' and under Additonal Options choose 'Empty Project'.

Now goto your Solution Explorer tab usually right hand side of VS window, right click Source Files -> Add Item -> C++ file (.cpp) and give it a name (HelloWorld in my case)

Then in the new class paste this code:

#include <stdio.h>

extern "C"
{
  __declspec(dllexport) void DisplayHelloFromDLL()
  {
    printf ("Hello from DLL !\n");
  }
}

Now Build the project, after navigate to your projects DEBUG folder and there you should find: HelloWorldDll.dll.

Now, lets create our C# app which will access the dll, Goto File -> New -> Project -> Visual C# -> Console Application and give it a name (CallDllCSharp), now copy and paste this code to your main:

using System;
using System.Runtime.InteropServices;
...
        static void Main(string[] args)
        {
            Console.WriteLine("This is C# program");
            DisplayHelloFromDLL();
            Console.ReadKey();
        }

and build the program, now that we have both our apps built lets use them, get your *.dll and your .exe (bin/debug/.exe) in the same directory, and execute the application output should be

This is C# program

Hello from DLL !

Hope that clears some of your issues up.

References:

Community
  • 1
  • 1
David Kroukamp
  • 34,930
  • 13
  • 72
  • 130
  • I could not compile the C# program because my project can not find the DLL file. So I used the full path in the DllImport statement : `[DllImport("C:\\Users\\...full path...\\HelloWorldDll\\Debug\\HelloWorldDll.dll")]` – Quazi Irfan Apr 23 '17 at 08:16
  • Keep getting 'An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B)'. Anyone else got this issue? – Max Aug 19 '19 at 12:51
1

NOTE : BELOW CODE IS FOR MULTIPLE METHODS FROM DLL.

[DllImport("MyLibc.so")] public static extern bool MyLib_GetName();
[DllImport("MyLibc.so")] public static extern bool MyLib_SetName(string name);
[DllImport("MyLibc.so")] public static extern bool MyLib_DisplayName(string name);

public static void Main(string[] args)
{
string name = MyLib_GetName();
MyLib_SetName(name);
MyLib_DisplayName(name);
}
HaseeB Mir
  • 675
  • 12
  • 17
0

The P/Invoke method has been described extensively and repeatedly, ok so far. What I'm missing here is, that the C++/CLI method has a big advantage: Calling safety. In contrast to P/Invoke, where the call of the C funtion is like shooting blind into the sky (if this comparison is allowed), nobody will check the function arguments when calling the C function. Using C++/CLI in this case means normally, you include a headerfile with the functions prototypes you want to use. If you are calling the C function with wrong/to much /to few arguments, the compiler will tell you.

I don't think you can say in general which is the better method, honestly I don't like either of them.

Thomas Schmid
  • 131
  • 10