473,396 Members | 2,129 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,396 software developers and data experts.

Create Form from Async-Callback

Hi all

I stumbled over a new problem:
I have a programm with just a class that is asynchronous listening for
network connections. As soon as someone connected, a new form needs to be
created.
The Form gets created but hangs after creation. I'ts logical that that
happens because the new Form doesn't get a Message Loop.
The Message Loop is created on Application.Run() on the Main Method on the
Main Thread. The Callback is (normally) executed on another Thread so the
Form which is created there doesn't have this Message Loop and therefore
hangs.

Basically I would need to run the creation-code of the new Form on the Main
Thread. But how? I don't have a Form to call Invoke. Well I could make a
dummy form and pass that one to my Callback and invoke it there but that
doesn't seem very clean. I just need to somehow execute some code on the Main
Thread so that the Form is created there and can use the MessageLoop that is
waiting there.

Any Ideas how to solve that in a proper way?

At the end, you'll find a very simple example showing the problem. Form1 is
just needed that I can connect to the Application itself. You could also
comment out the Form1 Stuff and just use "telnet.exe localhost 1234" to
connect to the Application.
After the Connection, the Async-Callback from the class "Test" creates a new
Form with the "Create" Method. And there is where I need help. This "Create"
Method needs to run on the Main Thread so it can use it's Message Loop.

Thanks for help.
//Roman

/************ CODE ************/
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Windows.Forms;

namespace AsyncTest {
public class Form1 : Form {
private Button myButton;

public Form1() {
// Create Button
myButton = new Button();
myButton.Text = "Connect";
myButton.Click += new System.EventHandler(this.button1_Click);

// Add the Button
this.Controls.Add(myButton);
}

private void button1_Click(object sender, EventArgs e) {
// Just Connect...
TcpClient cl = new TcpClient("localhost", 1234);
// ... and disconnect
cl.Client.Close();
cl.Close();
}

protected override void OnClosed(EventArgs e) {
base.OnClosed(e);
Application.Exit();
}

[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(fals e);

// Just needed for a Button to tell when to Connect, Could also
use Telnet
Form f = new Form1();
f.Show();

// Start the "listener"
Test t = new Test();

Application.Run();
}
}

class Test {
TcpListener m_tcpListener;

public Test() {
// Start Listener
m_tcpListener = new TcpListener(IPAddress.Any, 1234);
m_tcpListener.Start();
// Start Accepting
m_tcpListener.BeginAcceptTcpClient(new
AsyncCallback(AcceptTcpClientCallback), this);
}

private static void AcceptTcpClientCallback(IAsyncResult ar) {
Test cl = (Test)ar.AsyncState;
TcpListener listener = cl.m_tcpListener;
TcpClient client = listener.EndAcceptTcpClient(ar);

Console.WriteLine("Connected");

// Start Accepting again
listener.BeginAcceptTcpClient(new
AsyncCallback(AcceptTcpClientCallback), cl);

// This Method needs to run on the "main"-Thread where the
MessageLoop is running
// (which was started with Application.Run() )
cl.Create();
}

private void Create() {
Form bla = new Form();
bla.Show();
}
}
}
/************ END CODE ************/
Mar 6 '06 #1
7 3197


Hi,

Thanks for posting at the newsgroup!

"The Message Loop is created on Application.Run() on the Main Method..."
As you have posted, this is the cause why the form failed. So we can find
one resolution to create the form in the main thread. I have one suggestion
for you:

At .Net 2.0 winform. there is one component called BackgroundWorker
provided by Microsoft for you, which can execute one long-time processing
job and the form can communicate with it very smoothly.

It provides the asynchronization to the WinForm. We can create one callback
function for its DoWork delegate which will execute the job at the
background. Then at the callback function, when some special event occur,
we can call the BackgroundWorker.ReportProgress method to report the
status. At the call of the ReportProgress method, BackgroundWorker will
invoke the callback function of its ProgressChanged delegate. Then we can
create one new form and display the information at the callback function
which is safe and the whole development will be quite easy.

The sample from Mark of Windowsforms.net will illustrate for you regarding
how to use this for your asynchronization work.
DataGridView App using BackgroundWorker for Async Data Load
http://www.windowsforms.net/Default....dex=4&tabid=49

Please feel free to let me know if you have any further question on this
issue.

Have a nice day!

Best Regards,
Wei-Dong XU
Microsoft Support
---------------------------------------------------------------------------
This posting is provided "AS IS" with no warranties, and confers no rights.
---------------------------------------------------------------------------
It is my pleasure to be of any assistance.

Mar 7 '06 #2
Thanks for you answer.

But I think BackgroundWorker won't help me much because I don't have a Form
or a Control that I can refer to.

All I have is a Main-Thread and a Message-Loop running on it. And now, some
other Thread (an Async-Callback in my case) needs to create a Form which
hooks into this Message-Loop on the Main-Thread. That should be possible if I
can somehow marshal the code which creates the new Form into the Main-Thread.
And that's where I hang. I played around with ISynchronizeInvoke and created
my own class that implements it on the Main-Thread with no success.

So I would still be very pleased if someone can give me more infos or maybe
another solution.
Mar 7 '06 #3


Hi,

Thanks for the reply ! Currently I think I should write one sample code on
how to instantiate one Form at the main thread. :)

If we need the message loop in the main thread, we will need to call the
Application.Run method and then run the code to execute the underlying
wait. Perhaps I was not very clear when introducing the BackgroundWorker
which, in fact, is not a windows control. It is just one utility class for
us to execute the asynchronous operation. It encapsulates one events list
and provides very easy interface for us to respond to the special events
occuring in the asynchronous thread.

So for your issue, I have one workaround: we can create one form to start
the BackgroundWorker, using it to execute your long-time processing. At
first, we can hide the form and when you are going to display something in
the UI, you can make the form visible with the mesage. For your
convenience, I provide one sample code at the end of this reply.

In addition, if you dislike to inherit your form from Form class, please
feel free to let me know. I will try the best to be of assistance for you.

Have a nice day!

Best Regards,
Wei-Dong XU
Microsoft Support
---------------------------------------------------------------------------
This posting is provided "AS IS" with no warranties, and confers no rights.
---------------------------------------------------------------------------
It is my pleasure to be of any assistance.
//-------------------------------------------------------------------------
// please paste the code in one cs file and name the file. Compile it in
this command line:
//csc /t:winexe <your file name>.cs

using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;

namespace AsynchronousCallBackForm
{
class Program
{
static void Main(string[] args)
{
Application.Run(new MyForm());
}

internal class MyForm : Form
{
private BackgroundWorker bw;
private Label lbl;
private int i;
public MyForm()
{
bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += new
ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerCompleted += new
RunWorkerCompletedEventHandler(bw_RunWorkerComplet ed);
lbl = new Label();
lbl.Text = string.Empty;
Controls.Add(lbl);
this.Load += new EventHandler(MyForm_Load);
}

void MyForm_Load(object sender, EventArgs e)
{
Visible = false;
bw.RunWorkerAsync();
}

void bw_DoWork(object sender, DoWorkEventArgs e)
{
while (i < Int32.MaxValue)
{
if (++i % 100 == 0)
{
BackgroundWorker bw = (BackgroundWorker)sender;
bw.ReportProgress(i);
}
Thread.Sleep(5);
}
}

void bw_ProgressChanged(object sender, ProgressChangedEventArgs
e)
{
if (!Visible)
Visible = true;
Controls[0].Text = e.ProgressPercentage.ToString();
}

void bw_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
Application.Exit();
}
}
}
}
//-------------------------------------------------------------------------

Mar 8 '06 #4
Hmm I played around with BackgroundWorker, SynchronizationContext and
ExecutionContext but couldn't achieve what I wanted.

Here's another very simplified example of what I need:

/************ CODE ************/
using System;
using System.Net.Sockets;
using System.Net;
using System.Windows.Forms;

class Program {
static void Main(string[] args) {
StartListening();
Application.Run();
}

static void StartListening() {
TcpListener m_tcpListener = new TcpListener(IPAddress.Any, 1234);
m_tcpListener.Start();

m_tcpListener.BeginAcceptTcpClient(new
AsyncCallback(AcceptTcpClientCallback), m_tcpListener);
}

private static void AcceptTcpClientCallback(IAsyncResult ar) {
TcpListener listener = (TcpListener)ar.AsyncState;
TcpClient client = listener.EndAcceptTcpClient(ar);

listener.BeginAcceptTcpClient(new
AsyncCallback(AcceptTcpClientCallback), listener);

// This Method has to run in the "Main Thread", where
Application.Run() was fired
CreateForm();
}

static void CreateForm() {
Form myForm = new Form();
myForm.Show();
}
}
/************END CODE ************/

Just run it as an Windows Application. You won't see anything. After you
started it, just use the Command Prompt with "telnet localhost 1234"

That will fire the Async Callback which calls CreateForm and creates a new
Form. Now if you try to interact with the Form you'll see that the Form
doesn't respond.

So my problem is, that I don't know how I can marshal CreateForm() to the
Main-Thread, where the Message-Loop is running so that the newly created Form
can interact to Messages.

Mar 9 '06 #5


Hi,

I think that will be helpful to instantiate one form with visible to false
at the main thread. This way, the customer will not see the form. Then at
the 2nd thread, it only need to set the visible to true and the form will
be displayed and the user can interact with the form. All this is
demonstrated at my sample code in the last reply. Please have a test.
Thanks!

Besides, from your sample code, the application is waiting for the client
request. Its behavior is very like one server and ASP.net team have one
sample server application with the source code. That application has one UI
for the user interaction. So I'd suggest you can have a look at that
application whose form is created at the main thread. For your scenario,
let's set the visible property of the form to false will resolve your
issue.
Download ASP.NET Cassini Sample Web Server
http://www.asp.net/Projects/Cassini/...ndex=0&tabid=1

Please feel free to let me know if you have any further question.

Have a nice day!

Best Regards,
Wei-Dong XU
Microsoft Support
---------------------------------------------------------------------------
This posting is provided "AS IS" with no warranties, and confers no rights.
---------------------------------------------------------------------------
It is my pleasure to be of any assistance.
Mar 10 '06 #6
Thanks, I'll have a look at the Cassini Sample Web Server.

The Problem with your code in the post before was, that, le'ts say, every
Client that connects would open a Form. So that means there could be
unlimited Forms so I need do genereate them when a Client connects.

I by now found a little Workaround which looks like:

/************ CODE ************/
using System;
using System.Net.Sockets;
using System.Net;
using System.Windows.Forms;
using System.Threading;

class Program {
static SynchronizationContext ctx;

static void Main(string[] args) {
Control ctl = new Control();
ctx = SynchronizationContext.Current;
StartListening();
Application.Run();
}

static void StartListening() {
TcpListener m_tcpListener = new TcpListener(IPAddress.Any, 1234);
m_tcpListener.Start();
m_tcpListener.BeginAcceptTcpClient(new
AsyncCallback(AcceptTcpClientCallback), m_tcpListener);
}

private static void AcceptTcpClientCallback(IAsyncResult ar) {
TcpListener listener = (TcpListener)ar.AsyncState;
TcpClient client = listener.EndAcceptTcpClient(ar);
listener.BeginAcceptTcpClient(new
AsyncCallback(AcceptTcpClientCallback), listener);

// This Method has to run in the "Main Thread", where
Application.Run() was fired
Program.ctx.Send(new SendOrPostCallback(CreateForm), null);
}

static void CreateForm(object o) {
Form myForm = new Form();
myForm.Show();
}
}
/************ END CODE ************/

Well, the most important part is that I added an SynchronizationContext
which I set on the Main Thread and then run the CreateForm on that Context
(ctx.Send()). So this solution is working so far but has something that
annoys me:

The first Line in the Main() is a
Control ctl = new Control();
Without this line, the SynchronizationContext.Current would be null so I
can't Send something to it.

I looked around in Reflector and found in the Constructor of a Control the
Line:
WindowsFormsSynchronizationContext.InstallIfNeeded ();

Most of what's going on in this Function is Internal so I can't just call it
in my Programm.
So I guess I have to life with that.

So, do you think that this solution with the SynchronizationContext and it's
Send and the dummy Control is ok and solid?

Thanks!
//Roman
Mar 10 '06 #7


Hi,

Thanks for the replying!

I have read your code sample, that looks well at the first glance. However,
since the SynchronizationContext class is provided in .Net 2.0 for
"Provides the basic functionality for propagating a synchronization context
in various synchronization models". I think this should not be the best
resolution for your application. This is because the main thread should be
notified to create one form when one client connection is established at
the working thread; at your code, we force one synchronization to make the
main thread creating one form which will hurt your application's
performance that the working thread will need to wait for the main thread
finishes the consturction of one form.

As what you have posted, the SynchronizationContext also introduces some
new problems. So my suggestions is that: it is the best for you to add one
queue at your mainthread to receive the notification from the working
thread. When it receives any notification, it can perform the corresponding
operation. This will make your application more robust and stable. There is
one CodeProject article demonstrating for you regarding how to create
in-process asynchronous services.
Create in-process asynchronous services in C#
http://www.codeproject.com/csharp/in...rvicesincs.asp

Please feel free to let me know if you have any further question on this
issue.

Have a nice day!

Best Regards,
Wei-Dong XU
Microsoft Support
---------------------------------------------------------------------------
This posting is provided "AS IS" with no warranties, and confers no rights.
---------------------------------------------------------------------------
It is my pleasure to be of any assistance.

Mar 13 '06 #8

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

8
by: Dave | last post by:
I'm using the BeginInvoke method of a delegate to invoke a thread asynchronously and then use the EndInvoke to retrieve the value. This works wonderfully until a Serviced Component is added to...
6
by: Vanessa | last post by:
I have a question regarding async mode for calling Microsoft.XMLHTTP object. Microsoft.XMLHTTP hangs the IE once in a while suddenly, but it will work again after half an hour or so without doing...
3
by: IMS.Rushikesh | last post by:
Hi Friends, I need a XPath query, whcih ll return me subset of data.... check below xml stream <label id="MyExpenseDetails_lbl" xlink:role="terseLabel">Short Expense Details</label> <label...
0
by: Passynkov, Vadim | last post by:
I am using Asynchronous Query Processing interface from libpq library. And I got some strange results on Solaris My test select query is 'SELECT * from pg_user;' and I use select system...
2
by: Devicharan | last post by:
I have two forms, a Main Form and a Child Form. And In the Main Form, I have a small Async Process, Which needs to show the child form. I use mainForm.BeginInvoke(ShowtheNewForm) to start the...
4
by: dbcuser | last post by:
Hi, I have a small windows form. This has 2 labels and a button. When a user presses a button, I copy a large file. Nowwhen the copy is going on, if I switch windows, my main window becomes...
4
by: Daylor | last post by:
hi. i have multi thread application in vb.net is there a way NET support, so i can mark the class , to be access only for 1 thread each time ? if there is , small sytax sample will help ...
2
by: Lespaul36 | last post by:
I have tried many things. I still have not found anything that seems to work. Here is the portions of my code that deal with the listening socket maybe you have a better idea? The form still...
0
by: peterperry | last post by:
Hi, Below in comments is the list of controls that I have defined. The function DoOpenUser loads and shows a new Form. This process freezes the application for a while as the Form takes time to...
10
by: Frankie | last post by:
It appears that System.Random would provide an acceptable means through which to generate a unique value used to identify multiple/concurrent asynchronous tasks. The usage of the value under...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.