Unable to assigned a member function from a managed class (C++/CLI) to C++ unmanaged class Hi,
I have been working on a project where I developed a ClassA in C++ has a function pointer (mCallback_reportProgress) as a call back function (or delegate) to report its progress. This function is assigned via another public function - RegisterCallback_reportProgress. There are actually 2 of them - one for any regular function and one is a template that is used to register a member function for a given class instance.
This code working
ClassA {
public:
template<typename CType>
bool RegisterCallback_reportProgress(void (CType::* DelegateFunction_)(int, size_t, float), CType& Class_) //member function version
{
if (mWritingStarted.load()) return false;
if (DelegateFunction_ == NULL) return false;
RegisterCallback_reportProgress(std::bind(DelegateFunction_, &Class_, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
return true;
}
bool RegisterCallback_reportProgress(std::function<void(int, size_t, float)> DelegateFunction_);
void DoProcess()
{
while (DataToProcess)
{
//Do some processing
mCallback_reportProgress(ID, NumberOfItems, percentageValue);
}
}
private:
std::function<void(int, size_t, float)> mCallback_reportProgress;
}
Please note this is a simplified version of my actual class.
So now I have it working in C++ console and I decided to do a GUI form around it. Decided to use CLR using C++/CLI in a different project - great way to do GUI using a C# style way. Created it and then added in ClassA (both header and Cpp file).
Here is the Form1 I created.
public ref class Form1 : public System::Windows::Forms::Form
{
public:
Form1(void)
{
InitializeComponent();
//
//TODO: Add the constructor code here
//
}
public:
void ProgressBarUpdate(int ThreadID_, size_t CountOfNumbersFound_, float ProgressPercentage_)
{
//Assign some progress percentage to a GUI element.
}
protected:
/// <summary>
/// Clean up any resources being used.
/// </summary>
~Form1()
{
if (components)
{
delete components;
}
}
}
The real issue occures when I tried to register the form's reporting function (ProgressBarUpdate) to ClassA's RegisterCallback_reportProgress.
#include "Form1.h"
#include "ClassA.h"
using namespace System::Windows::Forms;
delegate void AsyncRunCaller(Form form);
[STAThread]
int main()
{
ClassA ProcessingInstance;
Application::EnableVisualStyles();
Application::SetCompatibleTextRenderingDefault(false);
CppCLRWinFormsProject::Form^ FormApplication = gcnew CppCLRWinFormsProject::Form1(); //Interesting, it has to be only created AFTER SetCompatibleTextRenderingDefault.
//If BEFORE, Run will throw an error.
ProcessingInstance.RegisterThreadCallbackPercentage(&CppCLRWinFormsProject::Form1::ProgressBarUpdate, FormApplication); //Here is the part where I tried to register ProgressBarUpdate to mCallback_reportProgress in Class A
Application::Run(FormApplication);
return 0;
}
This is where it gives the errorm with the error "C++ a pointer-to-member is not valid for a managed class".
ProcessingInstance.RegisterThreadCallbackPercentage(&CppCLRWinFormsProject::Form1::ProgressBarUpdate, FormApplication);
I generally understand why it fails - it won't let my unmanaged class have the pointer to a member function for a managed class instance to prevent the unmanaged class from calling in case the managed class is GC or garbage collected later which would cause an error.
But I don't know how to marshel it so that it ClassA can register the callback function to the managed class. I have been searching around Stack Overflow but none of the solutions work or are not viable to my predicament.
Is there even a way to do that or do I need to create a separate ClassA that accepts delegate using C++/CLI? I would like to continue to use the same Class for both regular C++ and C++/CLI.
Thank you for your time and effort.
I had tried to get the function ProgressBarUpdate to registered with ClassA's RegisterCallback_reportProgress function. But I am not sure how to handle it when it comes to managed classes.
It seems my question didn't get an answer so I had to find an answer on my own but thanks to this post here c++/CLI error in GChandle I managed to fashion an answer to my problem. I have posted it here for everyone else in the future later on.
I created a wrapper class around ClassA. I know there was mention of using lambda function and gcroot but I couldn't understand the text I found on the matter and a lot of searching didn't yield anything I could understand.
Header file CWrapperClassA.h
namespace Wrapper {
using namespace System::Runtime::InteropServices;
using namespace System;
delegate void ProgressUpdateEventHandler(int, size_t, float);
public ref class CWrapperClassA
{
private:
ClassA* UnmanagedClass1obj;
GCHandle delegateHandle_;
CWrapperClassA(void);
delegate void EventDelegate(int ID, size_t CountOfNumberFound, float ProgressPercent);
EventDelegate^ functionNativeCallback_;
void ProgressUpdate(int ID, size_t CountOfNumberFound, float ProgressPercent);
public:
CWrapperClassA(int IDCount);
event ProgressUpdateEventHandler^ EventUpdate;
};
}
Cpp file for CWrapperClassA.cpp
#include "CWrapperClassA.h"
Wrapper::CWrapperClassA::CWrapperClassA(void)
{};
Wrapper::CWrapperClassA::CWrapperClassA(int IDCount)
{
UnmanagedClass1obj = new ClassA(ThreadCount);
functionNativeCallback_ = gcnew EventDelegate(this, &CWrapperClassA::ProgressUpdate);
// As long as this handle is alive, the GC will not move or collect the delegate
// This is important, because moving or collecting invalidate the pointer
// that is passed to the native function below
delegateHandle_ = GCHandle::Alloc(functionNativeCallback_);
// This line will actually get the pointer that can be passed to
// native code
IntPtr ptr = Marshal::GetFunctionPointerForDelegate(functionNativeCallback_);
// Convert the pointer to the type required by the native code
UnmanagedClass1obj->RegisterCallback_reportProgress(static_cast<void(__stdcall*) (int, size_t, float)>(ptr.ToPointer()));
}
void Wrapper::CWrapperClassA::ProgressUpdate(int ThreadId, size_t CountOfNumberFound, float ProgressPercent)
{
EventUpdate(ThreadId, CountOfNumberFound, ProgressPercent);
}
So in the form1's constructor I create the Wrapper Class and subscribe the form1's ProgressBarUpdate to CWrapperClassA Event handler. So anytime ClassA class the mCallback_reportProgress, the value pass to the function will be pass to CppCLRWinFormsProject::Form1::ProgressBarUpdate member function via the event handler and I can "add" or subscribe (C# delegate speak)
public ref class Form1 : public System::Windows::Forms::Form
{
public:
Form1(void)
{
InitializeComponent();
//
//TODO: Add the constructor code here
//
mWrapperForClassAInstance = gcnew Wrapper::CWrapperClassA(4);
mWrapperForClassAInstance->EventUpdate += gcnew Wrapper::ProgressUpdateEventHandler(this, &CppCLRWinFormsProject::Form1::ProgressBarUpdate); //This is the line that register form1's ProgressBarUpdate to ClassA wrapper
}
public:
void ProgressBarUpdate(int ThreadID_, size_t CountOfNumbersFound_, float ProgressPercentage_)
{
//Assign some progress percentage to a GUI element.
}
protected:
/// <summary>
/// Clean up any resources being used.
/// </summary>
~Form1()
{
if (components)
{
delete components;
}
}
private:
Wrapper::CWrapperClassA mWrapperForAClassInstance;
}
I have tested the codes in my project and it works! Please note that the solution I presented is a simplified version AND I know there are definitely better solution than creating wrapper classes but I couldn't find one and with the information I have, this was the best solution I could find.
I hope this helps someone else who are also facing the same issue.
If someone else can show me a better solution I would be grateful as this isn't an elegent solution with so much coding and I know I would need to create a public method in CWrapperClassA to access any function in ClassA but I can accept it for until I can find a better solution.