How to get refference to the unmanaged (not COM) interface in C#? - c#

I have a native DLL which is implementing some API. The C++ header looks like this:
class CAPIInterface
{
public:
virtual int __stdcall Release()=0;
virtual LPCSTR __stdcall ErrorDescription(const int code)=0;
virtual int __stdcall Login(const int login,LPCSTR password)=0;
}
In C++ a pointer to the interface is acquired this way:
typedef int (*APICreate_t)(int version,CAPIInterface **api);
pfnAPICreate =reinterpret_cast<APICreate_t>(::GetProcAddress(hlib,"APICreate"));
CAPIInterface *api=NULL;
if(pfnAPICreate) (*pfnAPICreate)(version,&api);
The methods of the interface are called like this:
api->Login(123,"password");
Now I need to load this native DLL and use the API in my C# program. I managed to load the DLL and acquire the pointer to the native interface this way:
public static class GlobalMembers
{
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public delegate int APICreate_t(time_t version, out IntPtr api);
}
ptr_pfnAPICreate = NativeMethods.GetProcAddress(hlib,"APICreate");
pfnAPICreate = (GlobalMembers.APICreate_t)Marshal.GetDelegateForFunctionPointer(ptr_pfnAPICreate, typeof(GlobalMembers.APICreate_t));
pfnAPICreate(version, out mptr);
But now I'm not sure how to map this pointer (mptr) to the C# implementation of the interface. Also I'm not sure how to declare the interface CAPIInterface in C# as well. I tried declaring the interface this way:
[StructLayout(LayoutKind.Sequential)]
public class CAPIInterface
{
public delegate int Release();
public delegate string ErrorDescription(int code);
public delegate int Login(int login, string password);
}
But then it doesn't compile... it returns this error:
Error 3 Non-invocable member 'CAPIInterface.Login' cannot be used like a method. I understand that the delegates must be instantiated somewhere as well... but how to do it? Is it correct approach at all to declare the CAPIInterface as above?

I was able to convert my C++ API to C# with the help of SWIG. It works great. Thank you.

Related

How do I load a struct containing function pointers from an external dll?

I have been given an external c++ dll that I need to load and use in my C# project. The dll comes with a header file, which is this (simplified / anonymized):
typedef struct
{
int (*GetVersion)();
int (*StartServer)(const char *ip, int port);
void (*OnRemoteError)(void *caller, int error);
} RemoteServerPluginI;
extern "C" __declspec(dllexport) RemoteServerPluginI* GetServerPluginInterface();
I have a few questions on how to use this in my C# project:
do I translate "void*" with object?
do I translate the char* array to a string or to a char[] ?
OnRemoteError is supposed to be a callback; to register my callback, should I simply assign my callback function to this field?
Any link to the relevant documentation is most appreciated.
I might have figured it out, after a ton of reading and helpful pointers both here on SO and reddit (a special thank you to this comment).
BIG DISCLAIMER: at this time I haven't been able to interface with the actual system, so this might be wrong. However I've successfully loaded the dll and read the version, which makes me think I might have solved it. If anything comes up I will update the answer.
First thing is to declare a struct to map the C++ struct into our C# code.
We can use the MarshalAs attribute to tell the marshaller that these delegates are really just function pointers:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int GetVersionT();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int StartServerT(string ip, int port);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void OnRemoteErrorT(object caller, int error);
public struct RemoteServerPluginI
{
[MarshalAs(UnmanagedType.FunctionPtr)] public GetVersionT GetVersion;
[MarshalAs(UnmanagedType.FunctionPtr)] public StartServerT StartServer;
[MarshalAs(UnmanagedType.FunctionPtr)] public OnRemoteErrorT OnRemoteError;
// a lot of other methods not shown
}
Then we make a helper class that loads the DLL using DLLImport and calls the method that was defined in the dll.
This is easily done using an extern method:
[DllImport("data/remoteplugin.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr GetServerPluginInterface();
Note that we had to specify the calling convention.
Another important thing to note: this method returns an IntPtr object.
Luckily we are done now, we just need to cast this to the correct type:
var ptr = GetServerPluginInterface();
var server = (RemoteServerPluginI)Marshal.PtrToStructure(ptr, typeof(RemoteServerPluginI));
At this point I just wrapped everything into a convenience class to manage access, and voilĂ !
Here is the final code:
public static class IntPtrExtensions
{
// I'm lazy
public static T ToStruct<T>(this IntPtr ptr)
=> (T)Marshal.PtrToStructure(ptr, typeof(T));
}
public static class RemoteControlPlugin
{
[DllImport("path/to/remoteplugin.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr GetServerPluginInterface();
private static RemoteServerPluginI? _instance = null;
public static RemoteServerPluginI Instance =>
(RemoteServerPluginI)(
_instance ??
(_instance = GetServerPluginInterface().ToStruct<RemoteServerPluginI>())
);
}
internal static class Program
{
private static void Main(string[] args)
{
var remoteServer = RemoteControlPlugin.Instance;
Console.WriteLine(remoteServer.GetVersion()); // Easy!
}
}

C++/CLI Implementing a C# Interface

I have a C# interface which looks like this:
public interface ITdcConnector
{
void Close(uint);
void FetchRequestAsync(ManagedFetchRequest);
UInt32 Open(String, Action<uint, ManagedFetchResponse>, out Int64);
}
I am trying to implement in C++/CLI like this:
public ref class MockTdcConnector : public ITdcConnector
{
public:
virtual Void Close(UInt32);
Void FetchRequestAsync(ManagedFetchRequest);
UInt32 Open(String, Action<UInt32, ManagedFetchResponse^>^,
[System::Runtime::InteropServices::Out] Int64%);
};
IntelliSense is giving me grief on the Open() method. It tells me: IntelliSense: class fails to implement interface member function "ITdcConnector::Open"
I've looked at a few relevant examples on implementing C# classes in C++/CLI, but no luck. Any idea on how to get the C++/cli method signature to look like the C# method?
So, I didn't see this until just now. I started typing the name of the C# method in the C++/cli class and IntelliSence showed the method signature it was expecting. I just needed some more ^s and virtuals.
Here is what I ended up using, for future reference:
public ref class MockTdcConnector : public ITdcConnector
{
public:
virtual Void Close(UInt32);
virtual Void FetchRequestAsync(ManagedFetchRequest^);
virtual UInt32 Open(String^, Action<UInt32, ManagedFetchResponse^>^
[Runtime::InteropServices::Out] Int64%);
};

Pass string from unmanaged code to managed

I have a problems with passing string from unmanaged code to managed.
In my unmanaged class (unmanagedClass.cpp) I have a pointer to function from managed code:
TESTCALLBACK_FUNCTION testCbFunc;
TESTCALLBACK_FUNCTION takes one string and returns nothing:
typedef void (*TESTCALLBACK_FUNCTION )(char* msg);
Unmanaged class inherites from ITest interface which has only one method:
STDMETHOD(put_TestCallBack) (THIS_
LPVOID FnAddress
) PURE;
In managedClass.cs I write this code:
public class ManagedClass
{
ITest unmanaged = new unmanagedClass();
public delegate void TestDelegate(string info);
ManagedClass()
{
unmanaged.put_TestCallBack(new TestDelegate(this.Test));
}
void Test(string info)
{
MessageBox.Show(info);
}
}
[ComImport, Guid("<my guid here>")]
public class unmanagedClass
{
}
[ComImport, System.Security.SuppressUnmanagedCodeSecurity,
Guid("<my guid here>"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ITest
{
[PreserveSig]
int put_TestCallBack([MarshalAs(UnmanagedType.FunctionPtr), In] Capture.TestDelegate func);
}
To call Test func from unmanaged code I use this
(*testCbFunc)("Func Uragan33::Execute has been started!");
But when Test method from managedClass.cs is called I always received null string.
Why does it happen?
Thank in advance!
You have a mismatch on the calling convention. The typedef in your C++ code declares a function pointer with the default calling convention, which is __cdecl. But the default for a delegate in managed code is __stdcall.
You will need an attribute to tell the pinvoke marshaller otherwise. Make that look like this:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void TestDelegate(string info);
Drop the [MarshalAs] in the function declaration. Fixing the typedef in your C++ code might be preferable, if you can, clearly making everything consistent is the preferred solution:
typedef void (__stdcall * TESTCALLBACK_FUNCTION )(char* msg);
Unrelated, this a bug you'll need to fix:
unmanaged.put_TestCallBack(new TestDelegate(this.Test));
The delegate object you create is not visible to the garbage collector. If will be collected on the next GC, your code will crash when the native code makes the callback. You have to store the delegate object somewhere so the GC always sees a reference. Either as a field in the class, with the additional requirement that the class object needs to stay alive long enough, or in a static variable.
Note how all of these problems disappear when you declare a callback interface instead of a delegate. The COM way.

Importation: Syntax translation for using C++ library under C#

Currently I'm trying to use a C++ library under C# using DLL importation. Library is called Interception.
The problem is that I don't know how to translate #define entries and typedef declaration of the header file:
https://github.com/oblitum/Interception/blob/master/include/interception.h
I tried to use "using" directive, but with no success (I can't access to the void definition).
Moreover, I didn't understood the role of __declspec(dllimport) in this header. In my c# project, I just ignored it? Is it good to do that?
This is the code I want to use in c# (it's a sample of the library)
https://github.com/oblitum/Interception/blob/master/samples/hardwareid/main.cpp
EDIT:
What I've tried: basic importation:
[DllImport("interception.dll", CharSet = CharSet.Auto, SetLastError = true)]
void interception_set_filter(void* context, InterceptionPredicate predicate, ushort filter);
I don't know ho to convert InterceptionPredicate. According the header file, InterceptionFilter is a ushort, and InterceptionContext is a void pointer (void*).
The C++ library should be compiled as a .DLL file. This .DLL file should have exported functions. You can use the Depends tool to check what's exported from a .DLL. .NET code can call C++ exported functions using what's called "Platform Invoke".
Now, I strongly suggest you take a deep look at this Platform Invoke Tutorial that will guide you.
PS: void * should be declared in c# as IntPtr. enums should be redeclared as enums. Functions should be declared as static extern methods marked with the DllImport attribute.
First, it looks like you're trying to implement a global keyboard/mouse hook .. if that's the case, I'd recommend googling 'C# low level keyboard and mouse hook'.
Now for your question, first is the __declspec(dllimport) issue: this would be if you were actually using the header in a C++ application, that is the C++ equivilent of the C# DllImport .. so in effect you didn't ignore it, you implemented it. In C++ it just tells the linker that the function declared as such will be imported from a specific DLL instead of it being a local function (pretty similar to what the C# DllImport directive does)
Next is for function pointer issue (InterceptionPredicate). In the header it is defined as such:
typedef int (*InterceptionPredicate)(InterceptionDevice device);
And InterceptionDevice is just an 'int'. So the InterceptionPredicate is just a function pointer type (or Delegate in C#), so your delegate definition for InterceptionPredicate would look like this:
// [UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate int InterceptionPredicate (int device);
A note about the UnmanagedFunctionPointer calling convention descriptor: IF you know what kind of calling convention (stdcall, fastcall, cdecl) the exported function might be using, you could specify here so that the .NET marshaler will know how to pass the data between the managed/unmanaged code, but if you don't know it or it's not specified typically you can just leave that off.
Also, as others have mentioned, unless you have the 'unsafe' flag specified in your C# properties, a void* type should always be an IntPtr in C#.
Also, be sure to mark the dll function in your C# code as public static extern, see example below.
So to make an example of the function you've specified, here's what could be done:
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace InterceptorTest
{
public class Interceptor : IDisposable
{
#region DllImports
[DllImport("interception.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr interception_create_context();
[DllImport("interception.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern void interception_destroy_context(IntPtr context);
[DllImport("interception.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern void interception_set_filter(IntPtr context, InterceptionPredicate predicate, ushort filter);
// The function pointer type as defined in interception.h that needs to be defined as a delegate here
public delegate int InterceptionPredicate(int device);
#endregion
#region private members
private InterceptionPredicate m_PredicateDelegate { get; set; }
private IntPtr m_Context { get; set; }
#endregion
#region methods
public Interceptor(ushort filter)
{
// be sure to initialize the context
this.m_PredicateDelegate = new InterceptionPredicate(this.DoSomethingWithInterceptionPredicate);
this.m_Context = interception_create_context();
interception_set_filter(this.m_Context, this.m_PredicateDelegate, filter);
}
private void Cleanup()
{
interception_destroy_context(this.m_Context);
// the next line is not really needed but since we are dealing with
// managed to unmanaged code it's typically best to set to 0
this.m_Context = IntPtr.Zero;
}
public void Dispose()
{
this.Cleanup();
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing) { this.Cleanup(); }
}
public int DoSomethingWithInterceptionPredicate(int device)
{
// this function is something you would define that would do something with
// the device code (or whatever other paramaters your 'DllImport' function might have
// and return whatever interception_set_filter is expecting
return device;
}
#endregion
}
static class Program
{
[STAThread]
private static void Main(string[] argv)
{
Interceptor icp = new Interceptor(10);
// do something with the Interceptor object
}
}
}
Hope that gets you on the right track.
Ok, I found an example code that was hidden on the GIT. Thank you google!
https://gist.github.com/1959219
It precises all function from DLL importation, with a working example.

How do I DllExport a C++ Class for use in a C# Application

I have created a C++ Dll project which contains a class "myCppClass" and tried to Dll export it using the following code as described by:
http://msdn.microsoft.com/en-us/library/a90k134d(v=vs.80).aspx
class __declspec(dllexport) CExampleExport : //public CObject
{ ... class definition ... };
I have omitted the "public CObject" as that requires afx.h and implies it is an MFC Dll. I am not sure if this is a good thing or not but it differed from the DLL project default settings.
From the above linked documentation I am led to believe that all "public functions and member variables" are available for import. How do I accomplish this in C#? Can simply instantiate the class?
Edit: I just realized that the Title of the post may be misleading. The emphasis should be on DllImport-ing from C# and ensuring that I followed the documentation properly in C++
C# cannot directly import C++ classes (which are effectively name-mangled C interfaces).
Your options are exposing the class via COM, creating a managed wrapper using C++/CLI or exposing a C-style interface. I would recommend the managed wrapper, since this is easiest and will give the best type safety.
A C-style interface would look something like this (warning: untested code):
extern "C" __declspec(dllexport)
void* CExampleExport_New(int param1, double param2)
{
return new CExampleExport(param1, param2);
}
extern "C" __declspec(dllexport)
int CExampleExport_ReadValue(void* this, int param)
{
return ((CExampleExport*)this)->ReadValue(param)
}
A C++/CLI-style wrapper would look like this (warning: untested code):
ref class ExampleExport
{
private:
CExampleExport* impl;
public:
ExampleExport(int param1, double param2)
{
impl = new CExampleExport(param1, param2);
}
int ReadValue(int param)
{
return impl->ReadValue(param);
}
~ExampleExport()
{
delete impl;
}
};
As far as I know, C# can only interop with COM interfaces. Lucky enough it doesn't need to be a full blown COM object with registry, it can be any plain old C++ class implementing IUnknown.
So do something like this in C++:
#include <Windows.h>
// Generate with from VisualStudio Tools/Create Guid menu
static const GUID IID_MyInterface =
{ 0xefbf7d84, 0x3efe, 0x41e0, { 0x95, 0x2e, 0x68, 0xa4, 0x4a, 0x3e, 0x72, 0xca } };
struct MyInterface: public IUnknown
{
// add your own functions here
// they should be virtual and __stdcall
STDMETHOD_(double, GetValue)() = 0;
STDMETHOD(ThrowError)() = 0;
};
class MyClass: public MyInterface
{
volatile long refcount_;
public:
MyClass(): refcount_(1) { }
STDMETHODIMP QueryInterface(REFIID guid, void **pObj) {
if(pObj == NULL) {
return E_POINTER;
} else if(guid == IID_IUnknown) {
*pObj = this;
AddRef();
return S_OK;
} else if(guid == IID_MyInterface) {
*pObj = this;
AddRef();
return S_OK;
} else {
// always set [out] parameter
*pObj = NULL;
return E_NOINTERFACE;
}
}
STDMETHODIMP_(ULONG) AddRef() {
return InterlockedIncrement(&refcount_);
}
STDMETHODIMP_(ULONG) Release() {
ULONG result = InterlockedDecrement(&refcount_);
if(result == 0) delete this;
return result;
}
STDMETHODIMP_(DOUBLE) GetValue() {
return 42.0;
}
STDMETHODIMP ThrowError() {
return E_FAIL;
}
};
extern "C" __declspec(dllexport) LPUNKNOWN WINAPI CreateInstance()
{
return new MyClass();
}
And on the C# side you do something like this:
[ComImport]
[Guid("EFBF7D84-3EFE-41E0-952E-68A44A3E72CA")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface MyInterface
{
[PreserveSig] double GetValue();
void ThrowError();
}
class Program
{
[DllImport("mylib.dll")]
static extern MyInterface CreateInstance();
static void Main(string[] args)
{
MyInterface iface = CreateInstance();
Console.WriteLine(iface.GetValue());
try { iface.ThrowError(); }
catch(Exception ex) { Console.WriteLine(ex); }
Console.ReadKey(true);
}
}
You can do pretty much anything you want this way, as long as the communication between C++ and C# goes through the virtual interface.
You cannot create a C++ class instance through pinvoke from C#. This is a troublesome implementation detail, only the C++ compiler knows how much memory needs to be allocated and when and how to properly call the constructor and destructor. The object size is by far the hardest nut to crack, there is no way whatsoever to make that reliable.
If you cannot flatten the C++ class out into static methods then you need to write a managed wrapper. That's done with the C++/CLI language, you'd write a "ref class" that has the unmanaged class object stored as a pointer, created in the constructor and deleted in the destructor and finalizer.
Actually, you can refer to the mangled names directly, using the EntryPoint property of the DllImport attribute. See this answer for more details.
C# and C++ are NOT ABI compatible like C++ and Delphi, so you cannot export virtual class members (methods) and declare them purely virtual on the calling side an invoke them, because C# cannot handle vtbl's of C++ objects.
I would suggest you to wrap your C++ classes by COM, so you have another positive side effect, that other COM compatible languages can use your classes too.

Categories