Working with Visual Studio 2019. Calling c functions from c# is common but when there is a runtime error, I get a popup window as such:
I would like however to actually break into the c++ code so I can debug it,in particular review the call stack, watch the local variables, add break points etc.
How to achieve this ?
If one need a demo project:
https://github.com/mprevot/InteropDemo
I'm calling a c function designed to fail:
#ifndef Pinvoke
#define Pinvoke extern "C" __declspec(dllexport)
#endif
std::wstring ToString(int number)
{
std::wstringstream s;
s << "got number " << number;
return s.str();
}
Pinvoke auto GetNumbers() -> void
{
std::vector<int> array0{11,12,13,14};
std::vector<int> array1{21,22,23};
for (size_t i = 0; i < 4; i++)
ToString(array0[i]);
for (size_t i = 0; i < 4; i++)
ToString(array1[i]);
}
I call such function from c#:
internal static class NativeLibCall
{
public const string _dll = "NativeLib.dll";
[DllImport(_dll, CallingConvention = CallingConvention.StdCall)]
internal static extern void GetNumbers();
}
public class NativeLibInterop
{
public void GetNumbers() => NativeLibCall.GetNumbers();
}
As suggested by Kevin Gosse, and detailed in MSFT documentation, just enable mixed-mode debugging:
Enable mixed-mode debugging for a managed calling app
Select the C# or Visual Basic project in Solution Explorer and select the Properties icon, press Alt+Enter, or right-click and choose
Properties.
Select the Debug tab, and then select Enable native code debugging.
Close the properties page to save the changes.
Related
I am working on a project and currently have the following structure:
C# WPF project containing the User Interface as well as calls to external methods.
C++ DLL project containing an algorithm.
ASM DLL project containing an algorithm.
For simplicity, let's assume the algorithm simply takes no parameters and returns the sum of two, predefined numbers.
Here's the function signature and implementation in the C++ (second) project:
int Add(int x, int y)
{
return x + y;
}
extern "C" __declspec(dllexport) int RunCpp()
{
int x = 1, y = 2;
int z = Add(x, y);
return z;
}
And here's how I call the function in C#:
[DllImport("Algorithm.Cpp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int RunCpp();
This works just fine - calling the function in C# returns the value 3, everything is working proplerly, no exceptions thrown.
However, I am now struggling to call the ASM procedure in C# code.
I have seen (and tested myself to an extent) that it's impossible to call a MASM DLL directly in C# code. However, I've heard that it's possible to call ASM in C++ and call that function in C#.
1. My first question is - is calling ASM code actually possible directly in C#? When I try that, I get an exception that basically says the binary code is incompatible.
2. I have tried to use C++ to indirectly call the ASM DLL, and while I get no exception, the returned value is "random", as in, it feels like a remainder left in memory, for example: -7514271. Is this something I'm doing wrong, or is there another way to achieve this?
Here's the code for calling ASM in C++:
typedef int(__stdcall* f_MyProc1)(DWORD, DWORD);
extern "C" __declspec(dllexport) int RunAsm()
{
HINSTANCE hGetProcIDDLL = LoadLibrary(L"Algorithm.Asm.dll");
if (hGetProcIDDLL == NULL)
{
return 0;
}
f_MyProc1 MyProc1 = (f_MyProc1)GetProcAddress(hGetProcIDDLL, "MyProc1");
if (!MyProc1)
{
return 0;
}
int x = 1, y = 2;
int z = MyProc1(x, y);
FreeLibrary(hGetProcIDDLL);
return z;
}
Here, the code for calling C++ in C#:
[DllImport("Algorithm.Cpp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int RunAsm();
And here's the ASM code of MyProc1, if needed:
Main.asm:
MyProc1 proc x: DWORD, y: DWORD
mov EAX, x
mov ECX, y
add EAX, ECX
ret
MyProc1 endp
Main.def:
LIBRARY Main
EXPORTS MyProc1
is calling ASM code actually possible directly in C#?
Example of this with two projects, C# and assembly based DLL. Looks like you already know how to get a C++ based DLL working. The project names are the same as the directory names, xcs for C# and xcadll for the dll. I started with empty directories and created empty projects, then moved source files into the directories and then added existing items to each project.
xcadll properties:
Configuration Type: Dynamic Library (.dll)
Linker | Input: xcadll.def
xcadll\xcadll.def:
LIBRARY xcadll
EXPORTS DllMain
EXPORTS Example
xcadll\xa.asm properties (for release build, /Zi is not needed):
General | Excluded From Build: No
General | Item Type: Custom Build Tool
Custom Build Tool | General | Command Line: ml64 /c /Zi /Fo$(OutDir)\xa.obj xa.asm
Custom Build Tool | General | Outputs: $(OutDir)\xa.obj
xcadll\xa.asm:
includelib msvcrtd
includelib oldnames ;optional
.data
.data?
.code
public DllMain
public Example
DllMain proc ;return true
mov rax, 1
ret 0
DllMain endp
Example proc ;[rcx] = 0123456789abcdefh
mov rax, 0123456789abcdefh
mov [rcx],rax
ret 0
Example endp
end
xcs\Program.cs:
using System;
using System.Runtime.InteropServices;
namespace xcadll
{
class Program
{
[DllImport("c:\\xcadll\\x64\\release\\xcadll.dll")]
static extern void Example(ulong[] data);
static void Main(string[] args)
{
ulong[] data = new ulong[4] {0,0,0,0};
Console.WriteLine("{0:X16}", data[0]);
Example(data);
Console.WriteLine("{0:X16}", data[0]);
return;
}
}
}
For debug, use
[DllImport("c:\\xcadll\\x64\\debug\\xcadll.dll")]
xcs properties | debug | enable native mode debugging (check the box)
I am trying to get map from dll c++
So I have to get the map and parsing it as dictionary in C# side.
I have tried to do below steps and it is not worked.
C++ code:
extern "C" __declspec(dllexport) map<string, int> createMap(string &fileName) {
ifstream infile(fileName);
vector<string> bitsLine;
bool headerEnded = false;
string line;
int i = 0;
int length = 0;
while (getline(infile, line)) {
if (headerEnded) {
bitsLine = split(line, ',');
signalsMap.insert({ bitsLine.at(0), length });
}
else {
if (line.find("HEADER_END") != std::string::npos) {
headerEnded = true;
}
}
length = infile.tellg();
i++;
}
return signalsMap;
}
C# code:
Dictionary<string, int> x = createMap("C:/users/asalah/source/repos/WindowsFormsApp3/WindowsFormsApp3/RR_Test2_3.csv");
The simple answer to this question is unfortunately "you shouldn't". You shouldn't export STL types from a dll in the first place, much less try to marshal them in C#. An STL type may vary in memory layout from compiler to compiler, C++ runtime to C++ runtime. It could cause very fragile code. So if you export a C function it should take a const char* instead of std::string for example.
What you could do could be to just marshal each key and value as they are made available. The advantage of this is that you don't have to do any work with memory management and it's fairly simple to integrate in what you already have, though I'm making no statement about performance.
Here is a short C++ and C# example to get you going on such a solution if it is of any help to you:
extern "C" __declspec(dllexport) void doFoo(void(*adder)(const char*, int32_t))
{
adder("Test", 346);
}
Below is the C# code for consuming this API. It should simply just add "Test" with the value 346 to the dictionary and nothing more. It does this by invoking a callback function which is a native shim around Dictionary.Add for the specified instance of the dictionary.
namespace Eff3
{
using System.Collections.Generic;
using System.Runtime.InteropServices;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void DictionaryAdd(string key, int value);
class Program
{
[DllImport("TestDll", CallingConvention = CallingConvention.Cdecl)]
static extern void doFoo(DictionaryAdd callback);
static void Main()
{
var result = new Dictionary<string, int>();
doFoo(result.Add);
}
}
}
I've tested this on my machine, and I built the DLL in Visual C++ 2017 in x64 and disabled "Prefer 32-bit" in C#.
This question already has answers here:
EEFileLoadException when using C# classes in C++(win32 app)
(6 answers)
Closed 4 years ago.
This is my first time with calling functions from c# dlls.
I have made a c# wrapper(c++/cli) to call c# functions from c++. This wrapper itself is a dll which I want to use in my main code. Below are the wrapper.h and wrapper.cpp files. CSharpWrapper is the abstract class that inherits the base class ImageTool. At the end of the header file I am exporting a factory of the wrapper class.
wrapper.h
#define Export_Wrapper
#ifdef Export_Wrapper
#define _DLLExport __declspec(dllexport)
#else
#define _DLLImport __declspec(dllimport)
#endif // Export_Wrapper
class CSharpWrapperPrivate;
class _DLLExport CSharpWrapper : public ImageTool
{
public:
CSharpWrapper();
~CSharpWrapper();
int Initialize();
int Deinitialize();
cv::Mat AcquireImg(double positionX, double positionY);
private:
CSharpWrapperPrivate* _private;
};
extern "C" __declspec(dllexport) ImageTool* __cdecl createFactory()
{
return new CSharpWrapper;
}
wrapper.cpp
class CSharpWrapperPrivate
{
public:
msclr::auto_gcroot<ImageDistiller^> ImageAPI;
};
CSharpWrapper::CSharpWrapper()
{
}
CSharpWrapper::~CSharpWrapper()
{
delete _private;
}
int CSharpWrapper::Initialize()
{
_private = new CSharpWrapperPrivate();
_private->ImageAPI = gcnew ImageDistiller();
return _private->ImageAPI->Initialize();
}
int CSharpWrapper::Deinitialize()
{
return _private->ImageAPI->Deinitialize();
}
cv::Mat CSharpWrapper::AcquireImg(double positionX, double positionY)
{
Mat img = _private->ImageAPI->AcquireImg(positionX, positionY);
}/**/
In the wrapper.cpp file, visual studio is able to suggest the functions available in the c# dll. The following is the main.cpp code where I am trying to call functions through this wrapper.
main.cpp
typedef ImageTool*(__cdecl *ImgFactory)();
ImageTool* instance_imgcapture;
HINSTANCE dll_imgcapture_handle;
int main()
{
dll_imgcapture_handle = ::LoadLibrary(TEXT("wrapper.dll"));
if (!dll_imgcapture_handle) {
std::cerr << "Unable to load wrapper DLL!\n";
return 1;
}
ImgFactory zximgcapturefactory = reinterpret_cast<ImgFactory>(::GetProcAddress(dll_imgcapture_handle, "createFactory"));
if (!zximgcapturefactory)
{
std::cerr << "Unable to load factory from wrapper.dll!!\n";
::FreeLibrary(dll_imgcapture_handle);
return 1;
}
instance_imgcapture = zximgcapturefactory();
instance_imgcapture->Initialize();
return 1;
}
Now, when I run main.cpp, it is able to create dll_imgcapture_handle and ImgFactory. But it gives the following error when instance_imgcapture->Initialize() is called:
Unhandled exception at 0x00007FFD20B5A388 (KernelBase.dll) in wrapper.exe: 0xE0434352 (parameters: 0xFFFFFFFF80070002, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x00007FFD03580000).
I tried this method to load dlls (as in main.cpp) with other c++ dlls - it works.
I tried to check if there are any missing dlls using dependency_walker- there were none.
I have included the correct opencv libraries and they work correctly. I have included the path to the dlls and libs in the project properties and added the lib names in Linker->Input.
Can you please help me to debug this problem.
It was a mistake related to the dependencies. Since I am trying to call managed code from unmanaged, the output produced by the debugger does not clearly mention the error and just outputs "Unhandled exception at 0x00....". I used dependency walker to look for any missing dependencies but it cannot find anything probably because I am trying to load the dll manually in the main code.
One of the answers in the link mentioned by AlexF solved my problem.(https://stackoverflow.com/a/31723080/5484662)
I first changed the "Debugger Type" to "Mixed" mode in configurationProperties->Debugging. This resulted in a more detailed error which mentions clearly which dependency is missing. And I copied the dependencies in the Debug folder and it works.
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++.
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.