Access controls from other modules of a program
-
I created a Windows Forms Application, put a TextBox and a Button on it. When I press the Button its event handler calls a function in another CPP module in my project that does the whole bunch of work. As it is doing that work I want it to send some progress text messages to the TextBox on the Form. How can I do this? This was a simple task before as I just used pointers, but with Managed C++ I just can't figure it out. Can somebody help, please?
-
I created a Windows Forms Application, put a TextBox and a Button on it. When I press the Button its event handler calls a function in another CPP module in my project that does the whole bunch of work. As it is doing that work I want it to send some progress text messages to the TextBox on the Form. How can I do this? This was a simple task before as I just used pointers, but with Managed C++ I just can't figure it out. Can somebody help, please?
Take a look at
GetFunctionPointerForDelegate()
in MSDN. You define adelegate
in managed code, get a function pointer for it, cast it as function pointer for the native function, and then hand it off to the native code to use as a callback into your managed code. -
Take a look at
GetFunctionPointerForDelegate()
in MSDN. You define adelegate
in managed code, get a function pointer for it, cast it as function pointer for the native function, and then hand it off to the native code to use as a callback into your managed code.Michael, thanks a lot for your reply. I did try delegates even before my desperate call for help and now I tried it again, but still no luck. Here is what I put on top of my Form1.h:
delegate void MyFormMethod (void); // declare the delegate. typedef void (*PFUNC)(void); // type for the method I want to call from outside. PFUNC pFunc (void); // actually create a public pointer for my method.
Then in the form's constructor I do this:public: Form1(void) { InitializeComponent(); MyFormMethod^ m = gcnew MyFormMethod(this, &Form1::MyMethod); // create instance of my delegate. IntPtr pint = Marshal::GetFunctionPointerForDelegate (m); // get the pointer my method. PFUNC pFunc = (PFUNC)pint.ToPointer (); // save it in the global pointer. }
So far so good, but if I try to use pFunc () anywhere in the code the program does not link. Even if I use it in the same Form's Button handler I get an error:error LNK2028: unresolved token (0A00000A) "void (__clrcall*__clrcall pFunc(void))(void)" (?pFunc@@$$FYMP6MXXZXZ) referenced in function "private: void __clrcall My2::Form1::button1_Click(class System::Object ^,class System::EventArgs ^)" (?button1_Click@Form1@My2@@$$FA$AAMXP$AAVObject@System@@P$AAVEventArgs@4@@Z)
There is just no simple example anywhere that I could start from. I saw the console app examples where they create a delegate in the main() function and then use it in that main() function. That works, but when I try to move the same concept into my form...:confused: There should be some obvious problem in my understanding of things, could you please point me to it? Many thanks! Eugene -
Michael, thanks a lot for your reply. I did try delegates even before my desperate call for help and now I tried it again, but still no luck. Here is what I put on top of my Form1.h:
delegate void MyFormMethod (void); // declare the delegate. typedef void (*PFUNC)(void); // type for the method I want to call from outside. PFUNC pFunc (void); // actually create a public pointer for my method.
Then in the form's constructor I do this:public: Form1(void) { InitializeComponent(); MyFormMethod^ m = gcnew MyFormMethod(this, &Form1::MyMethod); // create instance of my delegate. IntPtr pint = Marshal::GetFunctionPointerForDelegate (m); // get the pointer my method. PFUNC pFunc = (PFUNC)pint.ToPointer (); // save it in the global pointer. }
So far so good, but if I try to use pFunc () anywhere in the code the program does not link. Even if I use it in the same Form's Button handler I get an error:error LNK2028: unresolved token (0A00000A) "void (__clrcall*__clrcall pFunc(void))(void)" (?pFunc@@$$FYMP6MXXZXZ) referenced in function "private: void __clrcall My2::Form1::button1_Click(class System::Object ^,class System::EventArgs ^)" (?button1_Click@Form1@My2@@$$FA$AAMXP$AAVObject@System@@P$AAVEventArgs@4@@Z)
There is just no simple example anywhere that I could start from. I saw the console app examples where they create a delegate in the main() function and then use it in that main() function. That works, but when I try to move the same concept into my form...:confused: There should be some obvious problem in my understanding of things, could you please point me to it? Many thanks! EugeneI highly recommend getting Nishant Sivakumar's "C++/CLI In Action" book if you're looking for examples. He covers a lot of confusing stuff with remarkable clarity and brevity. How are you using
pFunc
? It's a pointer for the native code to use. If you want to use a native function pointer, you'd useGetDelegateForFunctionPointer()
in managed code. Also, I'm not certain, but I'm concerned about the scope of your variables. Whenm
goes out of the scope ofForm1(void)
it may be garbage collected. -
I highly recommend getting Nishant Sivakumar's "C++/CLI In Action" book if you're looking for examples. He covers a lot of confusing stuff with remarkable clarity and brevity. How are you using
pFunc
? It's a pointer for the native code to use. If you want to use a native function pointer, you'd useGetDelegateForFunctionPointer()
in managed code. Also, I'm not certain, but I'm concerned about the scope of your variables. Whenm
goes out of the scope ofForm1(void)
it may be garbage collected.Thanks, the book I am working with now is "Pro Visual C++/CLI" by Stephen Fraser, but I will look into the book you recommend too. I see, I guess I am trying to use pFunc() in the managed code itself. But you know, it actually works if I call pFunc() right at the end of the Form constructor. I get a warning message box that it is unsafe to call function pointers from managed code, but when I click "Continue" the pFunc() is actually called, it does its work and returns properly. However, if I call pFunc() from the Button handler of the Form or from elsewhere the project does not even build:
public:
Form1(void)
{
InitializeComponent();MyFormMethod^ m = gcnew MyFormMethod(this, &Form1::MyMethod); // create instance of my delegate. IntPtr pint = Marshal::GetFunctionPointerForDelegate (m); // get the pointer my method. PFUNC pFunc = (PFUNC)pint.ToPointer (); // save it in the global pointer. `**pFunc();** // works.` }
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) {
**pFunc();** // does not link, unresolved external pFunc.
}It's a good point about
m
garbage collected at some point. Although it should be OK since I only needed it at the time I extracted pointer out of it. Then the pointer is stored in pFunc, which is global and should not get deallocated. By the way, should we be even talking about calls between managed/unmanaged code here? My test app in my example is just a plain WinFormsApp "Hello World" example (should be all "managed") with a few mods: I put functionfoo()
in my WinFormsApp.CPP with the code to access something on the form (in Form1.h), say atextBox
. I press the button to callfoo()
andfoo()
writes some text, that's it. I cannot useForm1::textBox::Text = "Text"
infoo()
, I tried to create a new public method in my Form that doesthis->textBox->Text = "Text"
, but the compiler says "cannot access a non-static member". OK, I make my method static but then I cannot access the textBox from it anymore:error C2597: illegal reference to non-static member 'System::Windows::Forms::Form::Text'
. Of course, I will keep reading and searching for the example that does exactly what I am trying to do, but the solution to my problem should be very simple. Maybe it is so simple that they do n -
Thanks, the book I am working with now is "Pro Visual C++/CLI" by Stephen Fraser, but I will look into the book you recommend too. I see, I guess I am trying to use pFunc() in the managed code itself. But you know, it actually works if I call pFunc() right at the end of the Form constructor. I get a warning message box that it is unsafe to call function pointers from managed code, but when I click "Continue" the pFunc() is actually called, it does its work and returns properly. However, if I call pFunc() from the Button handler of the Form or from elsewhere the project does not even build:
public:
Form1(void)
{
InitializeComponent();MyFormMethod^ m = gcnew MyFormMethod(this, &Form1::MyMethod); // create instance of my delegate. IntPtr pint = Marshal::GetFunctionPointerForDelegate (m); // get the pointer my method. PFUNC pFunc = (PFUNC)pint.ToPointer (); // save it in the global pointer. `**pFunc();** // works.` }
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) {
**pFunc();** // does not link, unresolved external pFunc.
}It's a good point about
m
garbage collected at some point. Although it should be OK since I only needed it at the time I extracted pointer out of it. Then the pointer is stored in pFunc, which is global and should not get deallocated. By the way, should we be even talking about calls between managed/unmanaged code here? My test app in my example is just a plain WinFormsApp "Hello World" example (should be all "managed") with a few mods: I put functionfoo()
in my WinFormsApp.CPP with the code to access something on the form (in Form1.h), say atextBox
. I press the button to callfoo()
andfoo()
writes some text, that's it. I cannot useForm1::textBox::Text = "Text"
infoo()
, I tried to create a new public method in my Form that doesthis->textBox->Text = "Text"
, but the compiler says "cannot access a non-static member". OK, I make my method static but then I cannot access the textBox from it anymore:error C2597: illegal reference to non-static member 'System::Windows::Forms::Form::Text'
. Of course, I will keep reading and searching for the example that does exactly what I am trying to do, but the solution to my problem should be very simple. Maybe it is so simple that they do nI'm sorry. I took "This was a simple task before as I just used pointers" to mean that you had existing native C++ code calling the C++ module that does all the work and were moving that existing code into C++/CLI. In your case, you can define an
event
to update the text:delegate void UpdateText( String^ );
ref class SomeClass
{
public:
event UpdateText^ OnUpdateText;
};public ref class Form1 : Form
{
public:Form1() { InitializeComponent(); someClass\_ = gcnew SomeClass(); someClass\_->OnUpdateText += gcnew UpdateText( this, &Form1::UpdateTextHandler ); }
private:
SomeClass^ someClass\_; void UpdateTextHandler( String^ text ) { textBox->Text = text; }
};
In the implementation of
SomeClass
, callOnUpdateText( "New text" )
when you want to update the text. -- modified at 22:59 Monday 13th August, 2007 [Some corrections made to above code.] -
I'm sorry. I took "This was a simple task before as I just used pointers" to mean that you had existing native C++ code calling the C++ module that does all the work and were moving that existing code into C++/CLI. In your case, you can define an
event
to update the text:delegate void UpdateText( String^ );
ref class SomeClass
{
public:
event UpdateText^ OnUpdateText;
};public ref class Form1 : Form
{
public:Form1() { InitializeComponent(); someClass\_ = gcnew SomeClass(); someClass\_->OnUpdateText += gcnew UpdateText( this, &Form1::UpdateTextHandler ); }
private:
SomeClass^ someClass\_; void UpdateTextHandler( String^ text ) { textBox->Text = text; }
};
In the implementation of
SomeClass
, callOnUpdateText( "New text" )
when you want to update the text. -- modified at 22:59 Monday 13th August, 2007 [Some corrections made to above code.]Well, it did not work exactly as was written, but certainly led me to the working code, thank you very much! However, the solution I finally got seems ugly to me:
// I added public here so compiler stops giving a warning:
// 'OnUpdateText': signature of non-private member contains assembly private
// type 'EventTest::UpdateText'.
public
delegate void UpdateText( String^ );// Needed to add public here so I can use this class as a base class later.
public
ref class SomeClass
{
public:
event UpdateText^ OnUpdateText;
};public ref class Form1 : Form
{
public:
Form1()
{
InitializeComponent();
someClass_ = gcnew SomeClass();
someClass_->OnUpdateText += gcnew UpdateText( UpdateTextHandler );
}private:
SomeClass^ someClass_;
void UpdateTextHandler( String^ text )
{
textBox->Text = text;
}// This is just my button handler. It calls foo() located in MyCode.CPP, which calls
// MyClass_UpdateText(), which fires an OnUpdateText event.
System::Void button1_Click (System::Object^ sender, System::EventArgs^ e) {
void foo (String^ Text);
foo ("Text for textBox");
}};
Now here is MyCode.CPP:
#include "stdafx.h"
#include "Form1.h"using namespace EventTest;
// I had to add MyClass based on SomeClass as otherwise the compiler gives the error:
// error C3767: 'EventTest::SomeClass::OnUpdateText::raise': candidate function(s) not accessible. I
// looked up the error at MSDN: "Compiler Error C3767" and they had the example to avoid it via an extra
// class MyClass.public ref class MyClass: public SomeClass { public: void MyClass_UpdateText (String^ Text) { SomeClass^ EventKeeper = gcnew SomeClass; EventKeeper->OnUpdateText (Text); } };
void foo (String^ Text) {
MyClass^ y = gcnew MyClass();
y->MyClass_UpdateText (Text);
}Question #1: does it really have to be that twisted-complicated or it is just me being a beginner? To update a property on a Form from outside the Form I need to create and delegate an event, that has to be a member of SomeClass. Then to fire this event we have to create a new MyClass based on SomeClass with the actual event handler code and create an instance of it. And only then we can raise this event. Wow! Did I
-
Well, it did not work exactly as was written, but certainly led me to the working code, thank you very much! However, the solution I finally got seems ugly to me:
// I added public here so compiler stops giving a warning:
// 'OnUpdateText': signature of non-private member contains assembly private
// type 'EventTest::UpdateText'.
public
delegate void UpdateText( String^ );// Needed to add public here so I can use this class as a base class later.
public
ref class SomeClass
{
public:
event UpdateText^ OnUpdateText;
};public ref class Form1 : Form
{
public:
Form1()
{
InitializeComponent();
someClass_ = gcnew SomeClass();
someClass_->OnUpdateText += gcnew UpdateText( UpdateTextHandler );
}private:
SomeClass^ someClass_;
void UpdateTextHandler( String^ text )
{
textBox->Text = text;
}// This is just my button handler. It calls foo() located in MyCode.CPP, which calls
// MyClass_UpdateText(), which fires an OnUpdateText event.
System::Void button1_Click (System::Object^ sender, System::EventArgs^ e) {
void foo (String^ Text);
foo ("Text for textBox");
}};
Now here is MyCode.CPP:
#include "stdafx.h"
#include "Form1.h"using namespace EventTest;
// I had to add MyClass based on SomeClass as otherwise the compiler gives the error:
// error C3767: 'EventTest::SomeClass::OnUpdateText::raise': candidate function(s) not accessible. I
// looked up the error at MSDN: "Compiler Error C3767" and they had the example to avoid it via an extra
// class MyClass.public ref class MyClass: public SomeClass { public: void MyClass_UpdateText (String^ Text) { SomeClass^ EventKeeper = gcnew SomeClass; EventKeeper->OnUpdateText (Text); } };
void foo (String^ Text) {
MyClass^ y = gcnew MyClass();
y->MyClass_UpdateText (Text);
}Question #1: does it really have to be that twisted-complicated or it is just me being a beginner? To update a property on a Form from outside the Form I need to create and delegate an event, that has to be a member of SomeClass. Then to fire this event we have to create a new MyClass based on SomeClass with the actual event handler code and create an instance of it. And only then we can raise this event. Wow! Did I
You are definitely doing too much regarding Question #1. You don't need to create an object every time you need to fire an event. I'm sorry my example code wasn't better. I just typed it into a text editor. I should have grabbed it from a working project. Regarding Question #2, there's definitely a better way. I just stuck everything into one place for simplicity. I'd only confuse you more if I tried to describe a better way, so I'll make a working example in C++/CLI and send that to you. -- modified at 22:39 Monday 13th August, 2007 To be a little clearer and more correct, here's the form class with irrelevant stuff stripped out:
#include "WorkerClass.h"
namespace WinApp1
{
public ref class Form1 : public Form
{
public:Form1() { InitializeComponent(); workerClass\_ = gcnew WorkerClass(); workerClass\_->OnUpdateText += gcnew UpdateText( this, &Form1::UpdateTextHandler ); }
private:
TextBox^ textBox\_; Button^ button\_; WorkerClass^ workerClass\_; void button\_\_Click( System::Object^ sender, System::EventArgs^ e ) { workerClass\_->DoSomething(); } void UpdateTextHandler( String^ text ) { textBox\_->Text = text; }
};
}And, in another file called WorkerClass.h, here's
WorkerClass
, which wasSomeClass
in my previous example:namespace WinApp1
{
delegate void UpdateText( String^ );ref class WorkerClass
{
public:event UpdateText^ OnUpdateText; void DoSomething() { ++serialNumber\_; OnUpdateText( Convert::ToString( serialNumber\_ ) ); }
private:
int serialNumber\_;
};
}You don't need to derive your class from
WorkerClass
. All you need is anevent
of typeUpdateText^
to whichForm1
can attach a handler. -
You are definitely doing too much regarding Question #1. You don't need to create an object every time you need to fire an event. I'm sorry my example code wasn't better. I just typed it into a text editor. I should have grabbed it from a working project. Regarding Question #2, there's definitely a better way. I just stuck everything into one place for simplicity. I'd only confuse you more if I tried to describe a better way, so I'll make a working example in C++/CLI and send that to you. -- modified at 22:39 Monday 13th August, 2007 To be a little clearer and more correct, here's the form class with irrelevant stuff stripped out:
#include "WorkerClass.h"
namespace WinApp1
{
public ref class Form1 : public Form
{
public:Form1() { InitializeComponent(); workerClass\_ = gcnew WorkerClass(); workerClass\_->OnUpdateText += gcnew UpdateText( this, &Form1::UpdateTextHandler ); }
private:
TextBox^ textBox\_; Button^ button\_; WorkerClass^ workerClass\_; void button\_\_Click( System::Object^ sender, System::EventArgs^ e ) { workerClass\_->DoSomething(); } void UpdateTextHandler( String^ text ) { textBox\_->Text = text; }
};
}And, in another file called WorkerClass.h, here's
WorkerClass
, which wasSomeClass
in my previous example:namespace WinApp1
{
delegate void UpdateText( String^ );ref class WorkerClass
{
public:event UpdateText^ OnUpdateText; void DoSomething() { ++serialNumber\_; OnUpdateText( Convert::ToString( serialNumber\_ ) ); }
private:
int serialNumber\_;
};
}You don't need to derive your class from
WorkerClass
. All you need is anevent
of typeUpdateText^
to whichForm1
can attach a handler.