C++/CLI Implementing a C# Interface - c#

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%);
};

Related

Passing unmanaged method as callback to managed C++/CLI class

I want to pass as callback a C++ member function to a C# project. I have other project in C++/CLI and I want to do it through it.
So, in unmanaged C++ of my C++/CLI project I have a function object:std::function<void(int)>callback;
This function is coming from my C++ project and it works fine, I save it there as example to avoid the previous step. Now, I would like to pass this callback function to my C# project. For this, I create a method in unmanaged C++, pass it to managed C++ and from this point pass it finally to C#. I'd like something like this:
// Unmanaged class
public class Wrapper
{
public:
std::function<void(int)>callback;
void ReturnToCallback(int data)
{
callback(data);
}
void PassCallback()
{
NETWrapper netWrapper;
netWrapper.TestCallback(ReturnToCallback);
}
};
//Managed class
public ref class NETWrapper
{
public:
void TestCallback(Action<int>^ callback)
{
StartGenerator^ startGen = gcnew StartGenerator(callback);
}
};
// C#
public class StartGenerator
{
private Communication comm;
public StartGenerator(Action<int> callback)
{
comm = Communication.Instance;
comm.callback = callback;
}
}
This solution, of course, gives me back an error when compiling:
Error 3 error C3867: 'IfaceXXX::Wrapper::ReturnToCallback': function call missing argument list; use '&IfaceXXX::Wrapper::ReturnToCallback' to create a pointer to member d:\XXX.h
I have tried other ways such as Get the delegate for the function pointer so I can work on Managed C++ and pass it to C# but I am not able to implement it right. What do you think is the best way to try this?
Make Wrapper::callback a pointer to the std::function.
Change Wrapper to a ref class.
That's it.
public ref class Wrapper
{
public:
std::function<void(int)>* callback;
void ReturnToCallback(int data)
{
(*callback)(data);
}
void PassCallback()
{
NETWrapper netWrapper;
netWrapper.TestCallback(gcnew Action<int>(this, &Wrapper::ReturnToCallback));
}
};
You do then need to manage the lifetime of the std::function now, perhaps my clr_scoped_ptr could show you how to do that.

Send status update to C# from C++ using delegate?

I have a C++ function which performs a number of tasks "PerformJob()". I have a C# wrapper which calls PerformJob(). The job takes a while and I would like the C++ method to send "status updates" back up to the calling C# class. The C++ code is not exposed to the C# class. Adding the C# project as a reference would cause a circular dependency.
I've attempted to pass a delegate through as a parameter but I'm not familiar enough with C++ syntax to make this work (or if it is even possible?). Is there an appropriate way to pass a delegate into C++ as a parameter? Is there a better method to facilitate this communication? I'd like to avoid a dllimport, as I only need to receive updates from this one class.
CSharpClass.cs:
public delegate void CallbackDelegate(ref string status);
public CallbackDelegate jobStatusDelegate;
public void UpdateJobStatus(ref string status)
{
Job.JobStatus = status;
}
public void StartJob()
{
jobStatusDelegate = new CallbackDelegate(UpdateJobStatus);
CPlusClass jobHelper = new CPlusClass();
jobHelper.PerformJob(jobStatusDelegate);
}
CPlusClass.h:
public ref class CPlusClass
{
public:
void PerformJob(delegate del); // is there c++ delegate type?
};
CPlusClass.cpp:
void CPlusClass::PerformJob(delegate del)
{
// ....
}
C++ doesn't have delegates, but C++/CLI does, and based on ref class your code already is C++/CLI.
Delegates work much like any other reference type, they get stored as a tracking handle with the ^.
In order to not create a dependency on the C#, which would be in the wrong direction, I suggest you use a System::Action<System::String^>^ instead of defining your own delegate type.
public ref class CPlusClass
{
public:
void PerformJob(System::Action<System::String^>^ del)
{
del->Invoke(gcnew String("Hello World"));
}
};

How to convert IntPtr to SafeHandle?

I have C# Windows service class:
class MyService : ServiceBase
{
private void InitializeComponent() {
//some other code ...
SafeHandle sHandle = this.ServiceHandle; // I want to do this but this fails.
SetServiceObjectSecurity(sHandle, secInfo, binaryDescriptor);
//some more code ...
}
}
How to convert an IntPtr (like this.ServiceHandle) to a "System.Runtime.InteropServices.SafeHandle"? so that I can use that in the function call "SetServiceObjectSecurity()"?
My ultimate aim is to give admin permission to the Service.
Have you tried using SafeHandle.SetHandle(IntPtr handle) as explained here https://msdn.microsoft.com/de-de/library/system.runtime.interopservices.safehandle.sethandle(v=vs.110).aspx and see if that works for you?
EDIT:
Since SafeHandle.SetHandle(IntPtr handle) is a protected method, you could create a derived class like this:
public class SafeHandleExtended : SafeHandle
{
public void SetHandleExtended(IntPtr handle)
{
this.SetHandle(handle);
}
// .. SafeHandle's abstract methods etc. that you need to implement
}
Although I have not tested this for correctness, so I do not know if this works the way you want it to.

How to get refference to the unmanaged (not COM) interface in 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.

C# to VB6 COM events ("object or class does not support the set of events")

Really pulling my hair out with this one...
I have a C# project with an interface defined as:
/* Externally Accessible API */
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface ISerial
{
[DispId(1)]
bool Startup();
[DispId(2)]
bool Shutdown();
[DispId(3)]
bool UserInput_FloorButton(int floor_number);
[DispId(4)]
bool Initialize();
}
/* Externally Accesssible Event API */
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface ISerialEvent
{
[DispId(5)]
void DataEvent();
}
[ComSourceInterfaces(typeof(ISerialEvent), typeof(ISerial))]
[ClassInterface(ClassInterfaceType.None)]
public class SerialIface : ISerial
{
public delegate void DataEvent();
public event DataEvent dEvent;
public bool Initialize()
{
//testing the event callback
if (dEvent != null)
{
dEvent();
}
}
...
}
And the VB6 code looks like:
Private WithEvents objSerial As SerialIface
Private Sub objSerial_DataEvent()
'do something happy'
End Sub
Public Sub Class_Initialize()
Set objSerial = New SerialIface '<---this is the line that fails'
Call objSerial.Initialize '<--Initialize would trigger DataEvent, if it got this far'
End Sub
Well, the normal API-type functions appear to be working (if I declare objSerial without the WithEvents keyword), but I can't for the life of me get the "DataEvent" to work. It fails with the "object or class does not support the set of events" message.
I'd originally lumped the two interfaces together, but then C# complained that DataEvent was not defined in the class. The way it is currently, I am able to view all of the APIs and the one event perfectly in the VB6 object browser -- everything looks like it's there... I just can't make it actually work!
I'm sure I'm missing something obvious or doing something stupid -- but I'm new to the whole interop business, so it's just escaping me entirely.
Help!
Look at this article here.
Specifically it looks like you missing a declaration that looks something like this.
[Guid("9E5E5FB2-219D-4ee7-AB27-E4DBED8E123E"),
ClassInterface(ClassInterfaceType.None),
ComSourceInterfaces(typeof(DBCOM_Events))]
public class DBCOM_Class : DBCOM_Interface
{
You have this part
// // Events interface Database_COMObjectEvents
[Guid("47C976E0-C208-4740-AC42-41212D3C34F0"),
InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface DBCOM_Events
{
}
But without the second the vtable and typelib of the COM object doesn't have the Event Maps needed to work with VB6 (or other COM Consumers).
You can use the Google search terms "com event" c# and get a bunch of other good results.
I was defining the interface using the delegate instead of the event:
/* Externally Accesssible Event API */
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface ISerialEvent
{
[DispId(5)]
void DataEvent();
}
should be
/* Externally Accesssible Event API */
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface ISerialEvent
{
[DispId(5)]
void dEvent();
}

Categories