C++/CLI - Convert System::Object to ContextMenuStrip - c++-cli

I'm working on a piece of legacy code & I'm trying to update some of the interface. I'm not proficient in C++/CLI and the documentation for C++/CLI is sparse at best. I do my best to convert C# documentation to C++/CLI but it doesn't always work.
I want to convert a System::Object to a ContextMenuStrip.
A sample code is:
System::Void Form1::unzoomToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e)
{
System::Windows::Forms::ContextMenuStrip ^menu = sender;
//a value of type "System::Object ^" cannot be used to initialize and entity of type "System::Windows::Forms::ContextMenuStrip ^"
//Other code here
}
How is this done in C++/CLI?

From the link posted by Hans Passant:
A downcast is a cast from a base class to a class that's derived from the base class. A downcast is safe only if the object that's addressed at runtime is actually addressing a derived class object. Unlike static_cast, safe_cast performs a dynamic check and throws InvalidCastException if the conversion fails.
So you should use:
Windows::Forms::ContextMenuStrip ^menu = safe_cast<Windows::Forms::ContextMenuStrip^>(sender);

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 ^.

What are properties in C++/CLI?

I saw in the term property in C++ code. I think it's connected to C++/CLI.
What is it exactly?
It was indeed connected to C++/CLI (unmanaged C++ doesn't really have a notion of properties).
Properties are entities that behave like fields but are internally handled by getter and setter accessor functions. They can be scalar properties (where they behave like a field) or indexed properties (where they behave like an array). In the old syntax, we had to specify the getter and setter methods directly in our code to implement properties - wasn't all that well received as you might guess. In C++/CLI, the syntax is more C#-ish and is easier to write and understand.
Taken from this article: http://www.codeproject.com/KB/mcpp/CppCliProperties.aspx
Also see MSDN on properties in C++/CLI.
Sample code:
private:
String^ lastname;
public:
property String^ LastName
{
String^ get()
{
// return the value of the private field
return lastname;
}
void set(String^ value)
{
// store the value in the private field
lastname = value;
}
}
Yep indeed this is Microsoft's version of managed c++ code or C++/CLI. Now not only do you still have to write Get & Set Methods but you also need to define it as a property. I will say as much as I hate the extra typing the 'Read Only' and 'Write Only' versions of the property is pretty neat.
But unnecessary in un-managed c++!!!
For instance you could write in a class (will do exactly the same thing!):
std::string GetLastName() const { return lastname;}
void SetLastName(std::string lName) { lastname = lName;}
The 'const' made sure it 'GET' was read only, and the set was clear. No need to define a property or add the confusion of String^ vs. std::string....

Cannot use managed event/objects in unmanaged code error c3265, c2811

Native C++ library that I am using in C++/CLI project raises events giving me results,
If I try to handle the event by extending the unmanaged event, it says the ref class can only extend ref class.
I then tried to create a native event but have manged object inside it to collect the results, but I get the error cannot declare managed object in unmanaged class.
Is there anyway to get it done in one of the ways I am trying, or should I declare unmanaged result objects fill them in unmanaged event and then Marshall it ?
Edit:
class MyNativeListener: public NativeEventListener
{
private:
ManagedResultsObject ^_results;
public:
void onEndProcessing(ProcessingEvent *event)
{
_results.Value = event->value;
//Many more properties to capture
}
};
This is what I am trying, I have extended the native event listener to capture the event, but not sure how to capture the results to a managed object.
Edit2
Found this while searching on the same line as suggested by #mcdave auto_gcroot
Your native class needs to store a handle to the managed object instead of a reference to it. You can do this using the gcroot template. If you dig into the gcroot template you will find it uses the GCHandle Structure, which with appropriate static casting can be stored as a void* pointer and so provides a means of storing managed references in native code.
Try expanding your code along the following lines:
#include <vcclr.h>
class MyNativeListener: public NativeEventListener
{
private:
gcroot<ManagedResultsObject^> _results;
public:
void onEndProcessing(ProcessingEvent *event)
{
_results->Value = event->value;
//Many more properties to capture
}
};

C++ Class read as variable, default-type int? Say what?

So, I have two classes...Very basic in structure. I try to import one into the other, and declare a new object of that class type...however, it seems to read the class name as the name of a variable?!
The header class provided below will not read the "ApplicationManager" class properly.
Code:
####ifndef _GAME_H_
####define _GAME_H_
####include "application.h"
####include "applicationmanager.h"
class Game : public Application
{
public:
Game();
~Game();
void LoadContent() override;
void UnloadContent() override;
void Draw() override;
private:
//int ApplicationManager; //WHY DOES THIS COMPILE??!
ApplicationManager management; //This DOES NOT WORK?
};
####endif
Here is the header for the "ApplicationManager" class.
Code:
####ifndef _APPMANAGER_H_
####define _APPMANAGER_H_
####include "game.h"
####include "application.h"
class ApplicationManager
{
public:
ApplicationManager(void);
~ApplicationManager(void);
private:
};
####endif
The error that occurs, tells me that I need a ";" before "management", and that "ApplicationManager" is missing a type specifier, so it is assumed to be default-type int.
...any ideas why it won't compile properly? Can someone else try this and report the results? I copied the code, and pasted it in a different solution, to see if something became corrupted....it still didn't work.
You have a cyclic reference. When game.h is included from applicationmanager.h, the ApplicationManager class has not yet been read by the compiler.
To fix, remove the line
#include "game.h"
from applicationmanager.h.
Why do you have circular dependency between Game.h and AppicationManager.h?
Aside from that, I'd say check your header guard (#ifdef _*_H) in Application.h. A fairly often occurence in C++, when copy/pasting code or copying files, is to forget to change the header guard define name for a new class, so you end up with two different headers guarded by the same define. In which case, if both are included into some other file, only the first will actually be expanded into anything useful.
THe error message is some what misleading. It basically says "For some reason (probably an error in the referenced type) I cannot recognize the type you're using (in you case ApplicationManager)".
If you need ApplicationManager to know about Game make a pure virtual base class (interface in other terms) and have Game inherit from that (with out extending the interface) and have ApplicationManager include the base class header file