C# delegates vs C function pointers - c#

I have a dll written in C, which I need to use
in my C# application.
But I have a problem,I have an interface which uses the structure from below:
typedef struct {
int (*Func1)(void*, int*);
int (*Func2)(void*, int*);
...
} myStructure;
So, I need to translate it to C#:
[StructLayout(LayoutKind.Sequential)]
public unsafe struct myStructure
{
public delegate int Func1(void* l,int* data);
public delegate int Func2(void* l,int* data);
...
}
In C# I need to couple functions of this structure to the actual methods, but I can not do this, because delegate are strong types and I need to make instance of these delegates, so i decided to change this structure a bit in C#,
like this:
[StructLayout(LayoutKind.Sequential)]
public unsafe struct myStructure
{
public delegate int func1(void* l,int* data);
public delegate int func2(void* l,int* data);
...
public func1 Func1;
public func2 Func2;
...
}
It compiles, but application crashes and give this debug info:
receive this error:
Managed Debugging Assistant 'PInvokeStackImbalance'
A call to PInvoke function 'myApp!myApp.Somenamespace::MyInterface' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.
The program '[7976] myApp.exe' has exited with code -1 (0xffffffff).
Some ideas, how can i solve this issue?

Related

Calling C dll from C#, return types slightly different

I've written a dll in C which has functions I can call when referencing the DLL in C#. If I use a basic type like an int it works fine, but I have structs which are slightly different in C# than they are in C due to language differences. Here is an example. This is the function definition in C#:
[DllImport("hello_world_cuda.dll", CharSet = CharSet.Auto)]
public static extern Batch Cut();
And here is it in C:
extern "C" Batch __declspec(dllexport) __stdcall Cut()
You can see the return type Batch is the same, but here is its definition in C#
class Envelope
{
public byte[] Payload;
public byte[] Signature;
}
class Batch
{
public Envelope[] Messages;
public int MsgCount;
}
And here is the definition in C
struct Envelope
{
public:
char* Payload;
char* Signature;
};
struct Batch
{
public:
Envelope* Messages;
int MsgCount;
};
How do I overcome these language differences in order to successfully make the DLL call in C#?
You should define Envelope and Batch as structs in C# too, and apply the StructLaylout attribute:
e.g.:
[StructLayout(LayoutKind.Sequential, Pack=0)]
struct Envelope
{
...
}
Pointers in an unmanaged language do not map to managed arrays as you have done, this is why it is complaining. char is (almost always, with very limited exceptions) an 8 bit value that maps well to byte in C# as you've noticed, but you need to make them pointers in the managed struct as well:
unsafe struct Envelope
{
public byte* Payload;
public byte* Signature;
}

Passing a C# delegate to a C++/CLI wrapper

I was following this example to use a c# delegate as a c++ callback.
My wrapper looks like this:
// GLFWWrapper.h
#pragma once
using namespace System;
namespace GLFWWrapper {
typedef void(__stdcall *KEY_CALLBACK)(int, int, int, int);
public ref class Window {
public:
void setKeyCallback(KEY_CALLBACK fp);
static KEY_CALLBACK csharpKeyCallback;
...
};
}
// GLFWWrapper.cpp
#include "stdafx.h"
#include "GLFWWrapper.h"
namespace GLFWWrapper {
void cppKeyCallback(GLFWwindow * window, int key, int scancode, int action, int mods) {
if (Window::csharpKeyCallback) {
Window::csharpKeyCallback(key, scancode, action, mods);
}
}
void Window::setKeyCallback(KEY_CALLBACK fp) {
csharpKeyCallback = fp;
}
...
}
The cppKeyCallback function is a callback I set in another function (glfwSetKeyCallback(m_ptr, cppKeyCallback);). The csharpKeyCallback is supposed to be the delegate from my C# project. In the example I linked to above it is only explained how I can set a delegate from inside my wrapper. But how can I set the delegate from my C# project? When I try to call setKeyCallback from my C# project I get an error Window.setKeyCallback(?) is not supported by the language.
You seem to be missing the managed part of the instruction:
#pragma managed
public delegate void keyCallback(int, int, int, int);
void Window::setKeyCallback(keyCallback^ fp) {
csharpKeyCallback = fp;
}
In order to pass a reference to a function instance (delegate) from C# to C++\CLI you need to declare it i.e. declare a managed delegate. This can be done either in C# or in C++\CLI.
In your code the KEY_CALLBACK is an unmanaged function declaration. Thus it is "Not supported by the language". C# side cannot use it as a delegate declaration.
In order to avoid such a mess I myself always keep my C++\CLI projects clear of any managed declarations and provide those in an additional C# class library that is referenced from both the C++\CLI wrapper and the C# side.
Edit: Do not forget to put the ^ sign after the managed reference type in the managed method declaration or the compiler is going to bust you with an error 'A delegate type is not allowed here'.

pinvoke giving AccessViolationException when calling C code

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)]

How to call function in c++ which return a class object to c#

C# function calls the exposed function in c++ which in-turn calls class abc's some_fun(int), how should i get the object of abc in C# some_fun enter code here
C++:
class abc
{
public: abc some_fun(int x);
}
this function calls class abc's some_fun(int),
how should i get the object of abc in C# some_fun (marshalled object)
extern "C" SAMPLEDLL_API void __cdecl some_fun(int x);
from c# wrapper: how to i marshal it
public static extern IntPtr some_fun(int abc);
You'll have to "Pinvoke" your c++ function from you C# program.
When you do this, you'll have to declare your function signature in C# terms.
In that declaration, you'll have to give a C# struct represnting the C++ struct you trying to pass.
for example, if you have the C++ struct:
struct FOO
{
public long Dummy;
};
In your C# program, you'll have to define a C# struct
public struct CSFOO
{
public int Dummy;
}
which you'll pass to your PINVOKEd function.
This link can help you:
http://msdn.microsoft.com/en-us/library/aa288468(v=vs.71).aspx

Attempting to read/write protected memory error while pinvoking a c++ function

I have a C++ project X with calling convention set as __stdcall (/Gz) and a C# project Y.
I have defined a class myClass in BOTH these projects.
class myClass
{
private:int mem1;
};
In the C# definition of the class, I have prefixed it with
[StructLayout(LayoutKind.Sequential)]
The C++ function is
_declspec (dllexport) void getLen(myClass* str)
{
printf("%s",sizeof(int));
}
In Y, I have defined the function as follows
[DllImport("X.dll")]
private static extern void getLen(ref myClass str);
And I am calling it like this:
getLen(ref str);
where str is an object of type myClass.
Why is this error coming up when I run this solution?
Your problem is in the printf() call: "%s" expects a pointer to a null-terminated string of characters, but you are providing a size_t.

Categories

Resources