I am trying to follow this example: http://blogs.artinsoft.net/Mrojas/archive/2008/06/19/Log4NET-for-C++.aspx
to get Log4Net running in a c++ dll.
I created an empty CLR project, changed the output type to lib, and added the following code:
//Bridge.cpp
#include <atlstr.h>
using namespace System;
/// <summary>
/// Example of how to simply configure and use log4net
/// </summary>
ref class LoggingExample
{
private:
// Create a logger for use in this class
static log4net::ILog^ log = log4net::LogManager::GetLogger("LoggingExample"); static LoggingExample()
{
log4net::Config::BasicConfigurator::Configure();
}
public:static void ReportMessageWarning(char* msg)
{
String^ data = gcnew String(msg);
log->Warn(data);
}
static void ReportMessageError(char* msg)
{
String^ data = gcnew String(msg);
log->Error(data);
}
static void ReportMessageInfo(char* msg)
{
String^ data = gcnew String(msg);
log->Info(data);
}static void ReportMessageDebug(char* msg)
{
String^ data = gcnew String(msg);
log->Debug(data);
}
};
extern "C"
{
_declspec(dllexport) void ReportMessageWarning(char* msg)
{
LoggingExample::ReportMessageWarning(msg);
}
_declspec(dllexport) void ReportMessageError(char* msg)
{
LoggingExample::ReportMessageError(msg);
}
_declspec(dllexport) void ReportMessageInfo(char* msg)
{
LoggingExample::ReportMessageInfo(msg);
}
_declspec(dllexport) void ReportMessageDebug(char* msg)
{
LoggingExample::ReportMessageDebug(msg);
}
}
I have created a c++ console application project, and added this code:
#include "stdafx.h"
#include <iostream>
#include <atlstr.h>
extern "C"
{
//log4net from the wrapper lib
_declspec(dllimport) void ReportMessageWarning(char* msg);
__declspec(dllexport) void RunTest()
{
std::string message = "hey it seems it is working";
ReportMessageWarning(_strdup(message.c_str()));
}
}
This will be a c++ dll that is called from another C# application.
I have compiled the clr lib, and linked it in the dependencies of the c++ application.
However, the c++ dll will not compile as is, giving me:
Error LNK2001 unresolved external symbol __imp_ReportMessageWarning
What have i missed here?
Related
I achieved interoperability between C# & C++ using SWIG.
However, the C ++ to C # callback does not work properly.
If you pass "std::wstring" to the callback on the C++ side, it will be treated as a "string" on the C# side, but only the first character of the string passed on the C++ side will be stored.
My goal is using "std::string" in argument of callback.
Creating a base class with virtual function in C++ and derive a class in C# which implement the virtual method.
I referred to this link.
Callback from C++ to C# using SWIG
My code what callback from C++ to C# using SWIG is following.
C++ : Header.h
#include <string>
#include <vector>
class CallbackBase
{
public:
virtual void Callback(const std::wstring& str) = 0;
};
class CppClass
{
public:
void SetString(const std::wstring &str);
std::wstring GetString();
void SetCallback(CallbackBase& callbackObject);
void ExeCallback();
private:
std::wstring string;
CallbackBase* pCallbackObj = NULL;
void InvokeCallback();
};
typedef CppClass* (*GetInstanceFuncPointer)(void);
extern "C" __declspec(dllexport) CppClass * CreateInstance(void);
C++ : Impl.cpp
#include <thread>
#include "Header.h"
void CppClass::SetString(const std::wstring& str)
{
string = str;
}
std::wstring CppClass::GetString()
{
return string;
}
void CppClass::SetCallback(CallbackBase &callbackObj)
{
pCallbackObj = &callbackObj;
}
void CppClass::ExeCallback()
{
std::thread *th = new std::thread(&CppClass::InvokeCallback, this);
th->join();
}
void CppClass::InvokeCallback()
{
if (pCallbackObj != NULL)
{
pCallbackObj->Callback(string);
}
}
__declspec(dllexport) CppClass* CreateInstance(void)
{
return new CppClass;
}
Swig : Interface file
%include <windows.i>
%include <wchar.i>
%include <std_wstring.i>
%module (directors="1") MyDLL
%{
#include "Header.h"
%}
%feature("director") CallbackBase;
%include "Header.h"
C# : main.cs
using System.Diagnostics;
namespace CsharpApp
{
public class MyCallback : CallbackBase
{
public override void Callback(string str)
{
Debug.WriteLine($"str = {str}"); // --> str = H
}
}
class Program
{
static void Main(string[] args)
{
MyCallback myCallbackObj = new MyCallback();
CppClass instance = MyDLL.CreateInstance();
string str = "Hello World!";
instance.SetString(str);
Debug.WriteLine($"str = {instance.GetString()}"); // --> str = Hello World!
instance.SetCallback(myCallbackObj);
instance.ExeCallback();
}
}
}
I'm getting the string twice on the C# side.
The first is "GetString()" in the main function.
In this case, "Hello World!" Is stored.
The second time I get it as an argument of the callback function.
In this case, only "H" is stored.
How can I correctly store the string in the callback function argument?
I have a test code that has this stack C#->C->GO->C#(ERROR HERE).
Whenever an error happens on C# and if that C# code is called by GOLANG the error is not redirected to error.std file. However, if I start the code from VS with the attached debugger the error is redirected to the file.
C#:
Program.cs
class Program {
[DllImport("gotestDLL.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
internal static extern void Error();
[DllImport("gotestDLL.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
internal static extern void SetCSharpPointerHandler(IntPtr ptr);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate IntPtr CSharpPointerHandler(IntPtr message);
private static CSharpPointerHandler _messageHandlerCb = new CSharpPointerHandler(SomeMethodWithError);
private static IntPtr _messageHandlerCbPtr = Marshal.GetFunctionPointerForDelegate(_messageHandlerCb);
static void Main(string[] args) {
SetCSharpPointerHandler(_messageHandlerCbPtr);
Console.WriteLine("|-C#->START OF C#");
Error();
Console.WriteLine("|-C#->Waiting for program to crash");
do {
} while (true);
}
private static IntPtr SomeMethodWithError(IntPtr messagePtr) {
Console.WriteLine("|-C#->Back to C#");
var a = 0;
Console.WriteLine("|-C#->ERROR HAPPENS HERE C#");
var b = 10 / a;
Console.WriteLine(b);
return IntPtr.Zero;
}
}
C:ctestdll.C
#include <stdio.h>
#include "gotestDLL.h"
void Error() {
printf("|-C--->START OF C\n");
RedirectStdError();
printf("|-C--->START ERROR\n");
StartError();
printf("|-C--->END START ERROR\n");
printf("|-C-->END OF C\n");
}
int main() {
}
H:ctestDLL.h
#include <stdio.h>
#include <stdbool.h>
typedef char* CSharpPointerHandler(char* query);
static inline char* RunCSharpPointerHandler(CSharpPointerHandler* handler, char* in) {
return handler(in);
}
#ifdef __cplusplus
extern "C"
{
#endif
__declspec(dllexport) void SetCSharpPointerHandler(CSharpPointerHandler* handler);
__declspec(dllexport) void Error();
#ifdef __cplusplus
}
#endif
GO:gotestDLL.go
package main
/*
#include<stdio.h>
#include <windows.h>
#include <io.h>
#include <fcntl.h>
#include "ctestDLL.h"
*/
import "C"
import "fmt"
import "os"
var queryHandler *C.CSharpPointerHandler = nil
//export SetCSharpPointerHandler
func SetCSharpPointerHandler(handler *C.CSharpPointerHandler) {
queryHandler = handler
}
//export StartError
func StartError() {
inc := C.CString("TEST")
fmt.Println("|-GO--->Calling Handler from GO->C#")
C.RunCSharpPointerHandler(queryHandler, inc)
fmt.Println("|-GO--->END Calling Handler from GO")
}
//export RedirectStdError
func RedirectStdError() {
f, err := os.OpenFile("error.std", os.O_WRONLY | os.O_CREATE | os.O_SYNC, 0777)
if err != nil {
fmt.Println("os.OpenFile")
}
newfd := C._open_osfhandle(C.intptr_t(f.Fd()), C._O_APPEND | C._O_TEXT)
if newfd == C.int(-1) {
fmt.Println("failed to create osfhandle: _open_osfhandle(%v, _O_APPEND|_O_TEXT)", C.intptr_t(f.Fd()))
}
stderr_fd := C._fileno(C.stderr)
forwarded := C._dup2(newfd, stderr_fd)
if forwarded == C.int(-1) {
fmt.Println("failed to duplicate stderr fd: _dup2(%v, %v): %v", newfd, stderr_fd, forwarded)
}
os.Stderr = f
}
func main() {
// Need a main function to make CGO compile package as C shared library
}
OUTPUT:
|-C#->START OF C#
|-C--->START OF C
|-C--->START ERROR
|-GO--->Calling Handler from GO->C#
|-C#->Back to C#
|-C#->ERROR HAPPENS HERE C#
Here i created in DLL project in vc++ 2008. Following are two code files lib.h and lib.cpp.
lib.h
#include "stdafx.h";
class __declspec(dllexport) test
{
public:
test();
static void hello();
static void hello1();
};
class __declspec(dllexport) test1
{
public:
test1();
void hello_test1();
void hello1_test1();
};
lib.cpp
#include "stdafx.h"
#include "lib.h"
#include <stdio.h>
void test::hello()
{
printf("Hello");
}
void test::hello1()
{
printf("Hello1");
}
void test1::hello_test1()
{
printf("Hello_test1");
}
void test1::hello1_test1()
{
printf("Hello1_test1");
}
stdafx.h
#include "targetver.h"
#define WIN32_LEAN_AND_MEAN
// Windows Header Files:
#include <windows.h>
dllMain.cpp
#include "stdafx.h"
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
I have written C# code to call the method of test and test1 classes:
ConsoleApp
[DllImport("lib.dll" )]
public static extern void hello();
[DllImport("lib.dll")]
public static extern void hello1();
[DllImport("lib.dll")]
public static extern void hello_test1();
[DllImport("lib.dll")]
public static extern void hello1_test1();
static void Main()
{
hello();
hello1();
hello_test1();
hello1_test1();
Console.ReadKey();
}
when i run above code i have got following error:
EntryPointNotFoundException: Unable to find an entry point named 'hello' in DLL 'lib.dll'
I know about how to call function only(without using Class) of vc++ DLL from C# but i don't know how to call method of any class and how to code that in proper way in vc++.
I know somewhere is mistake in my above code, please experts guide me about my mistake because i tried all from my side.
If anyone has full example like above then suggest me.
Thanks in advance..
As far as I'm aware there is no way to directly call a native class method from c#. The typical way to get around this is to make non-member functions that call the class methods and interact with those. Please refer to the following link for more specifics. It includes a direct example of what you are trying to do.
using a class defined in a c++ dll in c# code
After reading more of the answers it might be possible but it appears that the link shows the examples you are looking for.
#Jarrett Thanks for moving me towards to Marshal concept.
I added testClass.h and testClass.cpp in this project to marshal C++ classes
testClass.h
#ifndef __testClass_h__
#define __testClass_h__
#include "lib.h"
#ifdef __cplusplus
extern "C" {
#endif
extern LIBDLL_API test* createTestClass();
extern LIBDLL_API void disposeTestClass(test* pObject);
extern LIBDLL_API void callHello(test* pObject);
extern LIBDLL_API void callHelloTest1Class(test1* pObject);
#ifdef _cplusplus
#endif
}
#endif
testClass.cpp
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "stdafx.h"
#include "testClass.h"
extern "C" LIBDLL_API test* createTestClass()
{
return new test();
}
extern "C" LIBDLL_API void disposeTestClass(test* pObject)
{
if(pObject!=NULL)
{
delete pObject;
pObject=NULL;
}
}
extern "C" LIBDLL_API void callHello(test* pObject)
{
if(pObject!=NULL)
{
pObject->hello();
}
}
extern "C" LIBDLL_API void callHelloTest1Class(test1* pObject)
{
if(pObject!=NULL)
{
pObject->hello_test1();
}
}
lib.h
#include "stdafx.h";
#ifdef LIB_EXPORTS
#define LIBDLL_API __declspec(dllexport)
#else
#define LIBDLL_API __declspec(dllimport)
#endif
class LIBDLL_API test
{
public:
test();
virtual ~test();
static void hello();
static void hello1();
};
class LIBDLL_API test1
{
public:
test1();
void hello_test1();
void hello1_test1();
};
lib.cpp
#pragma once
#include "stdafx.h"
#include "lib.h"
#include <stdio.h>
#include <iostream>
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#include <windows.h>
test::test()
{}
test::~test()
{}
void test::hello()
{
printf("\nHello");
}
void test::hello1()
{
printf("Hello1");
}
void test1::hello_test1()
{
printf("\nHello_test1");
}
void test1::hello1_test1()
{
printf("Hello1_test1");
}
And now create a console application and add some code in program.cs file
Program.cs
namespace ConsoleApplication1
{
class ConsoleApplication1
{
static void Main()
{
CS_Test_Class testClass = new CS_Test_Class();
testClass.hello();
testClass.hello_test1();
Console.ReadKey();
testClass.Dispose();
}
}
}
program.cs call the method of CS_Test_Class.cs So....
CS_Test_Class.cs
using System;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
public class CS_Test_Class:IDisposable
{
[DllImport("lib.dll")]
public static extern IntPtr createTestClass();
[DllImport("lib.dll")]
static private extern void disposeTestClass(IntPtr pTestClassObject);
[DllImport("lib.dll")]
static private extern void callHello(IntPtr pTestClassObject);
[DllImport("lib.dll")]
static private extern void callHelloTest1Class(IntPtr pTestClassObject);
private IntPtr nativeObject;
public CS_Test_Class()
{
this.nativeObject = createTestClass();
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool bDisposing)
{
disposeTestClass(this.nativeObject);
this.nativeObject = IntPtr.Zero;
if (bDisposing)
GC.SuppressFinalize(this);
}
~CS_Test_Class()
{
Dispose(false);
}
public void hello()
{
callHello(this.nativeObject);
}
public void hello_test1()
{
callHelloTest1Class(this.nativeObject);
}
}
}
Marshling allows you to acces the class and method of c++ Dll. Microsoft provides facility to Marshal C-functions and also provides the mechanism to Marshal c++ clases.
Enjoy Marshling...
Thank you.
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!
developers!
I have very strange problem. My project has DLL writen in C++ and a GUI writen in C#. And I have implemented callback for some interoperability. I planed that C++ dll will call C# code in some circumstances. It works... but not long and I cant understand why. The problem marked in comment in C# part
Here the complete code of simplified sample:
C++ DLL:
#include <SDKDDKVer.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
extern "C"
{
typedef void (*WriteSymbolCallback) (char Symbol);
WriteSymbolCallback Test;
_declspec(dllexport) void InitializeLib()
{
Test = NULL;
}
_declspec(dllexport) void SetDelegate(WriteSymbolCallback Callback)
{
Test = Callback;
}
_declspec(dllexport) void TestCall(const char* Text,int Length)
{
if(Test != NULL)
{
for(int i=0;i<Length;i++)
{
Test(Text[i]);
}
}
}
};
C# part:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace CallBackClient
{
class Program
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void WriteToConsoleCallback(char Symbol);
[DllImport("CallbackSketch.dll",CharSet=CharSet.Ansi,CallingConvention=CallingConvention.Cdecl)]
private static extern void InitializeLib();
[DllImport("CallbackSketch.dll",CharSet=CharSet.Ansi,CallingConvention=CallingConvention.Cdecl)]
private static extern void SetDelegate(WriteToConsoleCallback Callback);
[DllImport("CallbackSketch.dll",CharSet=CharSet.Ansi,CallingConvention=CallingConvention.Cdecl)]
private static extern void TestCall(string Text,int Length);
private static void PrintSymbol(char Symbol)
{
Console.Write(Symbol.ToString());
}
static void Main(string[] args)
{
InitializeLib();
SetDelegate(new WriteToConsoleCallback(PrintSymbol));
string test = "Hello world!";
for (int i = 0; i < 15000; i++)
{
TestCall(test, test.Length);// It crashes when i == 6860!!!! Debugger told me about System.NullReferenceException
}
}
}
}
The problem is that it crashes in 6860th iteration! I believe that the problem is lack of my knowlege in the subject. Could sombody help me?
SetDelegate(new WriteToConsoleCallback(PrintSymbol));
Yes, this cannot work properly. The native code is storing a function pointer for the delegate object but the garbage collector cannot see this reference. As far as it is concerned, there are no references to the object. And the next collection destroys it. Kaboom.
You have to store a reference to the object yourself. Add a field in the class to store it:
private static WriteToConsoleCallback callback;
static void Main(string[] args)
{
InitializeLib();
callback = new WriteToConsoleCallback(PrintSymbol);
SetDelegate(callback);
// etc...
}
The rule is that the class that stores the object must have a lifetime at least as long as native code's opportunity to make the callback. It must be static in this particular case, that's solid.