There is a library a colleague of mine has made in C and I would like to call it from C#. I think it almost right but its getting an AccessViolationException when I call the 2nd method/function. I've tried a few different things:
class instead of struct
adding [MarshalAs(UnmanagedType.FunctionPtr)] to callbacks/delegates (this actually throws exception on first call instead of second)
Removing [MarshalAs(UnmanagedType.LPStr)] for string in struct (this will then throw exception after second method on closing brace)
The code below for the C# side and the C header I'm using.
C:
#pragma once
#define MAX_FEATURE_LENGTH 30
#define HELPERDLL_API __declspec(dllimport)
struct HelperAttributes {
void(*SaveCallBack) ();
void(*ExitCallBack) ();
char* feature_name;
int attempt_limit;
int check_interval;
};
extern "C"
{
void HELPERDLL_API DoStartUp();
void HELPERDLL_API ReSpecify();
void HELPERDLL_API Initialise(HelperAttributes* attributes);
}
C#:
namespace test
{
public partial class Form1 : Form
{
public delegate void SaveCallBack();
public delegate void ExitCallBack();
public Form1()
{
InitializeComponent();
}
[StructLayout(LayoutKind.Sequential)]
public struct HelperAttributes
{
public SaveCallBack saveCallBack;
public ExitCallBack exitCallBack;
[MarshalAs(UnmanagedType.LPStr)]
public string feature_name;
public int attempt_limit;
public int check_interval;
};
[DllImport("testing.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int DoStartUp();
[DllImport("testing.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int ReSpecify();
[DllImport("testing.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int Initialise(
[In, MarshalAs(UnmanagedType.LPStruct)]
HelperAttributes attributes
);
private void button1_Click(object sender, EventArgs e)
{
HelperAttributes attributes = new HelperAttributes();
attributes.saveCallBack = saveCallBackDelegate;
attributes.exitCallBack = exitCallBackDelegate;
attributes.feature_name = "XXX";
attributes.attempt_limit = 10;
attributes.check_interval = 30;
Initialise(attributes);
DoStartUp();
}
public void saveCallBackDelegate()
{
this.textBox1.Text = "save callback made";
}
public void exitCallBackDelegate()
{
this.textBox1.Text = "exit callback made";
}
}
}
HelperAttributes attributes = new HelperAttributes();
That's very, very troublesome. You have clear memory management problems in this code. The struct you allocate here has a very limited lifetime. It is only valid for the duration of the Click event handler method, nanoseconds at best. This blows up in more than one way:
The C code must not store the passed pointer, it must copy the struct. It probably does not do that. You now have a "dangling pointer", a pretty infamous C bug. Dereferencing the pointer, later, produces arbitrary garbage.
The delegate objects that your code creates and assigns to the saveCallback and exitCallback members of the struct cannot survive. The garbage collector cannot find out that the C code requires them to stay alive. So the next garbage collection destroys the objects, Big Kaboom when the C code makes the callback. Also note that you must declare them with [UnmanagedFunctionPointer] to make them Cdecl if they actually take arguments.
There's more than one way to solve these problems. By far the simplest way is to move the variable outside of the method and declare it static. That keeps it valid for the lifetime of the program. The garbage collector can always see a reference to the delegate objects that way. You however cannot bypass the need to fix the C code and have it make a copy, this struct is not blittable and the C code gets a pointer to a temporary copy that the pinvoke marshaller created. It becomes junk as soon as Initialise() returns.
The functions are all void in the unmanaged side, and declared with int return type on the managed side. That mis-match must be corrected.
Your callback function pointers use the cdecl calling convention. The delegates you pass from the managed code use stdcall calling convention.
Deal with that by marking the delegates like so:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void SaveCallBack();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void ExitCallBack();
The Initialise can be more simply declared as:
public static extern void Initialise(ref HelperAttributes attributes);
Beyond that, we have to guess at the code we cannot see. For instance, does Initialise copy struct it is passed, or does it remember the address of the struct.
It it is the latter, then that is a fatal problem. The solution there is to fix the unmanaged code so that it does not remember addresses, but rather copies content. And even if it copies the struct, does it do a deep copy, or does it copy the pointer to the character array? To get to the bottom of this requires sight of code that is not present in the question.
And even if the function does copy the struct correctly, you will need to keep the delegates alive, as explained by Hans.
In your C dll you need to use dllexport instead of dllimport
#define HELPERDLL_API __declspec(dllexport)
In C# code import functions like that
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
Related
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.
I have an unmanaged DLL that exports only a C style factory method that returns a new instance of a class (simplified here to look simple).
hello.h
#if defined(HWLIBRARY_EXPORT) // inside DLL
# define HWAPI __declspec(dllexport)
#else // outside DLL
# define HWAPI __declspec(dllimport)
#endif
struct HelloWorld{
public:
virtual void sayHello() = 0;
virtual void release() = 0;
};
extern "C" HWAPI HelloWorld* GetHW();
hello.cpp
#include "hello.h"
struct HelloWorldImpl : HelloWorld
{
void sayHello(){
int triv;
std::cout<<"Hello World!";
std::cin>>triv;
};
void release(){
this->HelloWorldImpl::~HelloWorldImpl();
};
HelloWorld* GetHW(){
HelloWorld* ptr = new HelloWorldImpl();
return ptr;
};
Now, I can use dllimport to access GetHW() but is there a way to access the member functions of the returned 'struct'... ie, sayHello and release?
I was also stuck with the same problem. This question was asked a while before. I commented to it for any better solution but didn't get any reply yet. So, reposting it.
When i googled, able to find out two solutions.
Solution1: Expose all the member functions in the C-style for the existing dll. Which i cant do, as it is a 3rd party dll.
Solution2: Write a managed C++ dll exposing the functionality of native C++ dll, which later can be used in your C# dll. Here many classes/functions are present. So, creating would take most of the time.
i got the above solutions from the link below.
How To Marshall
Please let me know if there is any better solution other than the above two solutions?
i have the source code for C++ solution. But what i though was not to touch C++ dll. If there is any possibility to do it in C#, it would be great.
If there is no alternative, i need to follow any one of the specified two solutions.
The C++ code is using the way abstract classes are implemented by the Visual C++ compiler. http://blogs.msdn.com/b/oldnewthing/archive/2004/02/05/68017.aspx. This memory layout is "fixed" because it is used for implementing COM interfaces. The first member of the struct in memory will be a pointer to a vtable containing the function pointers of your methods. So for a
struct HelloWorldImpl : public HelloWorld
{
public:
int value1;
int value2;
}
the "real" layout in memory would be:
struct HelloWorldImpl
{
HelloWorldVtbl *vtbl;
int value1;
int value2;
}
where vtbl would be:
struct HelloWorldVtbl
{
void *sayHello;
void *release;
}
Just for the sake of doing a complete response, I'm writing the example for this signatures:
struct HelloWorld {
public:
virtual int sayHello(int v1, int v2, int v3) = 0;
virtual void release() = 0;
};
C# code:
[DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr GetHW();
[StructLayout(LayoutKind.Sequential)]
struct HelloWorldVtbl
{
public IntPtr sayHello;
public IntPtr release;
}
Your functions are void Func(void) or int Func(int, int, int), but in truth they have a hidden parameter, this, so you can write them as:
int sayHello(HelloWorld*, int, int, int);
void release(HelloWorld*);
so in C# the delegate is
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate int Int32MethodInt32Int32Int32(IntPtr ptr, int v1, int v2, int v3);
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate void VoidMethodVoid(IntPtr ptr);
Then you can use
IntPtr ptr = GetHW();
IntPtr vtbl = Marshal.ReadIntPtr(ptr, 0);
HelloWorldVtblhw = (HelloWorldVtbl)Marshal.PtrToStructure(vtbl, typeof(HelloWorldVtbl));
Int32MethodInt32Int32Int32 sayHello = (Int32MethodInt32Int32Int32)Marshal.GetDelegateForFunctionPointer(hw.sayHello, typeof(Int32MethodInt32Int32Int32));
int res = sayHello(ptr, 1, 2, 3);
Console.WriteLine(res);
VoidMethodVoid release = (VoidMethodVoid)Marshal.GetDelegateForFunctionPointer(hw.release, typeof(VoidMethodVoid));
release(ptr);
I'm having trouble converting a C++ .dll function to C#.
The function is this:
void funct(void*(*handler)(void*));
I think this means passing a pointer to function taking a void pointer and returning a void pointer, as explained here:
Passing Function Pointer.
What I'm trying to do is do the same thing in C#, but I have do idea how. I tried to use delegates, but I am both unsure how to and also if they can even do what I am trying to do.
Thanks for the help!
EDIT:
Here are the C++ functions for register_message_handler and message_handler:
void register_message_handler(void*(*handler)(void*));
void *message_handler(void *raw_message);
EDIT:
xanatos has the exact explanation and conversion to C# below. Thanks a ton xanatos!
void funct(void*(*handler)(void*));
is a function that accepts a pointer and returns a pointer.
In C# it would be:
IntPtr MyFunc(IntPtr ptr);
So you'll need a delegate like:
public IntPtr delegate MessageHandlerDelegate(IntPtr ptr);
[DllImport("mydll.dll")]
public static extern void register_message_handler(MessageHandlerDelegate del);
Note that P/Invoke (calling native methods) is one of the rare cases where Action<...> and Func<...> delegates don't work, and you have to build "specific" delegate.
BUT to call it, it's a little complex, because you must save a "copy" of the delegate so that if the C functions calls this method at any time, this copy is still "alive" and hasn't been GC (see for example https://stackoverflow.com/a/5465074/613130). The most common way to do it is to encapsulate everything in a class, and put the copy in a property/field of the class:
class MyClass
{
public delegate IntPtr MessageHandlerDelegate(IntPtr ptr);
[DllImport("mydll.dll")]
public static extern void register_message_handler(MessageHandlerDelegate del);
[DllImport("mydll.dll")]
public static extern IntPtr message_handler(IntPtr message);
public MessageHandlerDelegate Del { get; set; }
public void Register()
{
// Make a copy of the delegate
Del = Handler;
register_message_handler(Del);
}
public IntPtr Handler(IntPtr ptr)
{
// I don't know what ptr is
Console.WriteLine("Handled");
return IntPtr.Zero; // Return something sensible
}
}
Note that if you use IntPtr then you don't need the unsafe.
If you want to pass message_handler to register_message_handler the safest way is to
// Make a copy of the delegate
Del = message_handler;
register_message_handler(Del);
There is a possibility that you can do directly no there isn't, checked
register_message_handler(message_handler);
and that the CLR will solve this, BUT I'm not sure of this... I can't easily test it, and I wouldn't do it. (if you want to test it, add a GC.Collect() just after the register_message_handler. If after some time you receive a CallbackOnCollectedDelegate error then you know you can't do it :-) )
Mmmh... checked. You can't do the raw register_message_handler(message_handler), you have to use MyClass.
Be very aware of something: it's better to always specify the calling convention C-side and C#-side even in function pointers. C# uses stdcall, while C uses cdecl. In x86 mode you can get very awful silent crashes (in x64 there is a single calling convention)
void __stdcall register_message_handler(void* (__stdcall *handler)(void*));
void * __stdcall message_handler(void *raw_message);
and C# side
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate IntPtr MessageHandlerDelegate(IntPtr ptr);
[DllImport("Win32Project1.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void register_message_handler(MessageHandlerDelegate del);
[DllImport("Win32Project1.dll", CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr message_handler(IntPtr message);
(or everywhere cdecl)
I am calling a method from C# like this:
[DllImport(#"C:\Hash.dll",
CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr ph_dct_videohash(
string file,
ref int length);
static void Main(string[] args)
{
int length = 0;
ph_dct_videohash(#"C:\Users\shady\H.avi", ref length);
Console.Read();
}
And here is the method I am calling from the library
ulong64* ph_dct_videohash(const char *filename, int &Length){
CImgList<uint8_t> *keyframes = ph_getKeyFramesFromVideo(filename);
if (keyframes == NULL)
return NULL;
Length = keyframes->size();
ulong64 *hash = (ulong64*)malloc(sizeof(ulong64)*Length);
//some code to fill the hash array
return hash;}
The question is how can I retrieve the unsigned 64bit Long array in C# and free the memory after using it. It would be even better if it is managed by the garbage collector for me.
I tried Marshal.copy but it didn't work and I am afraid there would be a mem leak (I don't know whether the mem will be freed automatically or not). Any help would be appreciated. Thanks.
You have two ways that you can follow:
Add "free" function to your library and call it explicitly for each resource allocated on library's side. This requires the least amount of code changes on library side, but you have to remember about freeing the memory, unless you'll create some IDisposable wrapper for IntPtr that will do the job automatically. For example, you could use this class:
Change public library's functions to accept pointers to buffers instead of allocating them internally. This potentially requires lots of changes on library side, but simplifies the code on C# side.
EDIT: as a suppliment for the first suggestion, you could use following class:
public class HashDllAutoPtr : IDisposable
{
[DllImport(#"C:\Hash.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void ph_dct_free(IntPtr ptr);
public HashDllAutoPtr(IntPtr ptr)
{
Ptr = ptr;
}
~HashDllAutoPtr()
{
Dispose();
}
public IntPtr Ptr
{
get;
private set;
}
#region IDisposable Members
public void Dispose()
{
if (Ptr != IntPtr.Zero)
{
ph_dct_free(Ptr);
}
Ptr = IntPtr.Zero;
}
#endregion
}
Just create an instance of it for each IntPtr result. You can then either call Dispose manually when you want the memory to be freed, or you can just leave it hanging and it will be picked up by GC eventually (although I don't recommend it). You can read more about disposing unmanaged data here
I'm trying to execute some methods (in this particular case, rdOnAllDone) from a third party DLL, written in C, and looking trough the headers files, I found this:
#ifndef TDECLSDONE
#ifdef STDCALL
#define CCON __stdcall
#else
#define CCON __cdecl
#endif
#define TDECLSDONE
#endif
#define DLLIMP __declspec (dllimport)
DLLIMP int CCON rdOnAllDone (void(CCON *)(int));
After goggling for a way to call this method, I made this:
[DllImport("sb6lib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int rdOnAllDone(Delegate d);
public delegate void rdOnAllDoneCallbackDelegate();
private static void rdOnAllDoneCallback()
{
Console.WriteLine("rdOnAllDoneCallback invoked");
}
The method was called correctly except that I couldn't get the int parameter. So I tried adding the input parameter int like this
[DllImport("sb6lib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int rdOnAllDone(Delegate d);
public delegate void rdOnAllDoneCallbackDelegate(int number);
private static void rdOnAllDoneCallback(int number)
{
Console.WriteLine("rdOnAllDoneCallback invoked " + number);
}
But now delegate is called twice and and it crashes the program with the following error " vshosts32.exe has stopped working"
What's the correct way to call this DLL method?
EDIT: Forgot to add the Main method:
public static void Main()
{
rdOnAllDoneCallbackDelegate del3 = new rdOnAllDoneCallbackDelegate(rdOnAllDoneCallback);
rdOnAllDone(del3);
while (true)
{
Thread.Sleep(1000);
}
}
Three things you need to do to make this work right:
you need to tell the pinvoke marshaller about the actual delegate type, using Delegate isn't good enough. That will create the wrong thunk that won't properly marshal the argument. Which is what you saw happening.
you need to tell the marshaller about the calling convention if it isn't __stdcall with the [UnmanagedFunctionPointer] attribute. Getting this wrong imbalances the stack with good odds for a hard crash.
you need to store a reference to the delegate object so that the garbage collector won't collect it. It cannot see references held by native code. Getting this wrong makes the native code fail with a hard crash after the next garbage collection.
So this ought to work better, tweak as necessary:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void rdOnAllDoneCallbackDelegate(int parameter);
[DllImport("sb6lib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int rdOnAllDone(rdOnAllDoneCallbackDelegate d);
class Foo {
private static rdOnAllDoneCallbackDelegate callback; // Keeps it referenced
public static void SetupCallback() {
callback = new rdOnAllDoneCallbackDelegate(rdOnAllDoneCallback);
rdOnAllDone(callback);
}
private static void rdOnAllDoneCallback(int parameter) {
Console.WriteLine("rdOnAllDoneCallback invoked, parameter={0}", parameter);
}
}
Your delegates signature has to match that of the native callback, also it has to have the UnmanagedFunctionPointerAttribute set appropriately.
In your case like so:
[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
public delegate void rdOnAllDoneCallbackDelegate(int parameter);
[DllImport("sb6lib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int rdOnAllDone(rdOnAllDoneCallbackDelegate callback);
Usage:
{
rdOnAllDone(rdOnAllDoneCallback);
}
private static void rdOnAllDoneCallback(int parameter)
{
Console.WriteLine("rdOnAllDoneCallback invoked, parameter={0}", parameter);
}