Allow managed code in hosted environment to call back unmanaged code - c++

I have C++ code that hosts a clr in order to make use of Managed.dll, written in c#.
This .net has a method like the following that allows code to register for notification of events:
public void Register(IMyListener listener);
The interface looks something like this
public interface IMyListener
{
void Notify(string details);
}
I'd like to do stuff in the C++ part of the program, triggered by the events in the .net world. I would not even mind creating another managed dll for the sole purpose of making Managed.dll more C++-friendly, if that is necessary.
What are my options here? The only one I am sure I could implement is this:
Write another managed dll that listens for those events, queues them and lets the C++ code access the queue via polling
This would of course change from an 'interrupt' style to a 'polling' style with all its advantages and disadvantages and the need to provide for queuing. Can we do without polling? Could I somehow call managed code and provide it a function pointer into the C++ world as the argument?
Update
Thanks to stijn's answer and comments I hope I moved a bit in the right direction, but I guess the main problem still open is how to pass a fn pointer from unmanaged land into the clr hosted environment.
Say I have an "int fn(int)" type of function pointer that I want to pass to the managed world, here are the relevant parts:
Managed code (C++/CLI)
typedef int (__stdcall *native_fun)( int );
String^ MyListener::Register(native_fun & callback)
{
return "MyListener::Register(native_fun callback) called callback(9): " + callback(9);
}
Unmanaged code
typedef int (__stdcall *native_fun)( int );
extern "C" static int __stdcall NativeFun(int i)
{
wprintf(L"Callback arrived in native fun land: %d\n", i);
return i * 3;
}
void callCLR()
{
// Setup CLR hosting environment
...
// prepare call into CLR
variant_t vtEmpty;
variant_t vtRetValue;
variant_t vtFnPtrArg((native_fun) &NativeFun);
SAFEARRAY *psaMethodArgs = SafeArrayCreateVector(VT_VARIANT, 0, 1);
LONG index = 0;
SafeArrayPutElement(psaMethodArgs, &index, &vtFnPtrArg);
...
hr = spType->InvokeMember_3(bstrMethodName, static_cast<BindingFlags>(
BindingFlags_InvokeMethod | BindingFlags_Static | BindingFlags_Public),
NULL, vtEmpty, psaMethodArgs, &vtRetValue);
if (FAILED(hr))
wprintf(L"Failed to invoke function: 0x%08lx\n", hr);
The spType->InvokeMember_3 call will lead to a 0x80131512 result.
Something seems to be wrong with the way I pass the pointer to NativeFun over to the managed world, or how my functions are defined. When using a String^ param instead of the fn ptr, I can call the CLR function successfully.

You can write a seperate dll in C++/CLI and implement the interface there, and forward the logic to C++. From my experience with mixing managed/unmanaged I can say using an intermediate C++/CLI step is the way to go. No fiddling with DllImport and functions only, but a solid bridge between both worlds. It just takes some getting used to the syntax and marshalling, but once you have that it's practically effortless. If you need to hold C++ objects in the managed class, best way is to use something like clr_scoped_ptr.
Code would look like this:
//header
#using <Managed.dll>
//forward declare some native class
class NativeCppClass;
public ref class MyListener : public IMylIstener
{
public:
MyListener();
//note cli classes automatically implement IDisposable,
//which will call this destructor when disposed,
//so used it as a normal C++ destructor and do cleanup here
~MyListener();
virtual void Notify( String^ details );
private:
clr_scoped_ptr< NativeCppClass > impl;
}
//source
#include "Header.h"
#include <NativeCppClass.h>
//here's how I marshall strings both ways
namespace
{
inline String^ marshal( const std::string& i )
{
return gcnew String( i.data() );
}
inline std::string marshal( String^ i )
{
if( i == nullptr )
return std::string();
char* str2 = (char*) (void*) Marshal::StringToHGlobalAnsi( i );
std::string sRet( str2 );
Marshal::FreeHGlobal( IntPtr( str2 ) );
return sRet;
}
}
MyListener::MyListener() :
impl( new NativeCppClass() )
{
}
MyListener::~MyListener()
{
}
void MyListener::Notify( String^ details )
{
//handle event here
impl->SomeCppFunctionTakingStdString( marshal( details ) );
}
update
Here's a simple solution to call callbacks in C++ from the managed world:
pubic ref class CallbackWrapper
{
public:
typedef int (*native_fun)( int );
CallbackWrapper( native_fun fun ) : fun( fun ) {}
void Call() { fun(); }
CallbackWrapper^ Create( ... ) { return gcnew CallbackWrapper( ... ); }
private:
native_fun fun;
}
you can also wrap this in an Action if you want.
Another way is using GetDelegateForFunctionPointer, for example as in this SO question

If someone still needs a better way for this , you can simply pass c++ function to CLR using intptr_t in variant and long in managed , then use Marshall and delegate to invoke your native function , super easy and works like charm.
if you need a code snippet , let me know.

Related

Qpid proton c++ - proton::make_work

I'm trying to add proton::work function (opening a new sender) inside the work queue of the proton::connection object. I have a pointer to the working queue, but my problem is how to bind the open_sender function correctly.
I'm aware of the real problem here : the parameter of the function :
sender open_sender(const std::string& addr);
As the string is passed by reference, I have to de-reference it. I'm ok with that, but how to do it with the proton tools ?
Here my line of code :
proton::work w = proton::make_work( &proton::connection::open_sender, &m_connection, p_url);
Note :
Of course I'm not using C++11 in my project, it would be too simple
to ask ;) !
Of course I cannot change to C++11
If you have a better idea on how to create a new sender in a multi-threaded program let me know.
Usually you will use the proton::open_sender API from within the handler for connection open or container start so you will not have to use proton::make_work in most cases. If you look at the Proton C++ examples, a good place to start is simple_send.cpp.
Abbreviated code might look like this:
class simple_send : public proton::messaging_handler {
private:
proton::sender sender;
const std::string url;
const std::string addr;
...
public:
simple_send(...) :
url(...),
addr(...)
{}
...
// This handler is called when the container starts
void on_container_start(proton::container &c) {
c.connect(url);
}
// This handler is called when the connection is open
void on_connection_open(proton::connection& c) {
sender = c.open_sender(addr);
}
...
}
int main() {
...
simple_send send(...);
proton::container(send).run();
...
}
There are other examples that come with Proton C++, that should help you figure out other ways to use Proton C++. See https://github.com/apache/qpid-proton/tree/master/examples/cpp.
There is also API documentation you can find at http://qpid.apache.org/releases/qpid-proton-0.20.0/proton/cpp/api/index.html (for the current release as of February 2018).

pass ref class object(Uri^) as native pointer parameter(IUriRuntimeClass *)

I have following midl method in my code:
interface IMyClass : IInspectable
{
HRESULT Func1([in] Windows.Foundation.Uri *uri);
}
It generates following interface method:
IMyClass : public IInspectable
{
public:virtual HRESULT STDMETHODCALLTYPE Func1(
/* [in] */ __RPC__in_opt ABI::Windows::Foundation::IUriRuntimeClass *uri) = 0;
}
The interface is implemented in App side and its object is passed to my code where I can 'see' only interface.
I want to know what is the best way to call Func1 and pass Windows::Foundation::Uri object as parameter?
Simply passing ref class object does not not work, due to C2664 error
Windows::Foundation::Uri^ u = ref new Uri(...);
IMyClassObj->Func1(u); // error cannot convert argument 1 from Windows::Foundation::Uri ^' to 'ABI::Windows::Foundation::IUriRuntimeClass *
I could achive my goal with reintrepret_casting:
Windows::Foundation::Uri^ u = ref new Uri(...);
ABI::Windows::Foundation::IUriRuntimeClass* uu = reinterpret_cast<ABI::Windows::Foundation::IUriRuntimeClass*>(u);
MyClassObj->Func1(u); // this works fine
Is reinterpret_cast right approach in this situation? Or is there any other way of passing Uri^ object as IUriRuntimeClass* parameter?
A slightly cleaner way to do this is to wrap it in a ComPtr as soon as possible so you get the right behaviour if any exceptions are thrown etc. (Your method might be simple today, but it could get more complex later on).
Something like this:
#include <wrl/client.h>
#include <windows.foundation.h>
// Dummy method that just prints out the URI to the debug console
HRESULT MyFunc(ABI::Windows::Foundation::IUriRuntimeClass* uri)
{
HSTRING str{};
HRESULT ret{ S_OK };
if (SUCCEEDED(ret = uri->get_AbsoluteUri(&str)))
OutputDebugString(WindowsGetStringRawBuffer(str, nullptr));
WindowsDeleteString(str);
return ret;
}
void Test()
{
using namespace Microsoft::WRL;
// Create the ref class
auto uri = ref new Windows::Foundation::Uri(L"http://www.bing.com");
// Wrap in a dummy IUnknown wrapper. In theory you could use
// IInspectable or even IUriRuntimeClass but if you're going to
// copy-paste the code elsewhere, IUnknown is the "safest" thing you
// can reinterpret_cast<>() to.
ComPtr<IUnknown> iUnknown{ reinterpret_cast<IUnknown*>(uri) };
// Try to cast it to the IUriRuntimeClass, and call our method if
// it succeeds
ComPtr<ABI::Windows::Foundation::IUriRuntimeClass> iUri{};
if (SUCCEEDED(iUnknown.As(&iUri)))
MyFunc(iUri.Get());
}

ComPtr<ID3D11Device> to IntPtr

I'm an absolute zero at C++. But I need to write a small c++ class for managing a d3ddevice.
My C# code is:
public class HCPPUtils
{
[DllImport("HSpectrum\\Assets\\HCPPUtils.dll")]
private static extern int Getd3Device(ICanvasResourceCreator resourceCreator);}
HCPPUtils hcp = new HCPPUtils();
var pnt = hcp.HGetOrCreate(ResourceCreator);
var d3dDevice = SharpDX.Direct3D11.Device.FromPointer<SharpDX.Direct3D11.Device>(new System.IntPtr(pnt));
My C++ code is:
extern "C"
{
__declspec(dllexport) int Getd3Device
(Microsoft::Graphics::Canvas::ICanvasResourceCreator^ canvasDevice)
{
ComPtr<ID3D11Device> m_device;
__abi_ThrowIfFailed(Windows::Graphics::DirectX::Direct3D11::GetDXGIInterface(canvasDevice->Device,m_device.GetAddressOf()));
return m_device???
}
}
How can i return a IntPtr from C++ code; so, how can i get IntPtr from ComPtr < ID3D11Device >?
[edited]
What I'm doing is...
I have a win2d canvasandimatedcontrol in my c# project. I want to draw direct3d object in it using sharpdx. But I found out that I need to have the d3ddevice object from win2d canvas. And there isn't a c# method to get it.
So the only solution I can imagine is to build a simple c++ project to which I can pass the canvas control and get the d3ddevice. The only problem is how to pass back the d3d device to c#. Sharp DX seems to have just a method Device.FormIntPtr to create it. But I'm not able to pass back the intptr to the c# object.
I tried to implement what Rook wrote, but I cannot understand how it could be useful for my scenario. I mean it could be usueful, but I need to pass the IDirect3DDevice object from a c++ project anyway.
I suspect what you need to do is to read the docs for things like this: http://microsoft.github.io/Win2D/html/M_Microsoft_Graphics_Canvas_CanvasDevice_CreateFromDirect3D11Device.htm
CanvasDevice implements ICanvasResourceCreator, so you could return it directly once you've created it using the static factory method.
Be careful with the scope and lifetime of m_device here, because you don't want its refcount to be decremented when Getd3Device returns and the ComPtr goes out of scope. I'm assuming that it is actually part of a class that will look after its lifetime, but it bears repeating just in case.
I've been trying to access the unity3d device today. This is how I passed the pointer back into unity/managed code:
cpp:
/*
delegate to pass directx device/context back to managed code
see https://forum.unity3d.com/threads/communicating-c-with-c.89930/#post-586885
*/
typedef int(__stdcall *ANSWERCB)(ID3D11Device* ptr);
static ANSWERCB cb;
extern "C" __declspec(dllexport) int GetDevice(ANSWERCB fp)
{
cb = fp;
if (cb)
{
return cb(s_CurrentAPI->GetDevice());
}
return 0;
}
cs:
[DllImport("RenderingPlugin")]
private static extern void GetDevice(Action<IntPtr> callback);
later I call:
GetDevice(devicePtr =>
{
Debug.Log(devicePtr);
if (devicePtr == IntPtr.Zero) return;
device = SharpDX.Direct3D11.Device.FromPointer<SharpDX.Direct3D11.Device>(devicePtr);
...
works fine in the editor as well as the built in the new 2017.1 beta version (as long as you copy the necessary 64bit system dlls to unitys plugin folder)

c++: How to obtain context when callback doesn't provide user arg?

First, some background:
(Note: Though I'm in non-.NET Win32 land, this is really a C++ question)
I'm using a 3rd party API which requires you to register a callback function in order to know when an async operation is complete. Gotta use the callback, no way around it.
A non-OOP implementation would be something like this:
void __stdcall MyCbFcn(int value)
{
do something with 'value'...
}
API_RegisterCallback(MyCbFcn);
Pretty standard stuff.
BUT...
My code is OOP, with multiple instances rx'ing the callback, thus the callback needs to be routed to the object that registered it.
Knowing that folks do this, callbacks typically include a user var, something like:
void __stdcall MyCbFcn(int value, U32 user)
{
do something with 'value'...
}
API_RegisterCallback(MyCbFcn, someUserValue);
and more specifically, when combined with OOP, this user arg allows you to get back into context:
(written inline for brevity):
class MyClass
{
public:
MyClass()
{
API_RegisterCallback(MyClass::StaticCbFcn, (U32)this);
}
private:
static void __stdcall StaticCbFcn(int value, U32 user)
{
MyClass* pThis = (MyClass*)user;
pThis->InstanceCbFcn(value);
}
void InstanceCbFcn(int value)
{
... do some work in context ...
}
}
BUT, my API doesn't feature a user arg :(
So now my question:
How I can get back into context?
I've considered kinda sketchy things like defining a "pool" of 100 distinct callbacks and assigning them as objects are created, but that seems like a real hack.
An obvious solution ... if I were in e.g. JavaScript :) ... would be to use an anonymous function, but AFAIK C++ doesn't have anything like that.
Any ideas would be appreciated.
"100 distinct callbacks" is really the only thing you can do, thus you use the function address as identifying parameter. It might help to implement the different functions as template with a constant parameter:
template < unsinged N >
void StaticCbFcn( int value )
{
map[ N ].InstanceCbFcn( value );
}
You can do this with boost bind:
boost::bind(&my::function_to_call_cb, this, _1, context));
void my_impl::function_to_call_cb(int result, std::string context)

COM interop: how to use ICustomMarshaler to call 3rd party component

I want to call a method in a COM component from C# using COM interop. This is the methods signature:
long GetPrecursorInfoFromScanNum(long nScanNumber,
LPVARIANT pvarPrecursorInfos,
LPLONG pnArraySize)
and this is sample code (which I checked is really working) to call it in C++:
struct PrecursorInfo
{
double dIsolationMass;
double dMonoIsoMass;
long nChargeState;
long nScanNumber;
};
void CTestOCXDlg::OnOpenParentScansOcx()
{
VARIANT vPrecursorInfos;
VariantInit(&vPrecursorInfos);
long nPrecursorInfos = 0;
m_Rawfile.GetPrecursorInfoFromScanNum(m_nScanNumber,
&vPrecursorInfos,
&nPrecursorInfos);
// Access the safearray buffer
BYTE* pData;
SafeArrayAccessData(vPrecursorInfos.parray, (void**)&pData);
for (int i=0; i < nPrecursorInfos; ++i)
{
// Copy the scan information from the safearray buffer
PrecursorInfo info;
memcpy(&info,
pData + i * sizeof(MS_PrecursorInfo),
sizeof(PrecursorInfo));
}
SafeArrayUnaccessData(vPrecursorInfos.parray);
}
And here's the corresponding C# signature after importing the typelib of the COM component:
void GetPrecursorInfoFromScanNum(int nScanNumber, ref object pvarPrecursorInfos, ref int pnArraySize);
If I'm not mistaken, I need to pass in null for pvarPrecursorInfos to make COM interop marshal it as the expected VT_EMPTY variant. When I'm doing it, I get a SafeArrayTypeMismatchException - not really surprising, looking at how the result is expected to be handled in the sample. So I was trying to use a custom marshaler. Since a cannot alter the component itself, I tried to introduce it this way:
[Guid("06F53853-E43C-4F30-9E5F-D1B3668F0C3C")]
[TypeLibType(4160)]
[ComImport]
public interface IInterfaceNew : IInterfaceOrig
{
[DispId(130)]
int GetPrecursorInfoFromScanNum(int nScanNumber, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(MyMarshaler))] ref object pvarPrecursorInfos, ref int pnArraySize);
}
The TypeLibType and DispID attribute are the same as in the original version. This works as far as that the MyMarshaller.GetInstance() method is called, but I do not get a callback in MyMarshaller.NativeToManaged. Instead, an access violation is reported. So is this a reliable approach? If yes - how can I make it work? If no: are there any alternatives?
(Just a footnote: in theory I could try to use managed C++ to call the component natively. However, there are lots of other methods in it that work fine with COM interop, so I would very much like to stick with C# if there is any way.)
Since someone asked for it, here's my solution in Managed C++.
array<PrecursorInfo^>^ MSFileReaderExt::GetPrecursorInfo(int scanNumber)
{
VARIANT vPrecursorInfos;
VariantInit(&vPrecursorInfos);
long nPrecursorInfos = -1;
//call the COM component
long rc = pRawFile->GetPrecursorInfoFromScanNum(scanNumber, &vPrecursorInfos, &nPrecursorInfos);
//read the result
//vPrecursorInfos.parray points to a byte sequence
//that can be seen as array of MS_PrecursorInfo instances
//(MS_PrecursorInfo is a struct defined within the COM component)
MS_PrecursorInfo* pPrecursors;
SafeArrayAccessData(vPrecursorInfos.parray, (void**)&pPrecursors);
//now transform into a .NET object
array<PrecursorInfo^>^ infos = gcnew array<PrecursorInfo^>(nPrecursorInfos);
MS_PrecursorInfo currentPrecursor;
for (int i=0; i < nPrecursorInfos; ++i)
{
currentPrecursor = pPrecursors[i];
infos[i] = safe_cast<PrecursorInfo^>(Marshal::PtrToStructure(IntPtr(&currentPrecursor), PrecursorInfo::typeid));
}
SafeArrayUnaccessData(vPrecursorInfos.parray);
return infos;
}
I look at the github code mzLib, which I believe is related to this topic. The code looks good, where it calls
pin_ptr<const wchar_t> wch = PtrToStringChars(path);
I think it may cause some problem, better use
pin_ptr<const wchar_t> pathChar = static_cast<wchar_t*>(System::Runtime::InteropServices::Marshal::StringToHGlobalUni(path).ToPointer());
The code then seems to be worked just fine when compiles. However, it might run in problem when imported as dll. I worked on that by adding a constructor,such as
public ref class ThermoDLLClass
{
public:
ThermoDLLClass();
PrecursorInfo GetPrecursorInfo(int scanNum, String^ path);
};
Then, it seems to get precursorInfo and parameters appropriately.