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);
}
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);
}
}
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.
Create a new C# Library project (I'll call it "Wrapper")
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
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:
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:
How to create a DLL library in C and then use it with C#
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);
}
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.
Related
Over the past few days I have been trying to interface with a C library (built for an ARM platform) on linux, in dotnet core. All I am trying to do is call a simple function which (essentially) returns a string.
However, I have no experience of using DLLImport or interop on the whole in C# and I am struggling.
The C code looks like (with substitute names as I am using a work platform):
int version(int argc, char *argv[])
{
return READ_DATA(0,
version, //callbackfunction
"version: 0x%04x\n"); //formatting string
}
public class Board
{
private Interop_Commands _commands = new Interop_Commands();
public string GetVersion()
{
return _commands.GetVersion();
}
}
internal class Interop_Commands
{
public const string LIBRARYPATH = "libname";
[DllImport(LIBRARYPATH,CharSet=CharSet.Unicode, CallingConvention =CallingConvention.Cdecl)]
public static extern int version(int argc, StringBuilder argv);
public string GetVersion()
{
var sb = new StringBuilder();
Console.WriteLine($"Calling {nameof(version)}");
version(0, sb);
Console.WriteLine($"Called {nameof(version)}, got: {sb.ToString()}");
return sb.ToString();
}
}
with the calling class (main for this very simple proof of concept/trial code):
static void Main(string[] args)
{
Console.WriteLine("Getting Version from board..");
var board = new Board();
Console.WriteLine(board.GetVersion());
Console.WriteLine("done");
Console.ReadLine();
}
The folder structure is (simplified):
folder
|--> Dll/runtime
|--> libname (note no .so here, just libname)
Any help would be appreciated, I am finding examples of C imports/usages limited, and also finding examples limited for how to use custom libraries in dotnet core.
EDIT 1:
Following help from #Sohaib Jundi, I have added the extern so the signature is now: (it wouldnt compile with extern "C")
extern int version(int argc, char *argv[])
I am unsure what to try next.
but dotnet core wont publish with x86 and target runtime set to linux-arm, just throws an unknown exception, with the log file not being very helpful..
If i use the compiled library with the previous code (AnyCPU + linux-arm), then the DllNotFoundException is still thrown
* EDIT 2: *
As it turns out, the original no extension file i was using appears to be an executable referencing a static library (which ends up compiled into the executable). rebuilding I have managed to get the static library separate, but still get the same DllNotFoundException. Does anyone know what the search procedure is for the DllImport on dotnet core?
The interop/import code now looks like:
[DllImport("libname",
CallingConvention =CallingConvention.Cdecl,
EntryPoint= "version")]
public static extern int version(ref uint val);
where the static lib code looks like:
extern int version(uint32_t *);
After some playing around, I managed to get an example to work.
Follow these steps:
1. export your function from the dll, i.e: add extern "C" __declspec(dllexport) to the function signature
2. Make sure that both the dll and your dotnet core application have the same architecture. don't keep the dotnet core as "Any CPU", force it to the same architecture as the dll. (project properties -> build -> platform target = x86 or x64)
I have found the solution.. the library was being compiled as a .la (statically linked library) rather than a .so (shared object) library. The DllImport doesnt work with statically linked libraries so.. a recompilation of the library into a shared object library has meant it will now find the dll (I also exported LD_LIBRARY_PATH as pwd to make sure it was in the search path..).
Once this was in, the rest of the code fell into place. The matching dll import declaration for the version above was correct (from *EDIT 2 *) - using ref uint. So now I have to extend the methods supported in my interop class to fully support the library.
Thanks for your help #Sohaib Jundi
I am trying to follow tutorial calling code from C++ into C#.
I followed the coding part correctly.
But when I run the code I get exception:
System.DllNotFoundException: 'Unable to load DLL 'SampleNativeLib': The specified module could not be found. (Exception from HRESULT: 0x8007007E)'
I create C# library as simple application file.
And C++ as dynamic .dll.
In the original tutorial (I also have full project file of it) in the C# project in the references there are no references to C++ .dll.
I would like to ask how C++ is referenced to C#?
The code of C#
using System;
using System.Linq;
using System.Runtime.InteropServices;
namespace ObjectPinning {
class Program {
[DllImport("SampleNativeLib")]
static extern int SetData([MarshalAs(UnmanagedType.LPArray)] int[] darray);
[DllImport("SampleNativeLib")]
static extern int DoCalc();
static void Main(string[] args) {
var data = Enumerable.Range(0, 10).Select(i => i + 1).ToArray();
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
Console.WriteLine(SetData(data));
Console.WriteLine(DoCalc());
Console.ReadLine();
GC.Collect();//Clean up any garbage object
Console.WriteLine(DoCalc());
handle.Free();
}
}
}
The code of C++:
// SampleNativeLib.cpp : Defines the exported functions for the DLL application.
//
#include "stdafx.h"
int* g_pData;
extern "C" __declspec(dllexport) int WINAPI DoCalc() {
int sum = 0;
for(int i = 0; i < 10; i++)
sum += g_pData[i];
return sum;
}
extern "C" __declspec(dllexport) int WINAPI SetData(int* data) {
g_pData = data;
return DoCalc();
}
My project file:
Download
Teachers File:
Download
My file
Teacher File
The exception you get is most likely because you didn't add the dll to the C# executable path. In the first step the application searching the dependencies in the exe directory, if not found, then it goes through the Window environment variables. That is why we don't need to copy the dlls when we use some of the Windows API function, because if you look at at your PATH variable, you'll have %SystemRoot% variable which links to your windows folder.
In C# you can't really reference C++ projects as you already know when adding another C# project, since you calling from managed code to unmanaged/native code.
Important note when you building your own C++ dll, make sure the compiler environment set to x32 bit, otherwise you'll get some other exceptions when calling C++ code from C#.
What actually expose the C++ function to allow for it consumed by other languages, is the:
extern "C" __declspec(dllexport)
On the signature of the function. So that gives you the power to do almost everything you can do with C++ also in C#/Java.
I'm trying to create a DLL exposing some static functions to use then in C.
Recently I read an article of Microsoft named "An Overview of Managed/Unmanaged Code Interoperability" and in this there is no a clear explanation on how to "Exposing a Managed API as a Flat API".
I installed this plugin to Visual Studio (https://www.nuget.org/packages/UnmanagedExports) but I still can't compile a project in C.
My C# project exposes a function like this:
using RGiesecke.DllExport;
using System.Runtime.InteropServices;
namespace libcallcstest
{
public class Class1
{
[DllExport("add", CallingConvention = CallingConvention.Cdecl)]
public static int add(int a, int b)
{
return a + b;
}
}
}
After building project, result these three files:
libcallcstest.dll
libcallcstest.pdb
libcallcstest.tlb
My C code is:
#include <stdio.h>
#include <stdlib.h>
int add(int, int);
int main(int argc, char** argv) {
int z = add(2,5);
printf("%d\n", z);
return (EXIT_SUCCESS);
}
And finally when I try to compile this file with:
gcc -o main.exe main.c -lcallcstest
Not work properly, files created by building the C# project are in the same folder as the main.c file.
Pleas any help!!!
One way to go: you may want to host CLR in your process. I would recommend against it though, because hosting is not the easiest procedure out there.
Also it's often not really needed or you can use some slower methods to communicate with .Net code from unmanaged environment (for example, present your library as a local server and access it through network interfaces. As I see it that way you'll have ten times less work to do).
Or you could go with your original variant using utilities to help you like mentioned here.
Is it possible to invoke a g++ executable file's function from mono in Ubuntu? Note that both C++ and C# code compiled in Ubuntu Operation System.
C++ application source:
#include <stdlib.h>
static int32_t Sum(int32_t a, int32_t b){
return a + b;
}
/*
*
*/
int main(int argc, char** argv) {
return (EXIT_SUCCESS);
}
C# mono application source:
using System;
using System.Runtime.InteropServices;
namespace MonoCsTest
{
class MainClass
{
[DllImport("/home/.../MonoCsTest/bin/Debug/testcpp")]
public static extern Int32 Sum(Int32 a, Int32 b);
public static void Main (string[] args)
{
Console.WriteLine (" 5 + 6 = " + Sum(5,6));
}
}
}
This throws DllNotFoundException
You need to compile the library as a shared library: a static library can't be loaded at runtime with P/Invoke.
The fact that you added a main() function suggests that you're compiling the code into an executable instead.
So the first thing for you is to learn how to compile a shared library, you can try something like:
gcc -shared -o libtestcpp.so testcpp.cpp
Then change the DllImport name to the path to the complete library name:
DllImport("/home/yourlogin/MonoCsTest/bin/Debug/libtestcpp.so")
The other mistake you made is not considering the C++ manadated name mangling: the simpler solution here is to export Sum() as a C function surrounding it with extern "C" {}.
To diagnose such mistakes it is often useful to enable the debug logging from mono using:
MONO_LOG_LEVEL="debug" MONO_LOG_MASK="dll" mono yourprog.exe
As long as a symbol is exported and Mono can understand the parameters (enough to marshal data to/from them), then yes, you can do this. I think the Mono pages on PInvoke actually mention that you can invoke functions exported by both libraries and executables, including an executable that embeds Mono.
You need to verify that the symbol is indeed being exported, and more importantly, that it is not mangled or you match that in the C# side. That's the spot I've had the most stupid problems with.
I have a dll which comes from a third party, which was written in C++.
Here is some information that comes from the dll documentation:
//start documentation
RECO_DATA{
wchar_t Surname[200];
wchar_t Firstname[200];
}
Description:
Data structure for receiving the function result. All function result will be
stored as Unicode (UTF-8).
Method:
bool recoCHN_P_Name(char *imgPath,RECO_DATA *o_data);
Input:
char * imgPath
the full path of the image location for this
function to recognize
RECO_DATA * o_data
data object for receiving the function
result.
Function return:
True if Success, otherwise false will return.
//end documentation
I am trying to call the recoCHN_P_Name from my C# application. To this end, I came up with this code:
The code to import the dll:
public class cnOCRsdk
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct RECO_DATA{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=200)]
public string FirstName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 200)]
public string Surname;
}
[DllImport(#"cnOCRsdk.dll", EntryPoint="recoCHN_P_Name")]
public static extern bool recoCHN_P_Name(byte[] imgPath, RECO_DATA o_data);
}
The code to call the function:
cnOCRsdk.RECO_DATA recoData = new cnOCRsdk.RECO_DATA();
string path = #"C:\WINDOWS\twain_32\twainrgb.bmp";
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
byte[] bytes = encoding.GetBytes(path);
bool res = cnOCRsdk.recoCHN_P_Name(bytes, recoData);
And the error I'm getting is
""Unable to find an entry point named 'recoCHN_P_Name' in DLL 'cnOCRsdk.dll'."
I'm suspecting that I'm having an error in converting a type from C++ to C#. But where exactly ... ?
First make sure the function is actually exported:
In the Visual Studio Command Prompt, use dumpbin /exports whatever.dll
C# doesn't support C++ name mangling and you either need to declare the C++ functions with
extern "C" {...}
(may not an option if they're from a third party), or call the mangled name directly if you can get it to work. It may be easier to get the third party to provide a non-mangled interface to the functionality.
Solved - at least to the point where the program does not break and actually returns me a bool value.
The key, I guess, was to specify the entry point as the 'mangled' name
[DllImport(#"cnOCRsdk.dll", EntryPoint="?recoCHN_P_Name#CcnOCRsdk##QAE_NPADPAURECO_DATA###Z")]
public static extern bool recoCHN_P_Name(ref string imgPath, ref RECO_DATA o_data);
After that I got some other errors but the 'unable to find entry point' went away.
I solved the same problem in these steps:
step 1) If you program your custom DLL in C++ using Visual studio,then at the property page of your project set the Common Language Runtime Support (/clr)parameter to Common Language Runtime Support (/clr).
step 2) To function deceleration in .h file use __declspec(dllexport) keyword like below:
__declspec(dllexport) double Sum(int a,int b);
step 3) Build and export DLL file, then use the Dependency Walker software to get your function EntryPoint.
step4) Import DLL file In the C# project and set EntryPoint and CallingConvention variable like below:
[DllImport("custom.dll", EntryPoint = "?Sum##YAXHHHHHHNNN#Z", CallingConvention = CallingConvention.Cdecl)]
public static extern double Sum(int a,int b);
I'd write a wrapper using C++/CLI. This wrapper will be able to include the .h files and link to the .lib files you got from the third party vendor. Then it is both easy and safe to write a managed interface for your C# program.
Correct EntryPoint string could be found in ".lib" file that comes along with main unmanaged dll.
We had this problem when we want to access to DB and solved it by changing EF core to EF 6.4.4
It may be you have a problem like this and need to change or downgrade your version of EF (If you used EF)
We had this problem .we change EntityFramework.core to EntityFrameWork 6.4.4 and after that the program worked fine. you most change you're Framework Version.
you may get this error due to string marshalling mismatch between DLL and your application . for example, one is using ANSI and the other is unicode.
you can try something like this:
[DllImport("yourDLL.dll", CharSet = CharSet.Unicode )]
public static extern String YourFunction(String name);
checkout HERE for a list of other possible reasons.
You could try using the unmangled name while specifying a CallingConvention in the DllImport