Calling overloaded C# array access from unmanaged C++/CLI - c#

I have a C# project which has overridden the array access ([]) like so:
Foo.cs:
public override FooItem this[long index] {
...
The project is compiled into a .dll which is referenced by my C++/CLI project.
I have an unmanaged C++/CLI class, FooAccess:
FooAccess.h:
class FooAccess : NativeCPPClass { // not ref class
private:
gcroot<CSDll::Foo^> myFoo;
public:
void Accessor();
In FooAccess.cpp:
void FooAccess::Accessor() {
myFoo->[0]; // doesn't work
myFoo[0]; // doesn't work
pin_ptr<FooItem^> p = &myFoo[0]; // doesn't work
I am stumped.
Note that I am not allowed to edit the C# project and the C++/CLI class cannot be a ref class since it inherits from a native class.

The gcroot template makes the syntax clumsy. You have to cast to convince it to spit out the object reference, ((Foo^)myFoo)[0]. Ugh. The readable way is to lift the object reference out of the template explicitly:
void FooAccess::Accessor() {
Foo^ obj = myFoo;
FooItem^ value = obj[0];
//...
}
But you can write it directly by using the default keyword:
FooItem^ value = myFoo->default[0];
Do consider encapsulation instead of inheritance, storing a private NativeCPPClass* in a ref class, helps you avoid the high cost and awkwardness of gcroot.

Related

Exposing C++ unmanaged code class pointers to C# with C++/CLI

Apologize for my very basic knowledge of C++/CLI, but it has only been introduced to me recently as a necessity. I am trying to create a wrapper for an unmanaged C++ library using C++/CLI. I have followed this helpful guide and can confirm that it (somehow) does the trick. The problem I have is with C++ pointers which expose access to public classes in the unmanaged library. In my CPP library I used them to call the methods from several classes, by having access to a single point-of-entry class. In C# I can't access the methods which should be accessible via these class pointers. I am getting an error stating "the member is inaccessible due to its protection level" and the "->" pointer does not grant access to the exposed class members.
Here's what my code looks like:
UnmanagedCode::ClassA
...
// some code
...
public:
void doSomething() { ... };
ClassB* classB() { return classB.get(); };
private:
std::unique_ptr<ClassB> classB_ {};
C++/CLI wrapper
public ref class WrapperClass: public ManagedObject<UnmanagedCode::ClassA>
{
public:
WrapperClass() : ManagedObject(new UnmanagedCode::ClassA) {}; // template class code in the hyperlink
~WrapperClass() {};
void doSomething() { m_Instance->doSomething(); };
UnmanagedCode::ClassB* classB() { return m_Instance->classB(); };
};
C# program
class Program
{
static void Main(string[] args)
{
WrapperClass w = new WrapperClass();
w.doSomething(); // this works fine
w.classB(); // here is where I am getting the error
}
}
My C# knowledge, is not quite as good as my C++, just as a note.
I dont quite get what you are doing here:
UnmanagedCode::ClassB* classB() { return m_Instance->classB(); };
This line returns a pointer to a ClassB object, that you retrieve in C#. This would require the ClassB to also be a managed object, as far as i know, otherwise, i dont think you can call functions on the object, without creating delegates for unsafe function pointers.
If i understand your issue correct, i think something like this, would fix your issue, in C++:
void* classB() { return static_cast<void*>(m_Instance->classB()); };
void doSomethingWithClassB(void* instance) {static_cast<ClassB*>(instance)->doSomethingWithClassB();}
and then in C#:
class Program
{
static void Main(string[] args)
{
WrapperClass w = new WrapperClass();
w.doSomething();
unsafe
{
void* classb = w.classB();
w.doSomethingWithClassB(classb);
}
}
I think you need to provide a wrapper for each class to make it useful in c#.
I.e. to return the classB you would write something like
MyClassBWrapper^ GetClassB(){ return gcnew MyClassBWrapper(m_Instance->classB());
While you can mess around with pointers in managed code, it requires unsafe code, and is quite cumbersome to use. If you are writing a c++/cli wrapper anyway you should provide a fully managed API. With the possible exception of (optionally) using pointers for large data blocks like images or similar.

Returning Managed C# List to Unmanaged C++ code

I have a C# dll ( for which register for COM interop option is set).
This C# dll has the below interface and class
interface IMyInterface
{
bool IsNameExists(string name);
List<string> GetNameList();
}
public class MyClass : IMyInterface
{
public bool IsNameExists(string name)
{
//DO Something
}
public List<string> GetNameList()
{
// DO something
}
}
I need to call the methods IsNameExists and GetNameList from unmanaged C++.
#import "..\..\ProdCon\bin\ProdCon.tlb" raw_interfaces_only
void main()
{
HRESULT hr =::CoInitialize(NULL);
IMyInterface pIMyInterface(__uuidof(MyClass));
VARIANT_BOOL bRet = FALSE;
BSTR bstrName = ::SysAllocString(_T("RAJESH"));
hr = pIMyInterface->IsNameExists(bstrName,&bRet);
}
So I created COM object as above and called the IsNameExists mehtod without any issue.
Since GetNameList method returns list, I am getting the below warning
'MyDll.IMyInterface.GetNameList(#0), MyDll'. Warning: Type library
exporter encountered a generic type instance in a signature. Generic
code may not be exported to COM.
Please help me how to return C# list to unmanaged C++ code. So that unmanaged C++ code can use this list.
'Generic code may not be exported to COM.' => hence its the type parameter string in public List GetNameList(). Thus essentially you need access to a non-generic c# method to get the data.
If you have control of the MyClass codebase you could (for example) add a:
public string[] GetNameArray()
{
return GetNameList.ToArray();
}
If not then you'll need to write a proxy/wrapper class to do something similar to the above and expose that via COM, either as a one off or a 'general' methodology using reflection say.
See for example http://weblog.west-wind.com/posts/2007/Jul/10/Generics-and-COM-Interop-dont-mix

Typedefs, C++/CLI, C# + dependency injection

I have a specialized dictionary written in C# that takes two generic arguments. The interface is
public interface IMyDictionary<TKey, TValue> { ... }
and one of it's implementations
public MyDictionary<TKey, TValue> {...}
I also have a complicated C++ template structure which I typedef in my C++/CLI ref class:
typedef boost::some_complex_template<stuff> MySimpleValueType;
In my C++/CLI ref class constructor, I used to create an instance of the dictionary:
MyCppCliClass::MyCppCliClass()
: _my_dict(gcnew MyDictionary<MySimpleValueType, Something^>())
{...}
Now, if you like dependency injection, you will notice that this is bad. Ideally, I should have a constructor like this:
MyCppClass::MyCppCliClass(IMyDictionary<MySimpleValueType, Something^>^ dict){...}
This class is instantiated in C#, so now my question:
How can I instantiate this valid ref class that I am using right now outside of C++/CLI, given that (afaik) the C++ template typedef nor the pure C++ types are available in C#?
MySimpleValueType obviously must be a native type of C#, or instantiation of the Dictionary will fail:
error C3225: generic type argument for 'T1' cannot be '...', it must be a value type
> or a handle to a reference type.
I feel I should be able to define the type in the C++/CLI class (with the typedef), but instantiate the Dictionary from outside. C++/CLI typedefs are not available in C#, so maybe there is a way with a getter and type deduction of the var? Any ideas?
boost is an unmanged library. That error message is telling you that you can't use an unmanaged type in a generic managed class.
To work around this, make a managed class that will hold your unmanaged one, and use that in the container.
public ref class MySimpleValueTypeHolder
{
private:
MySimpleValueType* unmanaged;
public:
property MySimpleValueType* ValueType {
MySimpleValueType* get() { return this->unmanaged; }
}
MySimpleValueTypeHolder() { this->unmanaged = new MySimpleValueType(); }
~MySimpleValueTypeHolder() { this->!MySimpleValueTypeHolder(); }
!MySimpleValueTypeHolder()
{
if(this->unmanaged != nullptr)
{
delete this->unmanaged;
this->unmanaged = nullptr;
}
}
};
Dictionary<MySimpleValueTypeHolder^, Something^>^ foo;

Return dynamic object in C++/CLI to C#

I've got a C++/CLI project that wraps a large number of classes - and the classes have their own meta-data system. So I'd like to return a dynamic object to make some use cases easy in C#. But I don't see how to do this in C++.
In C# I can write the following:
dynamic TestThisOut()
{
return null;
}
void mork()
{
var d = TestThisOut();
d.Fork();
}
I would like to write TestThisOut() in C++/CLI, so that I can use it exactly the same way as I do in the "mork" function above (i.e. not having to type the dynamic keyword). Is this possible?
You can create a dynamic object in c++/cli, you just can't consume it there as you would in C# project. However you can consume it from C#. Here is how you do it:
In C++/CLI create a property or a method that returns Object^. Mark this property or method with System::Runtime::CompilerServices::DynamicAttribute. You are good to go.
Here is a quick sample I created:
namespace ClassLibrary1
{
using namespace System;
using namespace System::Dynamic;
public ref class Class1 : public DynamicObject
{
public:
[System::Runtime::CompilerServices::Dynamic]
static property Object^ Global
{
public:
Object^ get()
{
return gcnew Class1();
}
}
public:
String^ Test()
{
return "Test";
}
};
}
And my C# consumer:
Console.WriteLine( ClassLibrary1.Class1.Global.Test() );
dyanmic is C# 4.0 keyword. It is not supported in C++ CLI (and also in VB.NET).
If you want to consume dynamic object in C++ CLI, then you can use impromptu-interface library. List of supported options. Information is from this question

How to import and use a unmanaged C++ class from C#?

I have an native C++ dll, some header files and the import library. Is there a way how to instantiate an object within C# that is defined in the dll?
The two ways I'm aware of are:
to wrap the C++ code into COM
to use DLLImport and external C functions
C++/CLI is your friend for this. You'll run into one problem though: it is not possible to store standard C++ objects inside C++/CLI ref or value classes (the ones for .NET). So you'll have to resort to the following class (that you can modify) that I use in production code:
#pragma once
#include <boost/shared_ptr.hpp>
template <typename T>
ref class Handle
{
boost::shared_ptr<T>* t;
!Handle()
{
if (t != nullptr)
{
delete t;
t = nullptr;
}
}
~Handle() { this->!Handle(); }
public:
Handle() : t(new boost::shared_ptr<T>((T*)0)) {}
Handle% operator=(T* p)
{
if (p != t->get()) t->reset(p);
return *this;
}
static T* operator&(Handle% h) { return h.t->get(); }
static boost::shared_ptr<T> operator->(Handle% h) { return *h.t; }
T& reference() { return *t->get(); }
T const& const_reference() { return *t->get(); }
};
Usage: Handle<MyCppClass>^ handle; inside a C++/CLI class. You then implement stub methods, forwarding them to the handle member. Garbage collected objects will call destructors of the C++ class instance iff there is no more pointer to it:
public ref class Foo
{
void bar() { handle->bar(); }
internal:
Handle<CppNamespace::Foo>^ handle;
};
I think that your option is only build a C++/CLI class wrapper (so you can reference it like a c# class), otherwise you can't instantiate a c++ class (unmanaged) from c# code.
Alternative could be the "ugly" way: instantiate the c++ class through a c function, but you'll treat the class as a void pointer inside the c# code, so you basically will not do anything with it (except if you create other functions to interact with this class; only C functions)
C# understands C, if you want make it understand C++ you have to use C++/CLI
P.S.
C# has some basic understanding of C++ classes, but it's only about converting class data (I mean bytes: fields) into some usable data in C# (there are some Attributes), but will not allow to work with methods and things like that (avoid totally is my suggestion).

Categories

Resources