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);
Related
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;
}
let say we have following code in a dll written in C-lang, where i try to map some functions defined in the dll as functionpointers, map to their actual functions, i follow following link to get till here
https://learn.microsoft.com/en-us/dotnet/framework/interop/marshaling-different-types-of-arrays
Dlltest.h
typedef struct VersionInfo
{
UINT uMajor;
UINT uMinor;
UINT uMRevision;
} STRUCT_VERSION_INFO;
typedef struct DllTestFPStruct
{
int(*Close) (void);
int(*Init) (STRUCT_VERSION_INFO *sVersInfo);
int(*RegisterClient) (int id);
} STRUCT_DLL_TEST_FP_STRUCT;
typedef struct DllTestMgr
{
STRUCT_DLL_TEST_FP_STRUCT *psItf;
int iDummy;
} STRUCT_DLL_TEST_MGR;
extern "C"
{ __declspec(dllexport) void GetDllTestFP(STRUCT_DLL_TEST_MGR *psFP); }
Dlltest.c
static int Close(void);
static int Init(STRUCT_VERSION_INFO *sVersInfo);
static int RegisterClient(int id);
STRUCT_DLL_TEST_FP_STRUCT sFP =
{
&Close,
&Init,
&RegisterClient,
};
DLLTESTC_API void GetDllTestFP(STRUCT_DLL_TEST_MGR *psFP)
{ psFP->psItf = &sFP; }
static int Close(void)
{ printf("Closed called.\n"); }
static int Init(STRUCT_VERSION_INFO *sVersInfo)
{ printf("Init called.\n");}
static int RegisterClient(STRUCT_VERSION_INFO *sVersInfo)
{ printf("RegisterClient called.\n");}
Now i want to write a c# application which uses this DLL, specially it should make use of the "GetDllTestFP"-Function which maps the functionpointers to their actuall function. right now my C#-Application is as follow:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int FP_DLL_TEST_CLOSE(ref VersionInfo sVersInfo);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int FP_DLL_TEST_INIT(ref VersionInfo sVersInfo);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int FP_DLL_TEST_RC(ref VersionInfo sVersInfo);
[StructLayout(LayoutKind.Sequential)]
public struct DllTestFPStruct
{
public FP_DLL_TEST_CLOSE Close;
public FP_DLL_TEST_INIT Init;
public FP_DLL_TEST_RC RegisterClient;
}
[StructLayout(LayoutKind.Sequential)]
public struct DllTestMgr
{
public IntPtr psItf;
public int iDummy;
}
[DllImport("DllTestC.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern void GetDllTestFP(ref DllTestMgr ps);
static void Main(string[] args)
{
VersionInfo vInfo = new VersionInfo();
DllTestFPStruct dllFPs = new DllTestFPStruct();
DllTestMgr dllTest = new DllTestMgr();
IntPtr buffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(dllFPs));
Marshal.StructureToPtr(dllFPs, buffer, false);
dllTest.psItf = buffer;
GetDllTestFP(ref dllTest); // Funtionpointers are not mapped, still null
dllFPs.Close(ref vInfo);
}
The problem is, that the functions do not get mapped to their actuall functions in the dll.
Any idea how can i achieve my goal?
Thanks
The C# code is:
DllTestMgr dllTest = new DllTestMgr();
GetDllTestFP(ref dllTest);
DllTestFPStruct dllFPs = (DllTestFPStruct)Marshal.PtrToStructure(dllTest.psItf, typeof(DllTestFPStruct));
VersionInfo vInfo = new VersionInfo();
dllFPs.Close(ref vInfo);
You don't need to allocate dllTest.psItf becase in GetDllTestFP you do:
DLLTESTC_API void GetDllTestFP(STRUCT_DLL_TEST_MGR *psFP)
{
psFP->psItf = &sFP;
}
So you copy the address of sFP.
Note that in general this is a bad idea, because you are giving to the "client" direct access to your data (the sFP struct). The alternative is that the client passes the memory (as you wrote before) and then you do:
(*psFP->psItf) = sFP;
(but then remember to free the allocated memory!)
Third alternative, the C-side allocates a block of memory through a shared allocator (one that can be used by C#, so no malloc/new here) and then the C# has to deallocate it.
wrong solution is
STRUCT_DLL_TEST_FP_STRUCT sFP2 = sFP;
psFP->psItf = &sFP2;
The lifetime of sFP2 ends when the method returns. psFP->psItf is now pointing to a piece of stack that doesn't exist anymore. don't do it!
Ah... as written by #Hans Passant, depending on who allocates the memory, the GetDllTestFP can be ref or out. If the memory is allocated by C# then it must be ref, if it isn't allocated (as is now) or is allocated by C++, then out is ok and you'll save on the marshaling in one direction.
Some time ago I wrote a Windows DLL in C++ to encapsulate some functionality that I wanted to share between different C++ and Delphi compilers. I always liked the way that COM allows me to work with objects in contrast to having a flat C API in a DLL. Since for this DLL I did not need the full power of COM and no reference counting, I decided to implement a DLL interface that somewhat mimics COM. I have a single exported C function that allows me to create an object and returns an vtable interface pointer. Via this pointer I can call methods on the object in a compiler neutral way in C++ and Delphi. When I'm done using the interface pointer, I call release on it.
Now I would like to use this DLL from C# but I don't know how to import the interface. Moreover I have a callback interface that the C# client must implement. Following is the C++ interface header file (somewhat shortended).
struct CksReadParams
{
...
};
struct ICksAdapter
{
virtual ~ICksAdapter();
virtual int32_t __cdecl Release() = 0;
virtual int32_t __cdecl ReadValues(CksReadParams* params) = 0;
};
struct ICksAdapterCallback
{
virtual ~ICksAdapterCallback();
virtual void __cdecl OnConnected(int32_t clientID) = 0;
virtual void __cdecl OnData(int32_t clientID, const char* csvLine) = 0;
virtual void __cdecl OnLog(int32_t clientID, const char* message) = 0;
virtual void __cdecl OnError(int32_t clientID, int32_t code, const char* message) = 0;
};
extern "C"
{
typedef int(__cdecl *CA_PCreateAdapter)(ICksAdapterCallback* callback, ICksAdapter** adapter);
__declspec(dllexport) int32_t __cdecl CA_CreateAdapter(ICksAdapterCallback* callback, ICksAdapter** adapter);
}
Can you give me a sketch on how to use this from C#, especially how to implement the callback interface, how to handle the __cdecl calling convention and how to handle char* parameters in the callback which are actually UTF8 encoded C++ strings?
I wrote the following code that seems to do what I need. It was mainly inspired by this blog post Implementing an unmanaged C++ interface callback. The code builds mainly on Marshal.GetDelegateForFunctionPointer and Marshal.GetFunctionPointerForDelegate which are available starting with .NET 4.5 which is acceptable in my case. To sum up, you need to know the vtable of the C++ object and you need to fake a vtable in unmanaged memory for the callback interface.
If it's worth the effort I will leave to the reader. In my case it at least allows me to reuse my DLL. Next time I will probably stick with a flat C API or use COM in the first place.
class CksAdapterCallback
{
private IntPtr instance;
public CksAdapterCallback()
{
instance = Marshal.AllocHGlobal(IntPtr.Size * 6);
IntPtr vtblPtr = IntPtr.Add(instance, IntPtr.Size);
Marshal.WriteIntPtr(instance, vtblPtr);
Marshal.WriteIntPtr(vtblPtr, IntPtr.Zero); //dummy entry for the destructor
OnConnectedInternal = new OnConnectedDelegate(OnConnected);
Marshal.WriteIntPtr(IntPtr.Add(vtblPtr, IntPtr.Size), Marshal.GetFunctionPointerForDelegate(OnConnectedInternal));
OnDataInternal = new OnDataDelegate(OnData);
Marshal.WriteIntPtr(IntPtr.Add(vtblPtr, 2*IntPtr.Size), Marshal.GetFunctionPointerForDelegate(OnDataInternal));
...
}
~CksAdapterCallback()
{
Marshal.FreeHGlobal(instance);
}
public IntPtr Instance
{
get { return instance; }
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void OnConnectedDelegate(IntPtr instance, Int32 clientID);
OnConnectedDelegate OnConnectedInternal;
void OnConnected(IntPtr instance, Int32 clientID)
{
...
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void OnDataDelegate(IntPtr instance, Int32 clientID, IntPtr data);
OnDataDelegate OnDataInternal;
void OnData(IntPtr instance, Int32 clientID, IntPtr data)
{
...
}
...
}
class CksAdapterProxy
{
private IntPtr instance;
public CksAdapterProxy(IntPtr instance)
{
this.instance = instance;
IntPtr vtblPtr = Marshal.ReadIntPtr(instance, 0);
IntPtr funcPtr = Marshal.ReadIntPtr(vtblPtr, 1 * IntPtr.Size);
ReleaseInternal = Marshal.GetDelegateForFunctionPointer<ReleaseDelegate>(funcPtr);
...
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate Int32 ReleaseDelegate(IntPtr instance);
ReleaseDelegate ReleaseInternal;
...
public Int32 Release()
{
return ReleaseInternal(instance);
}
...
}
public class CksAdapterTest
{
[DllImport("CKS\\CksAdapter.dll")]
[return: MarshalAs(UnmanagedType.I4)]
static extern int CA_CreateAdapter(IntPtr adapterCallback, out IntPtr adapter);
CksAdapterProxy adapter = null;
CksAdapterCallback adapterCallback = new CksAdapterCallback();
public CksAdapterTest()
{
IntPtr nativeAdapter = IntPtr.Zero;
int result = CA_CreateAdapter(adapterCallback.Instance, out nativeAdapter);
if(result == 0)
{
adapter = new CksAdapterProxy(nativeAdapter);
...
adapter.Release();
}
}
}
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)]
below is the c++ dll class
class A
{
public:
int __thiscall check(char *x,char *y,char *z);
private:
B *temp;
};
class B
{
friend class A;
Public:
B();
B(string x,string y,string z);
~B();
private:
string x;
string y;
string z;
};
c++ dll method definition is below
__declspec(dllexport) int __thiscall A::check(char *x,char *y,char *z)
{
temp=new B(x,y,z); //getting error at this point when i am assigning memory to temp
return 1;
}
c# dll import is like this
[DllImport("MyDll.dll", CallingConvention = CallingConvention.ThisCall, ExactSpelling = true, EntryPoint = "check")]
public static extern int check(IntPtr val,string x,string y,string z);
c++ dll build works fine but when c# calls the c++ dll method also it looks good and when it enters the function and in the first line of the method it try's to create memory for temp pointer which has been declared in class A as pointer of class B which is private. the error that it is giving is
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
The __declspec(dllexport) should be on the class (e.g. class __declspec(dllexport) MyClass), not on its member methods.
The entry point should be a mangled C++ name (e.g. 2#MyClass#MyMethod?zii), not "check".
You can use Depends.exe to find the name.
I found the issue and the issue is with the check function in c++. The temp is supposed to be created like this.
int __thiscall A::check(char *x,char *y,char *z)
{
A *xyz=new A();
A->temp=new B(x,y,z); // doing this eliminates the issue.
return 1;
}
Thanks to all who helped me out on this.