I have a WPF / c# application that uses Log4Net for logging. This application calls a few c++ dlls using:
[DllImport("test.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void TestFunction();
What I would like to do is have the dlls send logging messages back to the C# application, so that everything from both c++ and c# go to the same log. Is this possible?
If so, how can i go about it?
An example that redirect logs in c++ dill to c# callback:
c# side:
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
delegate void LogCallback([MarshalAs(UnmanagedType.LPWStr)] string info);
namespace WinApp
{
static class Wrapper
{
[DllImport("target.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern void SetLogCallback([MarshalAs(UnmanagedType.FunctionPtr)] LogCallback callbackPointer);
internal static void Init()
{
LogCallback log_cb = (info) =>
{
Console.Write(DateTime.Now.TimeOfDay.ToString() + " " + info);
};
SetLogCallback(log_cb);
}
}
c++ side( compiled in target.dll):
extern "C" {
typedef char* (__stdcall* LogCallback)(const wchar_t* info);
PEPARSER_API void SetLogCallback(LogCallback cb);
}
static LogCallback global_log_cb = nullptr;
void LogInfo(const wchar_t* info) {
if (global_log_cb) {
std::wstring buf = L"[PEParse]" + std::wstring(info) + L"\n";
global_log_cb(buf.c_str());
}
else {
std::cout << "global_log_cb not set\n";
}
}
void LogInfo(const char* info) {
const size_t cSize = strlen(info) + 1;
size_t t;
std::wstring wstr(cSize, L'#');
mbstowcs_s(&t, &wstr[0], cSize, info, cSize - 1);
LogInfo(&wstr[0]);
}
void SetLogCallback(LogCallback cb) {
global_log_cb = cb;
LogInfo("log init");
}
I have been using this interface for long times.
Related
I building a dll with golang, and need the dll receive a callback to trigger function directly from the dll
I tried execute callback on golang library but dont work
This is my code on golang
package main
import "C"
import "fmt"
type externFunc func(int)
//export Connect
func Connect(fn externFunc) {
fmt.Println("fn ",fn)
for i:= 0; i<3;i++{
// this is the func/method received from c#, and tried execute from golang
fn(i)
}
}
and so i build the library
go build -buildmode=c-shared -o library.dll main.go
this is my code on c#
class Program
{
static void Main(string[] args)
{
Connect()
}
private static unsafe void Connect()
{
Dll.CallbackDelegate cb = Callback;
// here sent the method to dll, to wait the multiple calls
Dll.Connect(cb);
}
private static unsafe void Callback(int num)
{
Console.WriteLine("call #: "+num);
}
}
class Dll {
private const string DllPath = "library.dll";
private IntPtr _dllHandle = IntPtr.Zero;
private static DllHelper _instance;
public static DllHelper Instance
{
get { return _instance ?? (_instance = new DllHelper()); }
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr LoadLibrary(string libname);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern bool FreeLibrary(IntPtr hModule);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern void FreeLibraryAndExitThread(IntPtr hModule, UInt32 dwExitCode);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi)]
private static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
internal Dll()
{
_dllHandle = IntPtr.Zero;
LoadDLL();
}
internal void LoadDLL()
{
bool? freeResult = null;
if (_dllHandle != IntPtr.Zero)
{
freeResult = FreeLibrary(_dllHandle);
}
// Load dll ..
_dllHandle = LoadLibrary(DllPath);
if (_dllHandle == IntPtr.Zero)
{
int errorCode = Marshal.GetLastWin32Error();
// Marshal.
throw new Exception(string.Format("Failed to load library (ErrorCode: {0})", errorCode));
}
}
and this part load the function from library
public unsafe delegate void CallbackDelegate(int num);
private unsafe delegate void ConnectDelegate(CallbackDelegate pFunc);
internal unsafe void Connect(CallbackDelegate pFunc)
{
var funcaddr = GetProcAddress(_dllHandle, "Connect");
var function = Marshal.GetDelegateForFunctionPointer(funcaddr, typeof(ConnectDelegate)) as
ConnectDelegate;
function.Invoke(pFunc);
}
}
this is the result when tried execute my console program created on C# with the go library
unexpected fault address 0xffffffffffffffff
fatal error: fault
[signal 0xc0000005 code=0x0 addr=0xffffffffffffffff pc=0x627ca9ab]
I think need receive an uinptr or something like that in the Connect and convert
the pointer on a type func but reading on other post says the type externFunc func is an func signature, and this is already a pointer.
Any idea what I need to make callbacks?
I register a function pointer that takes a string as parameter in the C++ dll that is used to send text back to the C# application. I tried to marshal the string parameter as IntPtr and BStr in addition to others. With the debug build I have no crash. With the release build the data are returned correctly (tested with log to a txt file). However, the C# program crashes with AccessViolationException.
Here is the code:
A: using BStr
C++:
typedef void(*callbackFunction)(BSTR resultDataJson);
extern "C" DLLEXPORT int setReceiveFunction(callbackFunction funcPointer)
{
//function pointer is stored
return 0;
}
//send the data back with function pointer
void MyClass::sendCallback(const QByteArray& msg)
{
if (this->m_callbackFunctionForReceivedData != nullptr)
{
BSTR resultMsg = this->ANSItoBSTR(msg.data());
this->m_callbackFunctionForReceivedData(resultMsg);
}
}
C#:
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void receiveCallbackData([MarshalAs(UnmanagedType.BStr)]string resultDataJson);
[DllImport("./myDll.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
static public extern int setReceiveFunction([MarshalAs(UnmanagedType.FunctionPtr)] receiveCallbackData functionCallback);
//callback function where data are received
private void functionCallback(string receivedJsonData)
{
//The received string can be logged here and looks correct.
}
B. using IntPtr
typedef void (*callbackFunction)(const char* resultDataJson);
extern "C" DLLEXPORT int setReceiveFunction(callbackFunction funcPointer)
{
//function pointer is stored
return 0;
}
//send the data back with function pointer
void MyClass::sendCallback(const QByteArray& msg)
{
if (this->m_callbackFunctionForReceivedData != nullptr)
{
this->m_callbackFunctionForReceivedData(msg.data());
}
}
C#:
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void receiveCallbackData(IntPtr resultDataJson);
[DllImport("./MyDll.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
static public extern int setReceiveFunction([MarshalAs(UnmanagedType.FunctionPtr)] receiveCallbackData functionCallback);
private void functionCallback(IntPtr receivedJsonDataInt)
{
string receivedJsonData = Marshal.PtrToStringAnsi(receivedJsonDataInt);
//The received string can be logged here and looks correct.
}
Both work in Debug but crash in Release. Why? What is the solution to this problem? Thanks for your help!
I'm trying to pass a string from C# to C++, using platform invoke.
C++ code:
#include<string>
using namespace std;
extern "C"
{
double __declspec(dllexport) Add(double a, double b)
{
return a + b;
}
string __declspec(dllexport) ToUpper(string s)
{
string tmp = s;
for(string::iterator it = tmp.begin();it != tmp.end();it++)
(*it)-=32;
return tmp;
}
}
C# code:
[DllImport("TestDll.dll", CharSet = CharSet.Ansi, CallingConvention =CallingConvention.Cdecl)]
public static extern string ToUpper(string s);
static void Main(string[] args)
{
string s = "hello";
Console.WriteLine(Add(a,b));
Console.WriteLine(ToUpper(s));
}
I receive a SEHException. Is it impossible to use std::string like this? Should I use char* instead ?
Сorrect decision
C# side:
[DllImport("CppDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr GetString(string s);
public string GetString_(string s)
{
var ptr = GetString(s);
var answerStr = Marshal.PtrToStringAnsi(ptr);
return answerStr;
}
C++ side:
extern "C" __declspec(dllexport) const char* GetString(char* s)
{
string workStr(s);
int lenStr = workStr.length() + 1;
char* answer = new char[lenStr];
const char * constAnswer = new char[lenStr];
strcpy(answer, workStr.c_str());
constAnswer = answer;
return constAnswer;
}
And disable /sdl- in the settings of the cpp project.
One way of doing it without causing a memory leak is using a callback.
C# side:
private delegate bool DLLCallback(IntPtr message);
[DllImport(#"YourLibrary.dll", SetLastError = true, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)]
private static extern void Receive(DLLCallback callback);
private static bool callback(IntPtr ptr)
{
string result = Marshal.PtrToStringAnsi(ptr);
Console.WriteLine(result);
// If the Heap is used
// Marshal.FreeHGlobal(ptr);
return true;
}
private static void Main(string[] args) {
Receive(callback);
}
C++ side:
extern "C" {
typedef BOOL(__stdcall* OutputCallback)(const char* str);
__declspec(dllexport) void Receive(OutputCallback callback)
{
char buffer[BUFFER_SIZE];
ZeroMemory(buffer, BUFFER_SIZE);
BOOL callbackResult = callback(buffer);
}
}
There are other options. This is a good article about passing strings between managed and unmanaged code: article
I suggest to use char*. Here a possible solution.
If you create another C# function ToUpper_2 as follows
C# side:
[DllImport("TestDll.dll"), CallingConvention = CallingConvention.Cdecl]
private static extern IntPtr ToUpper(string s);
public static string ToUpper_2(string s)
{
return Marshal.PtrToStringAnsi(ToUpper(string s));
}
C++ side:
#include <algorithm>
#include <string>
extern "C" __declspec(dllexport) const char* ToUpper(char* s)
{
string tmp(s);
// your code for a string applied to tmp
return tmp.c_str();
}
you are done!
I have this code in a c++ file, with compiles to a dll.
#include "stdafx.h"
#include "WHUU.h"
#include "stdafx.h"
typedef int (__stdcall * Callback)(const int text);
static int x , y= 0;
Callback Handler = 0;
int getX()
{
return x;
}
void setX(int i)
{
x = i;
}
void setY(int i)
{
y = i;
}
int getY()
{
return y;
}
extern "C" __declspec(dllexport)
void __stdcall SetCallback(Callback handler) {
Handler = handler;
}
extern "C" __declspec(dllexport)
void __stdcall addX(int x) {
setX(x);
}
extern "C" __declspec(dllexport)
void __stdcall addY(int y) {
setY(y);
}
extern "C" __declspec(dllexport)
void __stdcall TestCallback() {
int z = getX() + getY();
int retval = Handler(z);
}
My c# application now has to load this dll on runtime. Add to the callback and call the functions. I dont want to use a class. I could load the class and with
Type[] types = moduleAssembly.GetTypes();
But this overkill! Also c++ is not managed.
I mean its so tiny! (and yes this is an example , but the "real" is just as big as this example).
How do i do that?
Thanks you for your help!
add:
i dont like frameworks (like pinvoke / assembly)
the function names / types are fixed and will never change (think of a driver.dll read write)
this dll is written by customers so it should be as easy as possible!
You could also do it over p/invoke, as example:
[DllImport("unmanaged.dll", CharSet = CharSet.Ansi)]
private extern static int yourFunction(int var1, int var2);
i dont like frameworks (like pinvoke / assembly)
I'll suggest P/Invoke anyway. I don't think there are any reasonable alternatives. Perhaps writing a managed wrapper in managed C++, but really?
When you use P/Invoke the .NET runtime will dynamically load the DLL's that you have specified (and this is done when you first call the function). There is no reason to use P/Invoke to call LoadLibrary first.
You can either use P/Invoke to call the dll directly, or you can create a C++/CLI wrapper for it.
Here's how you can do it using P/Invoke:
Add the compiled dll to your C# project, set its 'Copy to Output Directory' property to 'Copy Always'. and call it like this:
class Program
{
[DllImport("cppdll.dll")]
private static extern void addX(int x);
[DllImport("cppdll.dll")]
private static extern void addY(int y);
static void Main(string[] args)
{
addX(5);
addY(7);
}
}
One way to do this would be through API. Find below an example for the setX and getX method from your sample above.
[DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)]
internal static extern IntPtr LoadLibraryEx(string libraryPath, IntPtr fileHandle, int actionFlag);
[DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)]
internal static extern bool FreeLibrary(IntPtr libraryHandle);
[DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)]
internal static extern IntPtr GetProcAddress(IntPtr dllPointer, string functionName);
IntPtr ptr = IntPtr.Zero;
private delegate void setX (int i);
private delegate int getX();
private setX setXDel;
private getX getXDel;
public Constructor()
{
loadLib();
}
public void YourMethod()
{
setXDel(100);
int y = getXDel();
Console.WriteLine(y.ToString());
}
private void loadLib()
{
string path = "your dll path";
ptr = LoadLibraryEx(path, IntPtr.Zero, 0);
if (ptr == IntPtr.Zero)
throw new Exception("Cannot load dll.");
IntPtr addressPtr = GetProcAddress(ptr, "setX");
setXDel = (setX)Marshal.GetDelegateForFunctionPointer(addressPtr, typeof(setX));
addressPtr = GetProcAddress(unrarPtr, "getX");
getXDel = (getX)Marshal.GetDelegateForFunctionPointer(addressPtr, typeof(getX));
}
public void Dispose()
{
if (ptr != IntPtr.Zero)
{
FreeLibrary(ptr);
}
}
I used P/Invoke, post my own cause the callback was not included by the others. But they were good answers! The C# Applicationhas use this GUI.
The Code for the Application is this : (mind it is a prototype to answer a technical question)
public partial class Form1 : Form
{
private delegate int Callback(int text);
private Callback mInstance; // Ensure it doesn't get garbage collected
[DllImport(#"C:\Visual Studio 2010\Projects\kalkulatorius\Debug\WHUU1.dll")]
private static extern void SetCallback(Callback fn);
[DllImport(#"C:\Visual Studio 2010\Projects\kalkulatorius\Debug\WHUU1.dll")]
private static extern void addX(int x);
[DllImport(#"C:\Visual Studio 2010\Projects\kalkulatorius\Debug\WHUU1.dll")]
private static extern void addY(int y);
[DllImport(#"C:\\Visual Studio 2010\Projects\kalkulatorius\Debug\WHUU1.dll")]
private static extern void TestCallback();
private int Handler(int text)
{
textBox3.Text = text.ToString();
return 42;
}
private void button1_Click(object sender, System.EventArgs e)
{
mInstance = new Callback(Handler); // to set the callback in lib
SetCallback(mInstance); // could also be withhin constructor!
addX(int.Parse(textBox1.Text)); // other people in this thread posted this correct answer
addY(int.Parse(textBox2.Text));
TestCallback();
}
public Form1()
{
InitializeComponent();
}
So if you want to use a c++ lib with functions and callbacks (like in the question posted) you can use this.
But how to change the lib?
The Path to the lib is hardcoded (see [dllImport ......] ). But you
can swop the lib in filesystem. This can happen AFTER you build this
application.
If the lib is not on the given path an exception is thrown. So if
your program wants to use this lib first check the lib is present on the filesystem.
(not included in this simple prototype)
So to switch functionality (swop a driver lib ect) you copy a different lib with the
same name into the given path. (Copy paste - maybe change name)
This solution has the least overhead and is good if you never change the interface of an dll after the application was build!
I have an nunit Test in C#, that calls a C# wrapper of a function in a C++ DLL.
The C++ code uses std::cerr to output various messages.
These messages cannot be redirected using nunit-console /out /err or /xml switch.
In nunit (the GUI version) the output does not appear anywhere.
I would like to be able to see this output in nunit (GUI version).
Ideally I would like to be able to access this output in the Test.
Thanks for any help.
Redirecting std::cerr is a matter of replacing the stream buffer with your own.
It is important to restore in original buffer before we exit. I don't know what your wrapper looks like, but you can probably figure out how to make it read output.str().
#include <iostream>
#include <sstream>
#include <cassert>
using namespace std;
int main()
{
streambuf* buf(cerr.rdbuf());
stringstream output;
cerr.rdbuf(output.rdbuf());
cerr << "Hello, world!" << endl;
assert(output.str() == "Hello, world!\n");
cerr.rdbuf(buf);
return 0;
}
Thanks for the hint.
This is what I ended up doing:
.CPP file ------------------------
#include <iostream>
#include <sstream>
static std::stringstream buffer;
static std::streambuf * savedBuffer = NULL;
extern "C" __declspec(dllexport) bool Redirect()
{
if (savedBuffer)
{
return false;
}
std::streambuf * buf(std::cerr.rdbuf());
std::cerr.rdbuf(buffer.rdbuf());
// This two lines are for illustration purposes only!
std::cerr << "Hello world" << std::endl;
return true;
}
extern "C" __declspec(dllexport) void Revert()
{
if (savedBuffer)
{
std::cerr.rdbuf(savedBuffer);
}
savedBuffer = NULL;
}
extern "C" __declspec(dllexport) const char * getCerr()
{
return _strdup(buffer.str().c_str());
}
extern "C" __declspec(dllexport) void freeCharPtr(char *ptr)
{
free(ptr);
}
.CS file ------------------------------------------
public static class Redirector
{
// PRIVATE ------------------------------------------------------------
private const String LibraryName = "MyCpp.dll";
[DllImport(LibraryName, CharSet = CharSet.Ansi)]
private static extern IntPtr getCerr();
// PUBLIC -------------------------------------------------------------
[DllImport(LibraryName, CharSet = CharSet.Ansi)]
public static extern bool Redirect();
[DllImport(LibraryName, CharSet = CharSet.Ansi)]
public static extern void Revert();
[DllImport(LibraryName, CharSet = CharSet.Ansi)]
internal static extern void freeCharPtr(IntPtr ptr);
public static string GetCerr()
{
IntPtr temp = getCerr();
string result = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(temp);
freeCharPtr(temp);
return result;
}
}
NUnit Test -----------------------
[Test]
// [Ignore]
public void TestRedirect()
{
Redirector.Redirect();
// Call more functions that output to std::cerr here.
Redirector.Revert();
System.Console.WriteLine(Redirector.GetCerr());
}
The freeCharPtr() stuff is necessary to free the allocated memory from _strdup(), since I could not work out (if it's even possible) how to marshal an std::string.
Note: This is not thread safe!