After looking for solutions already proposed to a similar question to mine,
and since this is the first time I'm using a non-.NET DLL in a .NET application,
I really need your help.
I have an WPF application, using MVVM Pattern, and in my ViewModel class I need to use a DLL done in C++ to recover a token. I have an example in C++ that uses this DLL, so I have the method's names, but I can't do the same in C#. I know that I must use DllImport to use this methods, but how implement it and use the pointer in C#??
#include "stdafx.h"
#include <windows.h>
#include <iostream>
#include <string>
int main()
{
HINSTANCE hinst = NULL;
typedef bool ( *GetTokenProto )( char ** );
typedef void ( *FreeTokenProto )( char * );
GetTokenProto GetToken;
FreeTokenProto FreeToken;
std::string str = "DllName.dll";
std::string token;
if ( (hinst = LoadLibraryA(str.c_str()) ) )
{
GetToken = (GetTokenProto) GetProcAddress(hinst, "GetToken");
FreeToken = (FreeTokenProto) GetProcAddress(hinst, "FreeToken");
if (GetToken && FreeToken)
{
char *buf;
if (GetToken(&buf))
{
token = buf;
FreeToken(buf);
std::cout << "Token:" << token << std::endl;
}
else
{
std::cerr << "DLL loaded but no token" << std::endl;
exit(1);
}
}
else
{
std::cerr << "DLL loaded but missing proc address(es)" << std::endl;
exit(1);
}
FreeLibrary(hinst);
}
else
{
std::cerr << "Failed to load DLL" << std::endl;
exit(1);
}
return 0;
}
Update
[DllImport("DllName.dll", EntryPoint = "GetToken", CallingConvention = CallingConvention.Cdecl)]
public static extern bool get_token(ref string token);
[DllImport("DllName.dll", EntryPoint = "FreeToken", CallingConvention = CallingConvention.Cdecl)]
public static extern void free_token(ref string token);
public static string a_token;
public string get_token_method()
{
try
{
string buffer = null;
if (get_token(ref buffer))
{
a_token = buffer;
free_token(ref buffer);
Debug.WriteLine("token : " + a_refresh_token);
}
else
{
Debug.WriteLine("DLL Loaded but no token");
}
}
catch (Exception ex)
{
Debug.WriteLine("\n" + ex.Message);
}
return a_refresh_token;
}
The error
I have an Exception "System.DllNotFoundException" : Unable to load DLL
'DllName.dll': The specified module could not be found. (Exception
from HRESULT: 0x8007007E).
Dll file is in the same folder of the .exe (..\bin\Debug)
If your DLL is in the same Directory, there are still a few things that might be the problem.
Firstly
The DLL may have dependencies
The native DLL you use may have other dependencies which have to be
installed (try Dependency Walker). If the native DLL requires for
example registry settings, config files etc. these should also be
present. It should be distributed to user machines the same way you
installed it on the dev machine.
Most likely you are missing the C++ redistributable package (which one I'm not sure) however Dependency Walker should tell you.
Secondly
It could be targeting a different bitness i.e x86 x64, so I'd try changing your project to see if that helps
Right click your project, and select properties.
In properties, select the build tab. Under platform target, select x86.
Hit Ctrl+Shift+S to save all files, right click the solution and select "Clean" to get rid of old binaries. Any builds after that
should be 32 bit
You can use SetDllDirectory method to set path of your dll before invoking your dll
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool SetDllDirectory(string lpPathName);
You should also use StringBuilder instead of string on c# side if you want to get string from c++.
Related
I have to read a .json file with ifstream from a c++ dll, and I need to use that dll in Unity. I know that there's a problem with Unicode and ASCII, but I can't solve this problem
PS: I made convert_to_string myself, it extract an int from a certain string
In the header of the Dll I have:
int leggiCpp(string nomeFile);
extern "C" {
QUARTALIBRERIA_API int leggiCs(wstring nomeFile);
}
In the cpp of the Dll I have:
int leggiCpp(string nomeFile) {
ifstream _stream(nomeFile.c_str());
string temp;
_stream >> temp >> temp >> temp >> temp >> temp;
return convert_to_int(temp);
}
QUARTALIBRERIA_API int leggiCs(wstring nomeFile){
std::string str = std::string(nomeFile.begin(), nomeFile.end());
int daRit = leggiCpp(str);
return daRit;
}
In the Unity script I have:
[DllImport("QuartaLibreria.dll", CharSet = CharSet.Unicode)]
static extern unsafe int leggiCs(string nomeFile);
// Start is called before the first frame update
unsafe void Start()
{
int temp = leggiCs("Assets/alzBraccioCorretto.json");
}
I have no compiling error, but the Unity program crashes and says: Runtime Error!
Program: This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
I am trying to write an C/C++ dynamic library in Xcode, compile it to an .dylib library package (or whatever you call that) and [DLLImport] it in .NET Core.
Little background on why: A Chinese company develops a device for us, for integrating their device in our software they wrote a demo library in Borland C++ to test the integration and that worked out.
Now I want to know if it is possible for us to import an C++ library, written in Xcode, into our application as well, using .NET Core or Xamarin.
Now I am a rooky at C/C++ and I am a bit new to the cross platform solutions provided by Microsoft. But according to this github question DLLImport should work on Mac. Now I am wondering how.
So, in my best effort to write a C++ library:
ApiFunc.h
#ifndef ApiFuncH
#define ApiFuncH
double mean(double x, double y);
typedef void (*SignalHandler)(int signum);
typedef int (*OPEN_IMAGE_FILE)(char*);
extern OPEN_IMAGE_FILE open(char *FileName);
extern SignalHandler signal(int signum, SignalHandler handler);
class TAPIFunc
{
public:
int OpenImageFile(char *FileName);
};
#endif
ApiFunc.cpp
#pragma hdrstop
#include "ApiFunc.h"
double mean(double x, double y){
return x * y;
}
int TAPIFunc::OpenImageFile(char *FileName)
{
return 5;
}
Just trying some different methods out there...
So this compiles to a libtestmachw.dylib
I import that into my .NET Core console application:
[DllImport("libtestmachw.dylib", EntryPoint = "mean")]
private static extern double mean(double x, double y);
[DllImport("libtestmachw.dylib")]
private static extern int OPEN_IMAGE_FILE(string fileName);
[DllImport("libtestmachw.dylib")]
private static extern int OpenImageFile(string fileName);
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
Console.WriteLine("Let's try to communicate with a Mac dylib library");
Console.WriteLine("We are now going to invole function 'mean' of libtestmacw.dylib");
try
{
double result = mean(2, 4);
Console.WriteLine("yes, we made it! Result:" + result);
}
catch (Exception e)
{
Console.WriteLine("Opes that didn't work!");
Console.WriteLine(e);
}
Console.WriteLine("We are now going to invole function 'OPEN_IMAGE_FILE' of libtestmacw.dylib");
try
{
int result = OPEN_IMAGE_FILE("SomeFile.png");
Console.WriteLine("yes, we made it! Result:" + result);
}
catch (Exception e)
{
Console.WriteLine("Opes that didn't work!");
Console.WriteLine(e);
}
Console.WriteLine("We are now going to invole function 'OpenImageFile' of libtestmacw.dylib");
try
{
int result = OpenImageFile("SomeFile.png");
Console.WriteLine("yes, we made it! Result:" + result);
}
catch (Exception e)
{
Console.WriteLine("Opes that didn't work!");
Console.WriteLine(e);
}
Console.ReadLine();
}
When running this, on Mac, I get an System.EntryPointNotFoundException
Unable to find an entry point named 'OpenImageFile' in DLL 'libtestmachw.dylib'.
I just want to test if I can import functions inside a .NETCore application, from their on I can instruct the Chinese company to get their code compiled into a .dylib. Who can help me out or point me into the right direction?
This Microsoft page shows it is possible, so I am guess I am doing something wrong at the c/c++ side? https://learn.microsoft.com/en-us/dotnet/standard/native-interop
You cannot call C++ functions, only C functions. Also, these functions need to be exported using standard C conventions, so in C++ you might need to add an extern "C" as well.
The problem in your sample is that TAPIFunc::OpenImageFile is a C++ function that cannot be imported.
For example, this function defined in a .cpp file can be used in .NET Core:
extern "C" int32_t Bridge_OpenFile(char* filename) {
// you can do some C++ calls here
return 0;
}
using
[DllImport("my.dylib")]
private static extern int Bridge_OpenFile([MarshalAs(UnmanagedType.LPStr)]string filename);
Another trick: DllImport can be used with just testmachw since CoreCLR will add lib and .dylib automatically as part of the probing so you can have the same C# code and it will match lib{foo}.dylib, lib{foo}.so and {foo}.dll depending on the platform run on.
I have a C# Project which Invoke a C++ dll
And before returning the value in the C++ dll, I would like to check the name of the C# exe which invoke my method. Can you advice me please?
I Load the c++ dll like this:
[DllImport("MindSystem.dll",
EntryPoint = "MindSystemPlusPlus",
CharSet = CharSet.Ansi,
CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl)]
public static extern IntPtr MindSystemPlusPlus(int value);
And when I load it, I want that the c++ dll check the name of the exe which invoke it
Edit: I tried this code, but the output in c# is in strange characters :
char fileName[MAX_PATH + 1];
GetModuleFileNameA(NULL, fileName, MAX_PATH + 1);
return fileName;
You should try using GetModuleFileName() function. You can get the full path of the exe. Keep in mind if your DLL is loaded by more than one applications then returned file path will refer to only one of them.
You can call GetModuleFileName function. NULL as first parameter means that path to the executable of the current process is requested.
std::string expectedPath("C:\\expected.exe");
TCHAR fileName[MAX_PATH + 1];
DWORD charsWritten = GetModuleFileName(NULL, fileName, MAX_PATH + 1);
if (charsWritten != 0)
{
if (expectedPath == fileName)
{
// do something
}
}
#include <windows.h>
#include <shellapi.h>
int argc = 0;
auto wargv = CommandLineToArgvW(GetCommandLineW(), &argc);
auto program_path = wargv[0];
...
LocalFree(wargv);
documentation:
https://msdn.microsoft.com/en-us/library/windows/desktop/bb776391(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/ms683156(v=vs.85).aspx
It depends.
If you are using c++ with /clr you can use read the name of the Process returned from Process::GetCurrentProcess().
In native code in Windows you can use GetModuleFileName()
In Linux or MAC there are different options depending on your platform.
I’ve built a C# DLL (MyTestDll) using the NuGet package UnmanagedExports:
[DllExport("Test", CallingConvention = CallingConvention.Cdecl)]
public static string Test(string name)
{
return "hi " + name + "!";
}
I use it from Python via ctypes DLL import:
path = "C:\\Temp\\Test"
os.chdir(path)
dll = ctypes.WinDLL("MyTestDll.dll")
f = dll.Test
f.restype = ctypes.c_char_p
print f('qqq')
It’s just a fantasy, it works.
Then, I added one more DLL (NoSenseDll):
namespace NoSenseDll
{
public class NoSenseClass
{
public static int Sum(int a, int b)
{
return a + b;
}
}
}
I started to use this NoSenseDll to implement MyTestDll:
[DllExport("Test", CallingConvention = CallingConvention.Cdecl)]
public static string Test(string name)
{
return NoSenseDll.NoSenseClass.Sum(4, 5).ToString();
}
Unfortunately, it does not work. Python says:
WindowsError: [Error -532462766] Windows Error 0xE043435
I’ve tried to add C:\\Temp\\Test to path, but that did not help.
I’ve written a C++ test:
#include "stdafx.h"
#include "windows.h"
#include <iostream>
#include <string>
#include "WinBase.h"
typedef char*(__stdcall *f_funci)(const char*);
int _tmain(int argc, _TCHAR* argv[])
{
int t;
std::string s = "C:\\Temp\\Test\\MyTestDll.dll";
HINSTANCE hGetProcIDDLL = LoadLibrary(std::wstring(s.begin(), s.end()).c_str());
f_funci funci = (f_funci)GetProcAddress(hGetProcIDDLL, "Test");
std::cout << "funci() returned " << funci(std::string("qqq").c_str()) << std::endl;
std::cin >> t;
return EXIT_SUCCESS;
}
It works if the second DLL (NoSenseDll) is in the same folder as the C++ executable. It does not work if I just add NoSenseDll folder to PATH.
Draft solution:
Copy NoSenseDll to the folder of Python, in my case %HOMEPATH%\Anaconda.
Restart IPython/Spyder.
Final solution:
static MyTestDllClass() // static constructor
{
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve += new ResolveEventHandler(LoadFromSameFolder);
}
static Assembly LoadFromSameFolder(object sender, ResolveEventArgs args)
{
string folderPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
string assemblyPath = Path.Combine(folderPath, new AssemblyName(args.Name).Name + ".dll");
if (File.Exists(assemblyPath) == false) return null;
Assembly assembly = Assembly.LoadFrom(assemblyPath);
return assembly;
}
Final note:
If you can’t use IronPython because of matplotlib or pandas,
if you can’t use python.net because of IPython or spyder,
if you don’t want to use COM Interop just because,
and you really want to get C# and Python to work together, use the solution above and C# reflection.
You can also check out Costura.Fody.
This is a build task that will add your dependencies as resources to your assembly and even hooks up a module initializer to load them at runtime.
you cant do it directly with managed code.
register COM object from you dll:
%SystemRoot%\Microsoft.NET\Framework\v2.0.50727\regasm.exe my.dll /tlb:my.tlb /codebase
and make com call from python.
see here examples: http://www.codeproject.com/Articles/73880/Using-COM-Objects-in-Scripting-Languages-Part-Py
There are a few people asking around to know how to use the dl library to dynamically load and invoke a plugin from C# on non-windows systems.
The advice appears to be, generally speaking, 'use GetDelegateForFunctionPointer'. However, no one can supply an actually working example of both loading the external library AND invoking it.
The following code:
class Helper
{
const int RTLD_NOW = 2;
[DllImport("dl")]
static extern IntPtr dlopen([MarshalAs(UnmanagedType.LPTStr)] string filename, int flags);
[DllImport("dl")]
static extern IntPtr dlsym(IntPtr handle, [MarshalAs(UnmanagedType.LPTStr)] string symbol);
public static void Test() {
IntPtr libraryHandle = dlopen("libextern.dylib", RTLD_NOW);
IntPtr action = dlsym(libraryHandle, "rs_trigger");
var caller = (rs_trigger) Marshal.GetDelegateForFunctionPointer(action, typeof(rs_trigger));
var r = caller(1);
log ("Success! " + libraryHandle + " .. " + action + " --> " + r);
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate int rs_trigger(int value);
}
Prints:
Success! 105553117399168 .. 6120303264 --> 1339432
However, the rs_trigger in the c code invoked does not return 1339432. Invoking the library using python and ctypes shows:
Python 2.7.9 (default, Jan 29 2015, 06:27:40)
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.56)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import ctypes
>>> lib = ctypes.CDLL("libextern.dylib")
>>> lib
<CDLL 'libextern.dylib', handle 7f7f83601e60 at 10b389550>
>>> lib.rs_trigger
<_FuncPtr object at 0x10b367940>
>>> lib.rs_trigger(1)
Trigger called
Invoking callback
No callback bound
101
>>> lib.rs_trigger(100)
Trigger called
Nope
200
>>> output = lib.rs_trigger(100)
Trigger called
Nope
>>> output
200
Clearly the C# call caller(1) is passing malformed data over to the c function call... but I've read a lot of example in the past hour, and all of them seem to say to do exactly this.
What am I doing wrong?
What exactly is the syntax to use for the delegate you define to bind the the IntPtr to to invoke it as an external function call?
(NB. everything is x86_64, that's categorically not the issue)
Here's the c code:
#include <stdio.h>
#ifdef _WIN64
#define __EXT __declspec(dllexport)
#elif _WIN32
#define __EXT __declspec(dllexport)
#else
#define __EXT extern
#endif
static void (*callback)(int value) = NULL;
__EXT int rs_trigger(int val) {
printf("Trigger called\n");
if (val == 1) {
printf("Invoking callback\n");
if (callback != NULL) {
(*callback)(100);
}
else {
printf("No callback bound\n");
}
}
else {
printf("Nope\n");
}
return val + 100;
}
__EXT void rs_register(void *cb) {
printf("Register called\n");
callback = cb;
}
...and finally, if you're going to down vote the question, please at the very least leave feedback on why this is poor question to ask.