Runtime exception when calling C++/CLI derived class - c++-cli

Edited by the OP 5 August 2014:
Much simplified code:
public ref class CXmlWriter : public System::Xml::XmlTextWriter
{
public:
CXmlWriter(System::String ^sFilename) : XmlTextWriter(sFilename, System::Text::Encoding::Unicode)
{
}
~CXmlWriter()
{
}
};
Call a function containing code that instantiates CXmlWriter (you don't have to execute that instantiation code) and you get the exception.
Comment out the destructor and you don't get the exception. Making the destructor virtual doesn't fix it.
End of edit
I am using Version 4 of the .NET framework.
I have C++/CLI classes CXmlWriter derived from System::Xml::XmlTextWriter and CMinMaxXmlWriter derived from CXmlWriter.
Implementation is pretty simple for both classes and everything compiles without error. However, when I try to instantiate CMinMaxXmlWriter at runtime I get a TypeLoadException with the error message:
Declaration referenced in a method implementation cannot be a final method
with a mention of CXmlWriter
This used to work without any problem in Version 2 of the framework.
Here's the header for CXmlWriter:
public ref class CXmlWriter : public System::Xml::XmlTextWriter
{
public:
CXmlWriter(System::String ^sFilename);
~CXmlWriter();
!CXmlWriter() {}
virtual bool Open();
virtual void Close() override;
virtual bool WriteValueAndAttribute(System::String ^sElementName, System::String ^sElementValue, System::String ^sAttrName, System::String ^sAttrValue);
virtual bool WriteValueAndAttribute(System::String ^sElementName, double dElementValue, System::String ^sAttrName, System::String ^sAttrValue);
protected:
bool m_bIsOpen;
};
CMinMaxXmlWriter is defined in a very similar way.
Please can someone explain why the exception occurs and what I should do to avoid it.

Related

Cannot cast C# enum to C++ enum when enum is in third assembly

I've got a C# class:
public class MyManagedClass
{
public ManagedEnum EnumValue {get; set;}
}
which uses the C# enum
public enum ManagedEnum
{
Enum1,
Enum2
}
This is accessed by a C++/CLI wrapper class and enum:
enum NativeEnum
{
Enum1,
Enum2
};
class WrapperClass
{
public:
WrapperClass(ManagedNamespace::MyManagedClass^ inManaged):
_Managed(inManaged)
{}
NativeEnum GetEnumValue()
{
return (NativeEnum)_Managed->EnumValue;
}
private:
gcroot<ManagedNamespace::MyManagedClass^> _Managed;
};
Now, as long as the C# class and C# enum are in the same assembly, this works fine.
But if the C# enum is in a different C# assembly, the C# class still builds fine, but trying to compile the C++ class yields the error:
error C2440: 'type cast' : cannot convert from 'OtherNamespace::ManagedEnum' to 'OtherNamespace::NativeEnum'
1> Conversion requires a constructor or user-defined-conversion operator, which can't be used by const_cast or reinterpret_cast
Try deriving the underlying value, then cast to the native enum.
This is a crude way of doing it, but may be sufficient in your case.
NativeEnum someMethod(ManagedEnum myEnum)
{
return (NativeEnum)(int)myEnum;
}
Another way is to create a native template method taking both types and the managed enum input, and returning the native type. In such a case, you would have to use reflection to ascertain the underlying type of the managed enum.
In trying out Aaron P's answer, I discovered that the problem was that my C++ project didn't have the C# assembly with the enums in it as a reference. Once I added that reference, it all worked fine.

Different function definition after compilation

I have my Keyboard class:
namespace BSGameFramework
{
namespace Input
{
static public ref class Keyboard
{
public:
static KeyboardState GetState();
};
}
}
Where KeyboardState is a ref struct.
After compilation as dll from my C# application I see the function from metadata as follow:
namespace BSGameFramework.Input
{
public class Keyboard
{
public Keyboard();
public static void GetState(ref KeyboardState value);
}
}
Keyboard class has lost its static state and the function GetState is now returning void and taking a KeyboardState as referenced parameter!
Somebody know why? Thanks in advance :D
The reason is because of the return type, ref struct KeyboardState.
In C++/CLI, the "ref" vs. "value" is the thing that determines whether a type is a reference type or a value type, not "class" vs. "struct". ref class and ref struct are both the same thing as C#'s class. value class and value struct are both the same thing as C#'s struct.
Therefore, you have a C++/CLI method declared as returning a reference type, but without the ^. This data type does exist in C++/CLI, but not in C#. The method signature you see is a workaround.
There are two possible solutions to this issue:
Change KeyboardState to a value struct. From what you said, it sounds like you intended for this to be a value type from the beginning, so this is probably the best solution.
Change the return type of the method to KeyboardState^. This will let the method show up in C# the same as it does in C++/CLI. However, if you do this, you'll want to switch all uses of KeyboardState to KeyboardState^. It's a reference type, it should be used with a ^.

System.NullReferenceException when using gcroot

Turns out this error had nothing to do with gcroot - I connected some constructors shoddily and lost the instantiated object on the way.
For the record, all versions of the code below worked after this fix, but adding the simple Wrapper class allows you to use & and * operators on the wrapped object, which gcroot forbids. I am using the version outlined in the third code block below.
I am trying to use some measuring instruments via a .dll, but I am having problems with the implementation. Writing all the code into the main.cpp and using the object like this works fine:
#using "M3D_FP_USB.dll"
int main(){
M3D_FP_USB ^obj = gcnew M3D_FP_USB;
obj->Connect();
...
}
However, when I put it into a class and wrap it with gcroot, I get an "Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of the object" on execution.
class User{
...
public:
User();
gcroot<M3D_FP_USB^> obj;
}
User::User(){
obj = gcnew M3D_FP_USB;
obj->Connect();
...
}
I suspect that the gcroot or it being called inside the unmanaged class might prevent the instruments from accessing the M3D_FP_USB object. I tried adding a managed Wrapper class, and give a handle to the Wrapper object to the User class. That didn't solve the problem, though.
class User{
...
public:
gcroot<Wrapper^> wobj;
}
ref class Wrapper{
...
public:
M3D_FP_USB^ iobj;
...
}
Wrapper::Wrapper(){
iobj = gcnew M3D_FP_USB;
iobj->Connect();
...
}
The new code I have looks like this, but I couldn't test it with the instruments yet. I will update when I get the chance.
Wrapper::Wrapper(M3D_FP_USB^ obj){
iobj = obj;
iobj->Connect();
...
}
int main(){
M3D_FP_USB ^obj = gcnew M3D_FP_USB;
Wrapper Ex(obj);
}
Does the exception also happen if you make the class managed and just declare the handle directly? Like:
ref class User
{
public:
M3D_FP_USB^ obj;
...
};
Though I don't see why it wouldn't work the way you're doing it...

c++ native code using an abstract class into a wrapper

I need to implement a c# GUI for my unmanaged code. So i have designed a wrapper to deal with my native code, but this does not work porperly.
I have a method which requires to make an instance to an abstract class and i'm not sure how to deal with it.
First for my C++ classes i used an abstract class:
class Interface abstract
{
public: Interface (void);
public: ~Interface (void);
public: virtual double Get() = 0;
};
And i used a ClassSpecific1 and a ClassSpecific2 depending on my current application, and i inherited the functions from the abstract class for each one.
class ClassSpecific1 : public Interface
{
public: ClassSpecific1 (void);
public: ~ClassSpecific1 (void);
private: double Get();//based on the abstract class
};
class ClassSpecific2 : public Interface
{
public: ClassSpecific2 (void);
public: ~ClassSpecific2 (void);
private: double Get();//based on the abstract class
};
Later i used another class, it works as a general class and uses the ClassSpecific1 or the ClassSpecific2 with an instance of the abstract class.
class ClassAPI
{
public: ClassLaserAPI(void);
public: ~ClassLaserAPI(void);
public: double Get(Interface *objToInterface);//This Get() calls the Get() in ClassSpecific1 or ClassSpecific2
};
Until here everything seems all right. I have tested everything and works as expected. My big problem is that i don't know how to make my method Get(Interface *objToInterface) from ClassAPI into my wrapper. Do i need to make a wrapper for my abstract class first in order to be able to create the instance**(Interface *objToInterface)** on the wrapper?
This is what i have so far, i hope someone can give me some help, i'm getting lost in how to proceed.
namespace API
{
public __gc class APIManaged
{
public: APIManaged(void);
public: ~APIManaged(void);
/** Unmanaged pointer to ClassAPI
*
*/
private: ClassAPI __nogc* cAPI;
public: double Get(Interface __nogc* objInterface);
};
I'm also having the same problem and got it resolved using this topic:
C++/CLI Inheriting from a native C++ class with abstract methods and exposing it to C#
I too am using an unmanaged abstract class and got things working by following the tips outlined in the link. I ended up with a few errors to fix and fixed them by changing my linker options in my unmanaged projects to not compile with /clr and only the wrapper project compiles using /clr.
Posting your output errors may help clarify some of the troubles you are having...

C++ CLI error C3767: candidate function(s) not accessible

I'm new to C++ CLI coming from unmanaged C++ world.
I'm getting this error:
candidate function(s) not accessible
when I pass a std::string as part of the method argument.
Here's the exact code:
Lib Project (compiled as .dll project)
//Lib.h
#pragma once
public ref class Lib
{
public:
Lib(void);
public:
void Extract( std::string& data_ );
};
//Lib.cpp
#include "Lib.h"
Lib::Lib(void)
{
}
void Lib::Extract( std::string& data_ )
{
data_.empty();
}
LibTest Project (compiled as application.exe)
// LibTest.h
#pragma once
ref class LibTest
{
public:
LibTest(void);
};
// LibTest.cpp
#include "LibTest.h"
LibTest::LibTest(void)
{
Lib^ lib = gcnew Lib;
lib->Extract( std::string("test") );
}
int main()
{
return 0;
}
Compiler Error:
1>------ Build started: Project: LibTest, Configuration: Debug Win32 ------
1>Compiling...
1>LibTest.cpp
1>.\LibTest.cpp(7) : error C3767: 'Lib::Extract': candidate function(s) not accessible
The problem is that std::string will compile as a internal (non public) type. This is actually a change in VS 2005+:
http://msdn.microsoft.com/en-us/library/ms177253(VS.80).aspx:
Native types are private by default outside the assembly
Native types now will not be visible outside the assembly by default. For more information on type visibility outside the assembly, see Type Visibility. This change was primarily driven by the needs of developers using other, case-insensitive languages, when referencing metadata authored in Visual C++.
You can confirm this using Ildasm or reflector, you will see that your extract method is compiled as:
public unsafe void Extract(basic_string<char,std::char_traits<char>,std::allocator<char> >* modopt(IsImplicitlyDereferenced) data_)
with basic_string being compiled as:
[StructLayout(LayoutKind.Sequential, Size=0x20), NativeCppClass, MiscellaneousBits(0x40), DebugInfoInPDB, UnsafeValueType]
internal struct basic_string<char,std::char_traits<char>,std::allocator<char> >
Note the internal.
Unfortunately you are then unable to call a such a method from a different assembly.
There is a workaround available in some cases: You can force the native type to be compiled as public using the make_public pragma.
e.g. if you have a method Extract2 such as:
void Extract2( std::exception& data_ );
you can force std::exception to be compiled as public by including this pragma statement beforehand:
#pragma make_public(std::exception)
this method is now callable across assemblies.
Unfortunately make_public does not work for templated types (std::string just being a typedef for basic_string<>)
I don't think there is anything you can do to make it work. I recommend using the managed type System::String^ instead in all your public API. This also ensures that your library is easily callable from other CLR languages such as c#
if you simply must access the internal methods another work around would be making the projects as Friend Assemblies like that:
//Lib Project
#pragma once
//define LibTest as friend assembly which will allow access to internal members
using namespace System;
using namespace System::Runtime::CompilerServices;
[assembly:InternalsVisibleTo("LibTest")];
public ref class Lib
{
public:
Lib(void);
public:
void Extract( std::string& data_ );
};
//LibTest Project
#pragma once
#using <Lib.dll> as_friend
ref class LibTest
{
public:
LibTest(void);
};
In addition to the solutions described above, one can subclass the templated type to obtain a non-templated type, and include its definition in both projects, thus overcoming some of the problems mentioned above.