|
antd the winner is "N a v a n e e t h" ...thank you man...you pointed out the problem...still i'm wondering why i didn't get it by my self
|
|
|
|
|
Winner should be Luc as he pointed out the problem. I am just a syntax provider.
modified on Monday, September 28, 2009 10:22 AM
|
|
|
|
|
yes...but i know only c++ and base for me before today was an unknown identifier... now i know it's a pointer to the "base class" from which i derive mine
|
|
|
|
|
I have a background worker thread which I run to analyse some data the results of which I want to dipslay in the GUI. My question is how do I inform the main GUI thread that the worker thread has finished (the data is ready) so that it can update the GUI. I do not want to pause the main GUI thread to wait for the worker thread.
The only method I know (used back before Managed C++/CLI) is to send a windows message from the worker thread. This is handled in the message loop of the main GUI thread and so the GUI can be updated. However this feels a bit clunky to me and can cause problems when a dialog is open and the windows message is handled in the dialog's message loop and not that of the main application.
Does anybody know of a better method?
|
|
|
|
|
LetsMond wrote: how do I inform the main GUI thread that the worker thread has finished (the data is ready) so that it can update the GUI
Once done, call a method that updates GUI thread. Only thing to take care is to use Invoke/BeginInvoke. Here is a sample code.
MethodInvoker^ method = gcnew MethodInvoker(this, &YourForm::UpdateUI);
this->BeginInvoke(method); This sends a message to the main thread's message loop. You can update the controls inside the method UpdateUI .
MethodInvoker is delegate supplied with .NET framework. If you want to pass additional arguments to method, you may need to create your own delegates.
LetsMond wrote: The only method I know (used back before Managed C++/CLI) is to send a windows message from the worker thread. This is handled in the message loop of the main GUI thread and so the GUI can be updated.
This is the correct method. Message sending is encapsulated inside Invoke or BeginInvoke methods. These methods calls win32 SendMessage and PostMessage .
|
|
|
|
|
Thanks. This will do what I need.
|
|
|
|
|
Good to hear that it helped.
If you are interested, another possible method to do cross thread communication is to use SynchronizationContext[^] class. This will do the marshaling and provide a neat way for cross-thread communication.
Here is a sample code.
SynchronizationContext^ context = SynchronizationContext::Current;
Thread^ t = gcnew Thread(gcnew ParameterizedThreadStart(this, &YourForm::Execute));
t->Start(context);
void Execute(Object^ obj)
{
SynchronizationContext^ context = dynamic_cast<SynchronizationContext^>(obj);
context->Post(gcnew SendOrPostCallback(this, &YourForm::UpdateUI), "Sample text");
}
void UpdateUI(Object^ obj)
{
}
|
|
|
|
|
LetsMond wrote: However this feels a bit clunky to me and can cause problems when a dialog is open and the windows message is handled in the dialog's message loop and not that of the main application.
I didn't get that fully.
Are you saying the updates will not be visible when you have a modal window shown? If yes, that shouldn't be a problem. You will see updates happening to parent form even if a modal windows is shown.
|
|
|
|
|
You can also use a BackgroundWorker . Set the DoWork event to be your worker method, and the WorkerCompleted event to be the method that updates your GUI. I believe the WorkerCompleted event executes on the thread that started the worker thread (which in most cases will be your GUI thread).
Dybs
|
|
|
|
|
dybs wrote: I believe the WorkerCompleted event executes on the thread that started the worker thread
Nope.
Both the ProgressChanged and RunWorkerCompleted event handler will execute on the main (aka GUI) thread, no matter which thread created or started the BackgroundWorker instance. So they basically execute a MainForm.Invoke() for you.
Luc Pattyn
Have a look at my entry for the lean-and-mean competition; please provide comments, feedback, discussion, and don’t forget to vote for it! Thank you.
Local announcement (Antwerp region): Lange Wapper? Neen!
|
|
|
|
|
Thanks for the clarification. I've always been starting a BackgroundWorker from the GUI thread anyway, so I haven't actually tested which thread the WorkerComplete event ran on. Good to know.
Dybs
|
|
|
|
|
Looks like I started a big thread discussion with that question!
I actually ended up using the BackgroundWorker class to solve my problem; however I'm having a slight problem with it. My WorkerComplete event is not always being called from the main GUI thread. I have 4 threads now which use a base class to handle the management of a BackgroundWorker object and then override functions for DoWork and WorkerComplete. In 2 cases the WorkerComplete call comes from the main GUI thread and in the other 2 cases it comes from a completely different worker thread. I'm a bit confused by this as the same code is used to create and start the BackgroundWorker in all 4 cases.
Does anybody have any ideas as to what might cause the WorkerComplete event to be called from a worker thread and not the main GUI thread?
|
|
|
|
|
LetsMond wrote: My WorkerComplete event is not always being called from the main GUI thread
I doubt that very much. what makes you think so?
I suggest you check by logging the value of Thread.CurrentThread.ManagedThreadId
Luc Pattyn
Local announcement (Antwerp region): Lange Wapper? Neen!
|
|
|
|
|
I'm looking in the "Threads" Window to see which thread I'm on when I hit a breakpoint in the RunWorkerCompleted function. In the 2 bad cases I can see that it's not on the main thread (which is helpfully shown in green). Also if I then go on to try and perform a GUI operation things go horribly wrong.
|
|
|
|
|
Hi,
I ran some tests and there seems to be something wrong with BackgroundWorkers.
Contrary to what the documentation says, it seems like the (ProgressChanged and) RunWorkerCompleted event only executes on the GUI thread if the BGW was created (or got started?) on the GUI thread.
My investigation continues...
Luc Pattyn
Local announcement (Antwerp region): Lange Wapper? Neen!
|
|
|
|
|
Thanks for investigating!
I've been looking into the issue and I think I've found the problem, if not a good solution. When the BackgroundWorker doesn't work correctly I have created, displayed and closed a Form before starting the thread. The From changes the current SynchronizationContext object to its own thread. This becomes invalid after the Form closes (it doesn’t change it back to the original SynchronizationContext) and causes the strange behaviour when the BackgroundWorker is used.
The reason I'm seeing this might be because (for various unchangeable reasons) my program is an MFC App which also uses CLI.
I have no idea how to solve the problem. Yet
|
|
|
|
|
Hi,
seems my previous post, which was based on theory (documentation) and limited experience (only created and started on GUI), isn't correct; the BGW does not fire its progress/done events on the GUI event when it wasn't created/started on the GUI thread. I'll investigate further.
Luc Pattyn
Local announcement (Antwerp region): Lange Wapper? Neen!
|
|
|
|
|
Hi there,
In my project I'm creating a Bitmap from a char-ptr (as Scan0 IntPtr).
Now the bitmap (->the char ptr) gets a changed size as a single region of the image
ist cut out. If the stride is not a multiple of four, the createbitmap function causes
an exception! Now what can I do? I have no possiblity to change the incoming char-ptr
(because I get it sent on my interface).
If I calculate the next %4 stride value and use it, my bitmap isn't displayed correctly.
BTW: It's a monochrome image with a manually assigned MonoPalette, 1 BytePerPixel: Format8bppIndexed
Thanks for always helping me so much! Special thanks to NAVANEETH!
************************************************
A little side question:
From time to time my windows forms application is hanging in a strange way. I make a snapshot of a bitmap
and display it in one of the two picture boxes. Now when I drag my mouse down on the forms, i. e. to maximize/minimize
the form, there is no reaction and the application "hangs".
My app is not run as an exe, it's loaded as a DLL from a superior application.
It also occurs, that I am in the "main app" to calculate the image, then call the forms DLL to display it, but I do not
get the "forms application" (although it is loaded) popped up from the task bar. Usually I have to click on the "forms app"
in the taskbar -> then send an image from the main app -> and then the forms project gets popped up (you see, by aiming to
popping it up before). But that is crap and shouldn't behave that way!
modified on Friday, September 11, 2009 8:39 AM
|
|
|
|
|
cherrymotion wrote: I have no possiblity to change the incoming char-ptr
(because I get it sent on my interface).
Then you need to copy the pixel data to a properly row-aligned array.
You could use the Bitmap(Int32, Int32, PixelFormat) constructor and
copy the incoming data row-by-row to the new bitmap's data.
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
Hi,
I'm a newbie to VC++ using Visual Studio 2005 and facing the below mentioned problem
Read many articles over the internet but of less help.
Thing is i am stuck at using managed and managed functions at a time.
Have pasted the .lib code and application which demonstates the issue
.lib code:
int unmngd_sample(void)
{
return 1;
}
application code:
#pragma once
#include"unmngd.h"
namespace mngd {
using namespace System;
using namespace System::ComponentModel;
using namespace System::Collections;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Drawing;
/// <summary>
/// Summary for Form1
///
/// WARNING: If you change the name of this class, you will need to change the
/// 'Resource File Name' property for the managed resource compiler tool
/// associated with all .resx files this class depends on. Otherwise,
/// the designers will not be able to interact properly with localized
/// resources associated with this form.
/// </summary>
public ref class Form1 : public System::Windows::Forms::Form
{
public:
Form1(void)
{
InitializeComponent();
//
//TODO: Add the constructor code here
//
}
protected:
/// <summary>
/// Clean up any resources being used.
/// </summary>
~Form1()
{
if (components)
{
delete components;
}
}
protected:
public: static System::Windows::Forms::RichTextBox^ Console;
private:
/// <summary>
/// Required designer variable.
/// </summary>
System::ComponentModel::Container ^components;
#pragma region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
void InitializeComponent(void)
{
this->Console = (gcnew System::Windows::Forms::RichTextBox());
this->SuspendLayout();
//
// Console
//
this->Console->Anchor = static_cast<System::Windows::Forms::AnchorStyles>((((System::Windows::Forms::AnchorStyles::Top | System::Windows::Forms::AnchorStyles::Bottom)
| System::Windows::Forms::AnchorStyles::Left)
| System::Windows::Forms::AnchorStyles::Right));
this->Console->Enabled = false;
this->Console->Location = System::Drawing::Point(0, 24);
this->Console->Name = L"Console";
this->Console->Size = System::Drawing::Size(788, 556);
this->Console->TabIndex = 2;
this->Console->Text = L"";
//
// Form1
//
this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
this->ClientSize = System::Drawing::Size(292, 262);
this->Controls->Add(this->Console);
this->Name = L"Form1";
this->Text = L"Form1";
this->ResumeLayout(false);
this->PerformLayout();
}
#pragma endregion
int func2(void)
{
return unmngd_sample();
}
};
int func1(const char *p, ...)
{
Form1::Console->AppendText("A");
return 1;
}
}
I've got a library unmngd.lib (unmanaged function), which is included in the GUI based application (managed func).
Enclosed unmngd.lib is unmanaged function and mixing is the application using managed functions.
Problem im facing is
1> if project->properties->
configuration properties->general->"Common Language Runtime support" is set to clr,
managed type or func cannot be used in an unmanaged func1(in form1.h").
Error: 'mngd::Form1': managed type or function cannot be used in an unmanaged function
2> if project->properties->configuration properties->general->"Common Language Runtime support" is set to clr/pure,
im not able to call unmanaged code(from .lib) in func2 (managed func).
Pls throw some light on this
|
|
|
|
|
You are getting this error because of the variable argument method. You can't have managed automatic variables inside a method that takes variable arguments. In your code I don't see any reason for using it. Try removing it
int func1(const char *p)
{
Form1::Console->AppendText("A");
return 1;
} There are potential problems with your code.
- Exposing a private variable violates OO rules. You should not expose the variable
console to outside. static keyword has a different implementation in managed world and it is not like the native implementation. In .NET, static variables will not be collected and will live until the application domain unloads.- Using
Form1::Console is dangerous. When Form1 is never instantiated before this call, you will run into troubles. As a solution, avoid using static members. Create a public method on Form1 which takes a string and call console->AppendText . Use this from func1 . func1 should get an instance of Form1 to do this.
Where are you calling func1 ?
|
|
|
|
|
Hey Navaneeth,
Let me brief you the situation. I've got a static lib file which is a win32 based and which requires application to register its display function address with the lib.
Library has few functions which keeps displaying the intermediate status of the functions called and hence the variable argument function required.
Earlier this library was used by the commandline application. Now I need to develop a GUI based application using the same lib. That is why i define variable argument function and register it with the library. This variable argument function which receives the messages from library, displays it on the Console.
Thanks a lot.
modified on Wednesday, September 9, 2009 2:01 AM
|
|
|
|
|
santoshkaif wrote: This variable argument function which receives the messages from library, displays it on the Console.
What will be the value of arguement p in your function int func1(const char *p, ...) ? Will that be some identifier to identify the types of variable arguements? If yes a possible solution is to write several method overloads that takes all possible types you are expecting. Depending on the type, you need call each overload. Also the way you implemented writing to rich text box is bad because of the reasons mentioned in my last post. Following is an improved version of your Form1 code.
public ref class Form1 : public System::Windows::Forms::Form
{
public:
static Form1^ GetInstance()
{
if(instance == nullptr)
instance = gcnew Form1();
return instance;
}
void PrintValue(System::Object value)
{
Console->AppendText(value.ToString());
}
private:
static Form1^ instance = nullptr;
System::Windows::Forms::RichTextBox^ Console;
} I have changed the Console variable to private and added a static variable named instance . This variable can be accessed using GetInstance method.
Following overloaded free functions are also added.
void WriteToScreen(int value)
{
Form1^ form = Form1::GetInstance();
form->PrintValue(value);
}
void WriteToScreen(double value)
{
Form1^ form = Form1::GetInstance();
form->PrintValue(value);
} Consider the following implementation of func1 . I am assuming in a call like func1("id", 10, 10.50) first argument will be int and second will be double.
void func1(const char* p,...)
{
va_list variable_arguements;
const char *temp;
va_start(variable_arguements, p);
int iValue;
double dValue;
for (temp = p; *temp; temp++)
{
switch (*temp)
{
case 'i':
iValue = va_arg(variable_arguements, int);
WriteToScreen(iValue);
break;
case 'd':
dValue = va_arg(variable_arguements, double);
WriteToScreen(dValue);
break;
default:
break;
}
}
va_end(variable_arguements);
} Finally, You need to modify your main method to use the singleton instance.
int main(array<System::String ^> ^args)
{
Application::EnableVisualStyles();
Application::SetCompatibleTextRenderingDefault(false);
Form1^ frm = Form1::GetInstance();
Application::Run(frm);
return 0;
} Hope that helps
|
|
|
|
|
Thanks, for a newbie this is a help to greater extent.
One more thing imp is I'm using visual studio 2005.
>> What will be the value of arguement p in your function int func1(const char *p, ...)?
Value of argument p in function int func1(const char *p, ...) can be anything. Its as good as printf(). I've built the sample library and application project and is as below
library.h
--------------------
#ifndef _LIBRARY_H_
#define _LIBRARY_H_
void register_func(int(*pConsoleHandle)(const char* p, ...));
void library_message1(void);
void library_message2(void);
void library_message3(void);
void library_operation(void);
#endif //_LIBRARY_H_
library.c
-----------------------
#include"library.h"
int(*pFuncHandle)(const char* p, ...);
void register_func(int(*pConsoleHandle)(const char* p, ...))
{
pFuncHandle = pConsoleHandle;
}
void library_message1(void)
{
pFuncHandle("Library msg1 is up!!");
}
void library_message2(void)
{
char msg[] = "Library msg2";
pFuncHandle("Library is up!!");
}
void library_message3(void)
{
int i = 3;
pFuncHandle("Library msg%d is up!!",i);
}
void library_operation(void)
{
library_message1();
library_message2();
library_message3();
}
this code generates library.lib which is to be included in GUI application.
GUI_Application:
-----------------------
#pragma once
#include <stdio.h>
#include <stdarg.h>
#include "library.h"
namespace GUI_APP {
using namespace System;
using namespace System::ComponentModel;
using namespace System::Collections;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Drawing;
int DisplayMessage(const char* msg, ...);
public ref class Form1 : public System::Windows::Forms::Form
{
public:
Form1(void)
{
InitializeComponent();
register_func(DisplayMessage);
}
protected:
~Form1()
{
if (components)
{
delete components;
}
}
public: static System::Windows::Forms::RichTextBox^ Console;
protected:
private:
System::ComponentModel::Container ^components;
#pragma region Windows Form Designer generated code
void InitializeComponent(void)
{
this->Console = (gcnew System::Windows::Forms::RichTextBox());
this->SuspendLayout();
this->Console->Location = System::Drawing::Point(-2, -2);
this->Console->Name = L"richTextBox1";
this->Console->Size = System::Drawing::Size(296, 265);
this->Console->TabIndex = 0;
this->Console->Text = L"";
this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
this->ClientSize = System::Drawing::Size(292, 262);
this->Controls->Add(this->Console);
this->Name = L"Form1";
this->Text = L"Form1";
this->ResumeLayout(false);
}
#pragma endregion
void library_func(void)
{
library_operation();
}
};
int DisplayMessage(const char* msg, ...)
{
int ret = 0;
va_list ap;
char *pBuffer = new char[256];
va_start(ap, msg);
ret = vsprintf(pBuffer, msg, ap);
GUI_APP::Form1::Console->AppendText(gcnew System::String((const char*)msg));
delete(pBuffer);
va_end(ap);
return (ret);
}
}
This GUI_application is where i'm unable to call and use unmanaged functions.
Thanks in advance
modified on Wednesday, September 9, 2009 2:52 PM
|
|
|
|
|
Well in that case we can make use of std::stringstream and have only one overload. You need to modify the PrintValue method in your form to take a managed string rather than System::Object .
void PrintValue(String^ value)
{
Console->AppendText(value);
} WriteToScreen method now requires only one overload which takes a std::stringstream& .
void WriteToScreen(std::stringstream& value)
{
std::string str = stream.str();
String^ managedString = gcnew String(str.c_str());
Form1^ form = Form1::GetInstance();
form->PrintValue(managedString);
} func1 is also modified to behave like printf .
void func1(const char* p,...)
{
const char *temp; int iValue; double dValue; bool percentageFound = false;
va_list variable_arguements;
va_start(variable_arguements, p);
std::stringstream stream;
for (temp = p; *temp; temp++)
{
if(*temp != '%' && !percentageFound)
stream << *temp;
else if(!percentageFound)
{
percentageFound = true;
continue;
}
if(percentageFound)
{
switch (*temp)
{
case 'd':
iValue = va_arg(variable_arguements, int);
stream << iValue;
break;
default:
break;
}
percentageFound = false;
}
}
va_end(variable_arguements);
WriteToScreen(stream);
} I have added only for %d and you can add cases for all other types. A function call like func1("Twelve = %d and Ten = %d",12,10); will print Twelve = 12 and Ten = 10 .
Hope this helps.
|
|
|
|