I'm trying to use an dll on my project which have a method that receives a function as parameter, but I don't know how to do that call using C#.
Here is the example that the dll manual provides.
do_pkd(void(*put-byte) (char));
I think it's an example writed in C and I don't know how to perform the same call on C#
Here is the way that I'm trying to do now
public static void Main(string[] args)
{
Console.WriteLine(do_pkd(put_byte));
}
[DllImport(#"C:\pkwin32.dll", CallingConvention = CallingConvention.Cdecl)]
static unsafe extern char do_pkd(Action<char> put_byte);
private static void put_byte(char s)
{
//TO-DO
Console.WriteLine(s);
}
I'm receiving the following error:
Can not perform marshalling of 'parameter # 1': Can not perform marshalling of generic types.
Related
I am trying to pass a string from my c# program to a c++ DLL I created.
Whenever I try to use the string my program throws a System.AccessViolationException: 'Attempted to read or write protected memory. This is often an indication that other memory is corrupt'
I have been able to pass other variable types like int and double from my C# code to my C++ code and use them, but passing string keeps throwing AccessViolationException.
System.Runtime.InteropServices;
Class Program{
[DllImport("myDll", SetLastError = true)]
static extern bool test(string data);
static void Main(string[] args)
{
test("Hello world");
}
extern "C" __declspec(dllexport) void test(string *data)
{
// I get an error whenever I asing *data to s;
string s = *data;
}
I expect the value in data to be assigned to s (in my c++ code) without throwing System.AccessViolationException error at run time
I implemented two functions in Visual C++ DLL project. These functions will be called from C# program.
These are my functions in C++ DLL (not the actual implementation). mytest.cpp project.
extern "C"
{
__declspec(dllexport)void print_DB(null *head)
{
/*print all nodes*/
return;
}
__declspec(dllexport)void* add_node(void *head, int data)
{
/*Add node to data base*/
return (head);
}
}
My C# program is as follows.
namespace example
{
class test
{
[DllImport("mytest.dll", CallingConvention = CallingConvention.Cdecl)]
unsafe public static extern void* add_node(void *head, int data);
unsafe public static extern void print_DB(void *head);
unsafe static void Main(string[] args)
{
/*initilialization*/
head = add_node(head, a)
head = add_node(head, b)
head = add_node(head, c)
printDB(head);
}
}
}
I am able to use on function at a time. ie, If I comment print_DB() deceleration from C# program, add_node() functionality is working. If I commented add_node() function print_DB() function is working. In this way both functions are giving the expected results.
If I am using both functions together, the function which is declared at the end is giving error as below. Invoking or not invoking the functions don't have any effect on the behavior.
Could not load type 'ConsoleApplication2.Program' from assembly
'ConsoleApplication2, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null' because the method 'printDB' has no
implementation (no RVA).
where "ConsoleApplication2.Program" is the name of my C# program.
If I change the order of the functions deceleration, will get same error for the other function.
These are my questions
1)I am new to C# programming. my expectation is this functions should work irrespective of how many functions we declared in the C# program. Is this is a expected behavior?
2)If it not the expected behavior What am I doing wrong?
The DllImport line must be present before every imported function declaration, like so:
namespace example
{
class test
{
[DllImport("mytest.dll", CallingConvention = CallingConvention.Cdecl)]
unsafe public static extern void* add_node(void *head, int data);
[DllImport("mytest.dll", CallingConvention = CallingConvention.Cdecl)]
unsafe public static extern void print_DB(void *head);
unsafe static void Main(string[] args)
{
/*initilialization*/
head = add_node(head, a)
head = add_node(head, b)
head = add_node(head, c)
printDB(head);
}
}
}
I used to register a delegate from C# to a callback function pointer like this (without using Marshal.GetFunctionPointerForDelegate):
C++ (Test.dll)
typedef void (__stdcall* Callback)(int num);
Callback pCallback = 0;
void __stdcall SetCallback(Callback callback)
{
pCallback = callback;
}
void __stdcall ActionCpp()
{
if(pCallback)
{
pCallback();
}
}
C#
class Program
{
[DllImport("Test.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void SetCallBack(Callback callback);
[DllImport("Test.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void ActionCpp();
public delegate void Callback();
static void Action()
{
Console.WriteLine("Callback succeeded.");
}
static void Main(string[] args)
{
Callback myAction = new Callback(Action);
SetCallback(myAction);
ActionCpp(); //Suppose to do the same thing as Action();
}
}
It seems that this way works fine. However, I found that we can do the same thing by using Marshal.GetFunctionPointerForDelegate and register the IntPtr of the delegate to the function pointer in C++.
I would like to know what the difference is and which practice is better? (And also Why? Thanks in advance.)
Here is the C# code when using Marshal.GetFunctionPointerForDelegate. (Nothing changes in C++ code.)
C# (With Marshal.GetFunctionPointerForDelegate)
class Program
{
[DllImport("Test.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void SetCallBack(IntPtr pCallBack); //change to type "IntPtr" this time.
[DllImport("Test.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void ActionCpp();
public delegate void Callback();
static void Action()
{
Console.WriteLine("Callback succeeded.");
}
static void Main(string[] args)
{
Callback myAction = new Callback(Action);
//GCHandle gcHandle = GCHandle.Alloc(myAction); <-- Is this necessary?
IntPtr pMyAction = Marshal.GetFunctionPointerForDelegate(myAction);
SetCallback(pMyAction);
ActionCpp();
//gcHandle.Free();
}
}
I have another further but related question about whether it is necessary to use "GCHandle.Alloc" (as in my comments in the above code) to avoid any GC action as long as my Callback myAction is still alive?
I am a newbie on C# and C++ callbacks, please let me know if I have naive mistakes made.
A callback requires the start address of the method that handles the event. So your two methods are both getting the same start address. The first method is clear since you are using the name of the method. 2nd method you have more statements that accomplishes the same as first method.
c# is managed language while c++ is un-managed. Which basically means that extra protection was added to c# to prevent PC from going blue screen. So when calling un-managed c++ from c# rules must be followed to prevent blue screen. So any pointer variables passed to a c++ method must be allocated in c# as un-managed using GCHandle.Alloc or by using other Marshal methods. Marshal.StrToPtr will also allocate the un-managed memory.
this is my C code
extern "C"
{
__declspec(dllexport) void DisplayHelloFromDLL(string a)
{
printf ("%s\n",a)
}
}
this is my C# code
class HelloWorld
{
[DllImport("TestLib.dll")]
public static extern void DisplayHelloFromDLL(string a);
static void Main ()
{
string a = "Hello";
DisplayHelloFromDLL(a);
}
}
It built successfully but crash like this:
SO,how to use P/invoke to call my own C dll from C#?
Please help,thanx in advance.
First of all your code is C++ rather than C. Your function receives a parameter of type std::string and the use of std::string means that your code is actually C++.
Now this parameter type is the root of your problem. You cannot create a std::string in .net and instead will need to use a char* to pass the string data. The following code is what you need:
C++
__declspec(dllexport) void DisplayHelloFromDLL(char* a)
{
printf("%s\n", a);
}
C#
[DllImport("TestLib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void DisplayHelloFromDLL(string a);
static void Main ()
{
string a = "Hello";
DisplayHelloFromDLL(a);
}
The default p/invoke marshalling for a .net string is to pass a char* as an [In] parameter. There is no need for the complexity of IntPtr, StringToHGlobalAnsi, FreeHGlobal as suggested by one of the other answers. If you can let the p/invoke marshaller do the work then it is preferable to do so.
Note that you also need to make sure that your calling conventions match. Under the assumption that you have not used any special compiler options when building your C++ code, that code will default to used cdecl calling convention. You can make that match with the CallingConvention parameter to the DllImport attribute.
Please take a look at marshalling string at MSDN
In a nut shell, a C# string doesn't get marshalled as std::string but a char* by default
For one thing the return type is not matching. In C it is void and in C# int.
Change your C++ param type to char* and update your C# code as following
class HelloWorld
{
[DllImport("TestLib.dll")]
public static extern void DisplayHelloFromDLL(IntPtr a);
static void Main ()
{
string a = "Hello";
var ptr = System.Runtime.Marshal.StringToHGlobalAnsi(a);
DisplayHelloFromDLL(ptr);
System.Runtime.Marshal.FreeHGlobal(ptr);
}
}
This one is basic, how do I call the function SubscribeNewsFeed in the following from a C# DllImport?
class LogAppender : public L_Append
{
public:
LogAppender()
: outfile("TestLog.txt", std::ios::trunc | std::ios::out)
, feedSubscribed(false)
{
outfile.setf(0, std::ios::floatfield);
outfile.precision(4);
}
void SubscribeNewsFeed()
{
someOtherCalls();
}
};
I'm unable to figure out how to include the class name when using the DllImport in my C# program here:
class Program
{
[DllImport("LogAppender.dll")]
public static extern void SubscribeNewsFeed();
static void Main(string[] args)
{
SubscribeNewsFeed();
}
}
PInvoke cannot be used to call directly into a C++ function in this way. Instead you need to define an extern "C" function which calls the PInvoke function and PInvoke into that function. Additionally you cannot PInvoke into a class instance method.
C / C++ Code
extern "C" void SubscribeNewsFeedHelper() {
LogAppender appender;
appender.SubscribeNewsFeed();
}
C#
[DllImport("LogAppender.dll")]
public static extern void SubscribeNewsFeedHelper();
P/Invoke doesn't work that way. It only can import C functions. So there are different types of interop between the managed (C#) and native (C++) world. Interop via COM would be a solution - providing a C interface another.