How do I load a C dll into a C# code? - c#

I am in the need of using some functions in a C made program. To test I defined the following :
This is my .h file :
namespace amt
{
class AMT_EXPORT FaceRecognition
{
public:
std::string amt_test_string(std::string in);
};
};
This is my .cpp file :
#include <memory.h>
#include <string>
#include <iostream>
#include <fstream>
#include "api_shared.h"
#include <sys/stat.h>
using namespace std;
std::string amt::FaceRecognition::amt_test_string (std::string in)
{
std::string s="in: "+in;
std::cout<<s<<std::endl;
return s;
}
I am trying to invoke the method like this :
const string str = "C:\\minimal.dll";
[DllImport(str)]
public static extern string amt_test_string(string input);
static void Main(string[] args)
{
string myinput = "12";
string myoutput = "";
myoutput = amt_test_string(myinput);
Console.WriteLine(myoutput);
Console.Read();
}
But im getting an error saying that it cannot find the entry point named amt_test_string..why so? I am a newbie in C btw

That's not a C DLL, that's a C++ DLL. C and C++ are not the same language. In particular, C++ has name mangling, so the function name which gets exported to the DLL is decorated.
I'd strongly recommend that you avoid having C++ exports in your DLL for that reason. If you use only C exports, the symbol name will be predictable (i.e. will not depend on the specific details of how your C++ compiler decorates names), and you won't have to worry about runtime differences, like how your C++ standard library implements std::string.
I'd recommend your DLL export look like this:
extern "C" // This says that any functions within the block have C linkage
{
// Input is 'in', output gets stored in the 'out' buffer, which must be 'outSize'
// bytes long
void DLLEXPORT amt_FaceRecogniztion_amt_test_string(const char *in, char *out, size_t outSize)
{
...
}
}
This interface does not rely on any particular library's std::string implementation, and C# knows how to martial char* parameters as C strings. However, memory management is more complicated, as you need to figure out an upper bound on how big the output is going to be and pass in an appropriately sized bufer.

Related

How to get content of array from C++ dll in C#

I want to use functions from DLL in C++ with C#.
I store string data in a vector.
My C++ file contains:
#include "stdafx.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
extern "C" __declspec(dllexport) std::vector<std::string> GetProduct();
std::vector<std::string> GetProduct()
{
std::vector<std::string> vectProduct;
vectProduct.push_back("Citroen");
vectProduct.push_back("C5");
vectProduct.push_back("MOP-C5");
return vectProduct;
}
In C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Runtime.InteropServices;
namespace ConsoleApplication
{
class Program
{
[DllImport("ProductLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern StringBuilder GetProduct();
static void Main(string[] args)
{
StringBuilder vectProduct_impl = GetProduct();
}
}
}
I don't know how to continue to browse the array in c#.
I don't know if the use of vector is optimal. if you have other solution I'm ready.
Please help.
My favourite way for passing an array of strings C++-->C# is by using a delegate.
C#:
// If possible use UnmanagedType.LPUTF8Str
// or under Windows rewrite everything to use
// wchar_t, std::wstring and UnmanagedType.LPWStr
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void AddAnsi([MarshalAs(UnmanagedType.LPStr)] string str);
[DllImport("CPlusPlusSide.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void TestReturnArrayStrings(AddAnsi add);
and then
var lst = new List<string>();
TestReturnArrayStrings(lst.Add);
foreach (string str in lst)
{
Console.WriteLine(str);
}
And C++:
#include <string>
#include <vector>
extern "C"
{
__declspec(dllexport) void TestReturnArrayStrings(void (add)(const char* pstr))
{
std::string str1 = "Hello";
std::string str2 = "World";
add(str1.data());
add(str2.data());
// Example with std::vector
add("--separator--"); // You can even use C strings
std::vector<std::string> v = { "Foo", "Bar" };
// for (std::vector<std::string>::iterator it = v.begin(); it != v.end(); ++it)
for (std::vector<std::string>::const_iterator it = v.begin(); it != v.end(); ++it)
{
add(it->data());
}
add("--separator--"); // You can even use C strings
// With C++ 11
// for (auto& it: v)
for (const auto& it: v)
{
add(it.data());
}
}
}
Here the "trick" is that C# passes to C++ a delegate to the List<string>.Add() method, and C++ "fills" directly the C# List<>. The memory managed by C++ remains in the C++ side, the memory managed by the C# remains in the C# side. No problems of cross-memory ownership. As you can imagine, it is quite easy to expand the "trick" to any other .Add() method, like HashSet<string>, or Dictionary<string, string>.
As a sidenote, I've created a github with many examples about marshaling between C/C++ and C# (both .NET Framework and .NET Core/5.0).
One way to do it is to use COM's SAFEARRAY structure as it's supported by .NET (the .NET Allocator used by P/Invoke is the COM allocator), including most of associated sub types, like BSTR.
So, in C/C++, you can define this:
extern "C" __declspec(dllexport) LPSAFEARRAY GetProduct();
LPSAFEARRAY GetProduct()
{
LPSAFEARRAY psa = SafeArrayCreateVector(VT_BSTR, 0, 3);
LONG index = 0;
// _bstr_t is a smart class that frees allocated memory automatically
// it needs #include <comdef.h>
// but you can also use raw methods like SysAllocString / SysFreeString
_bstr_t s0(L"Citroen"); // could use "Citroen" if you really want ANSI strings
// note SafeArrayPutElement does a copy internally
SafeArrayPutElement(psa, &index, s0.GetBSTR());
index++;
_bstr_t s1(L"C5");
SafeArrayPutElement(psa, &index, s1.GetBSTR());
index++;
_bstr_t s2(L"MOP - C5");
SafeArrayPutElement(psa, &index, s2.GetBSTR());
index++;
return psa;
}
And in C#, you can define this:
[DllImport("ProductLibrary.dll", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.SafeArray)]
public static extern string[] GetProduct();

How to pass List<int> from C# to C++ CLi?

I am beginner in C language family...
So, problem is - in my solution I have a repo (wrote in C#) that store List<int>, also I have an engine that (wrote in C++). So, I need pass List from C# implementation to C++ CLI for executing...
As far as I understood problem is C++ know how to work with std::vector and C# know how to work with List and I need somehow convert List to vector...
How to do it?
Any assumption appropriate.
EDIT
Sorry for misunderstanding, but my CLI works as a mapper for pure C++ implementation. So, as far as I understood from C# I need to pass my List to C++ CLI , C++ CLI will convert a List to vector and invoke another C++ file with pure C++ implementation.
This is my solution
h file
using namespace System;
using namespace System::Collections::Generic;
//forward declaration
class MathCore;
namespace MathCore_CLI_namespace
{
public ref class MathCore_CLI
{
public:
MathCore_CLI();
~MathCore_CLI();
int computeMulPlusVals(List<int>^ list_first, List<int>^ list_second);
//int computeMulPlusVals(std::vector<int> vect_first, std::vector<int> vect_second);
private:
MathCore * m_pMathCore;
};
}
cpp file
#include "stdafx.h"
#include "MathCore_CLI.h"
#include "..\Engine\MathCore.h"
#include <iostream>
#include <array>
using namespace System;
using namespace System::Collections::Generic;
namespace MathCore_CLI_namespace
{
const int size = 5;
int count = 0;
int arrayVal[size];
MathCore_CLI::MathCore_CLI()
{
m_pMathCore = new MathCore();
}
MathCore_CLI::~MathCore_CLI()
{
delete m_pMathCore;
}
int computeMulPlusVals(List<int>^ list_first, List<int>^ list_second)
{
return 0;
}
}
Error
What am I doind wrong?
C++ CLI directly supports List and you don't need a conversion. Here is a typical signature.
private: System::Void FooMethod( System::Collections::Generic::List<Int32>^ list )
Besides, In your updated question, you have a linker error.
Open C++ project properties, Find Linker and then Input. Add the location to your (EngineLib_Cli.dll) Library there
Also I found more optimized solution
int MathCore_CLI::computeMulPlusVals(array<int>^ arr_first, array<int>^ arr_second)
{
auto vec_first = std::vector<int>(arr_first->Length);
cli::pin_ptr<int> pPinnedFirst = &arr_first[0];
memcpy(vec_first.data(), pPinnedFirst, arr_first->Length * sizeof(int));
auto vec_second = std::vector<int>(arr_second->Length);
cli::pin_ptr<int> pPinnedSecond = &arr_second[0];
memcpy(vec_second.data(), pPinnedSecond, arr_second->Length * sizeof(int));
return m_pMathCore->computeMulPlusVals(vec_first, vec_second);
}
By steps
1) you need to create your vector
2) hold this memory in heap
3) just copy the data
It is going to be much more faster

Pass string like parameter to external method as char *

I'm creating Windows Runtime Component (c++) to use it later in windows phone 8.1 application (c#). My problem is preety simple but I cannot find any answer to it:
I need to create a method which take string/char */anything which is filepath as parameter and pass it to external method which takes char * as parameter.
I've tried with std::string, String^, char *. But I still get errors like such types are not supported (not in String^ case) or some other one.
Is there some simple answer which tells me how should I do this?
Code samples with errors
beginning
#include "pch.h"
#include "Class1.h"
using namespace Platform;
namespace WindowsRuntimeComponent2
{
public ref class Class1 sealed
{
various types:
int Foo(std::string param) {
cause: Foo': signature of public member contains native type 'std::basic_string<char,std::char_traits<char>,std::allocator<char>>' WindowsRuntimeComponent2 and few more containing same information about string's dependencies
int Foo(char * param) {
cause: 'Foo': signature of public member contains native type 'std::basic_string<char,std::char_traits<char>,std::allocator<char>>' WindowsRuntimeComponent2
int Foo(String^ photoPath) {
this one do not cause any errors but I don't know how to parse it to my char *.
Essentially here you just need to know how to convert from System.String^ to std::string, this can be done in C++/CLI mode as follows:
#include <msclr\marshal_cppstd.h>
//...
int Foo(String^ photoPath) {
std::string unmanaged_photoPath = msclr::interop::marshal_as<std::string>(photoPath);
// then convert to c-style string as normal using unmanaged_photoPath.c_str();
return 0;
}

C++ managed wrapper called from C#

I have some native C++ code and I have then created a wrapper to go around it.
creator.h
#pragma once
#include "file.h"
#define EXPORT __declspec(dllexport)
class EXPORT creator{
public:
creator();
~creator();
bool function1(int);
bool startFunction1(char *in, char *cache, char *out, long, double);
};
wrapper.h
#pragma once
using namespace System;
namespace nativeWrapper
{
public ref class Wrapper{
public:
creator *c;
Wrapper();
~Wrapper();
bool wStartCreating(System::String ^_in, System:: ^_cache, System:: ^_out);
};
}
wrapper.cpp
Lets just go straight to the wStartCreating function
bool Wrapper::wStartCreating(System::String ^_in, System:: ^_cache, System:: ^_out)
{
char *in = marshaling(_in);
char *out = marshaling(_out);
char *cache = marshaling(_cache);
return c->startFunction(in, cache, out, 0.0, 0.0);
}
in the c# code:
[DllImport("wrapper.dll")]
private static extern bool wStartCreating(String _in, String cache, String _out);
Then, I want to call wStartCreating(String, String, String) but I get a dllnotfoundexception whenever I try to make the call. I tried to add the reference (by the way this is in Visual studio 2005) and in the build event I copied the dll over to the referenced folder. Need some help as to why this runtime error may be coming up.
Thanks
-Tommy

Dll Import : Unable to find Entry Point "fnMultiply" in DLL "ImportDLL"

I am trying to use DLLImport for using Win32 dll method in C#.
Win32 dll C++
// .h file
#ifdef IMPORTDLL_EXPORTS
#define IMPORTDLL_API __declspec(dllexport)
#else
#define IMPORTDLL_API __declspec(dllimport)
#endif
// This class is exported from the ImportDLL.dll
class IMPORTDLL_API CImportDLL {
public:
CImportDLL(void);
// TODO: add your methods here.
int Add(int a , int b);
};
extern IMPORTDLL_API int nImportDLL;
IMPORTDLL_API int fnImportDLL(void);
IMPORTDLL_API int fnMultiply(int a,int b);
// .cpp file
// ImportDLL.cpp : Defines the exported functions for the DLL application.
//
#include "stdafx.h"
#include "ImportDLL.h"
// This is an example of an exported variable
IMPORTDLL_API int nImportDLL=0;
// This is an example of an exported function.
IMPORTDLL_API int fnImportDLL(void)
{
return 42;
}
IMPORTDLL_API int fnMultiply(int a , int b)
{
return (a*b);
}
Once i build this i get ImportDLL.dll
Now i create Windows Application and add this dll in debug folder and try to use this method using DLLImport
[DllImport("ImportDLL.dll")]
public static extern int fnMultiply(int a, int b);
And I try to call this in C#
int a = fnMultiply(5, 6); // This line gives error Unable to find an entry point
Can any body tell what i am missing?
Thanks.
If you are exporting a C function from a native DLL, you may want to use the __stdcall calling convention (which is equivalent to WINAPI, i.e. the calling convention used by most Win32 API C-interface functions, and which is the default for .NET P/Invoke):
extern "C" MYDLL_API int __stdcall fnMultiply(int a, int b)
{
return a*b;
}
// Note: update also the .h DLL public header file with __stdcall.
In addition, if you want to avoid name mangling, you may want to export using .DEF files.
e.g. Add a .DEF file to your native DLL project, and edit its content something like this:
LIBRARY MYDLL
EXPORTS
fnMultiply #1
...
(You can use the command line tool DUMPBIN/EXPORTS, or a GUI tool like Dependency Walker, to check the actual name with which the function is exported from the DLL.)
Then you can use P/Invoke like this from C#:
[DllImport("MyDLL.dll")]
public static extern int fnMultiply(int a, int b);
Turn off name mangling for the function your exporting. Should assist greatly. Alternative you could load the name mangled (there is a way to config the DllImport attribute to do this, so I hear, but I'm not a C# engineer, so I leave that to you to find if it exists).
extern "C" IMPORTDLL_API int fnMultiply(int a , int b)
{
return (a*b);
}

Categories

Resources