N
Natalia DeBow
Hi,
I am working on a Windows-based client-server application. I am involved in
the development of the remote client modules. I am using asynchronous
delegates to obtain information from remote server and display this info on
the UI.
From doing some research, I know that the way my implementation works today
is not thread-safe, because essentially my worker thread updates the UI,
which is BAD. So, here I am trying to figure out how to make my code to be
thread safe.
Another area where I would like some input is exception handling with
asynchronous delegates. As a general rule, I've read that if an exception
has been raised by the worker thread, that this exception would caught when
a call to EndInvoke(...) is being made. However, the complication comes in
with a possibility of having innerexception to be passed in, which I am not
sure how they should be handled.
Here is a brief explanation of what the code is supposed to accomplish.
1. Retrieve a hash table of text strings and display them for all of the UI
controls the require text to be displayed to the user.
Here I have a question with updating UI technique, exception handling
and also thread synchronization since I need to wait until this async call
completes, before I make the next call to Initialize UI. The next call
depends on the hash table data structure which needs to be fully populated
before the next call proceeds.
2. Initialize UI controls with certain values. (For example, a ListView
control needs to be populated with values obtained from the remote server.)
Here gain, I have a question with thread safe UI updating technique and
exception handling.
3. When the user requests to perform some action, say by clicking on a
button on the UI, perform this action via an asynchronous delegate call and
display the results of this action on the UI.
Here I am using asynchronous delegates and events. When an event is
triggered on the server side, the client should be able to handle this event
and display the result on the UI. The action is triggered by the user
initiating an asynchronous call, however, I am not sure where to call
EndInvoke() and how to do exception handling here.
Thanks so much for your assistance.
Natalia
************* Interface shared b/w client and remote server *************
public interface ISomeInterface
{
//...
Hashtable GetText(int[] textIds);
void InitializeComponent( out ComponentInfo[] ComponentInfoArray );
void DoSomeAction(Action action, params object[] parameters);
//...
event UpdateEventHandler SomeItemCompleted;
}
***************** Some client side helper implementation *****************
public class SomeHelperSample
{
//...
public delegate Hashtable GetTextDelegate(int[] textIds);
public delegate void InitializeComponentDelegate(out ComponentInfo[]
components);
public delegate void SomeActionDelegate(Action action, params object[]
parameters);
//...
// ISomeInterface someInterface = (ISomeInterface) Activator.GetObject(...);
public GetTextDelegate getText = new GetTextDelegate(someInteface.GetText);
public InitializeComponentDelegate initializeComponent = new
InitializeComponentDelegate(someInterface.InitializeComponent);
public SomeActionDelegate doSomeAction = new
SomeActionDelegate(someInteface.DoSomeAction);
//...
public event SomeInterface.UpdateEventHandler SomeItemCompleted
{
//...
}
}
******* Client side implementation *************
public class ClientSample : System.Windows.Forms.Form
{
public SomeHelperSample helper = new SomeHelperSample();
// ...
// obtain text to be displayed on the UI controls
private void InitializeText()
{
int[] textIds = new int[]{0,1,2,3,4};
try
{
// asynchronously call GetText method
AsyncCallback callback = new AsyncCallback(DisplayTextCallback);
helper.getText.BeginInvoke(textIds, callback, null);
}
catch (Exception e)
{
MessageBox.Show("Error obtaining text. ", e.Message);
e = e.InnerException;
while(e != null)
{
e = e.InnerException;
}
}
// callback function to display text on UI controls
private void DisplayTextCallback(IAsyncResult result)
{
try
{
// extract the delegate from the AsynchResult
GetTextDelegate getText =
(GetTextDelegate) ((AsyncResult)result).AsyncDelegate;
textMessages = getText.EndInvoke(result);
// access the key in the hash table to retreive text strings and update the
UI
if (textMessages.ContainsKey(0))
{
this.tabPage.Text = textMessages[0].ToString(); // updates UI components
with retrieved text
}
//...
InitializeUIControls(); //// Need to have the HashTable populated before
this method can be called, since it requires some of the HashTable values.
}
catch (Exception e)
{
// TODO: Exception handling code.
}
}
// Need to have the HashTable populated before this method can be called,
since it depends on the HashTable values.
private void InitializeUIControls()
{
try
{
// asynchronously call InitializeComponent method
AsyncCallback callback = new AsyncCallback(DisplayInfoCallback);
helper.initializeComponent.BeginInvoke(out controls, callback, null);
}
catch (Exception e)
{
MessageBox.Show("Error initializing component. ", e.Message);
// TO DO: Add a message to the messages panel.
}
}
private void DisplayInfoCallback(IAsyncResult result)
{
try
{
// extract the delegate from the AsyncResult
InitializeComponentDelegate initializeComponent =
(InitializeComponentDelegate)((AsyncResult)result).AsyncDelegate;
// obtain the list of user accounts
initializeComponent.EndInvoke(out controls, result);
// display user account name on the UI
foreach (ControlInfo controlInfo in controls)
{
AddInfoToDisplay(controlInfo);
}
}
catch (Exception e)
{
// TODO: Exception handling code goes here.
}
}
//...
private void buttonAction_Click(object sender, System.EventArgs e)
{
//......
try
{
// subscribe to the update event
helper.SomeItemCompleted += new UpdateEventHandler(OnItemCompleted);
// asynchronously call DoSomeAction method
helper.DoSomeAction.BeginInvoke(actionCode, componentInfoArray, null, null);
}
catch(Exception e)
{
MessageBox.Show("Error. ", re.Message);
}
}
private void OnItemCompleted(object sender, UpdateEventArgs e) // custom
UpdateEventArgs class derived from EventArgs
{
// updates UI directly. Where would I put the EndInvoke corresponding to
the above BeginInvoke?
}
}
I am working on a Windows-based client-server application. I am involved in
the development of the remote client modules. I am using asynchronous
delegates to obtain information from remote server and display this info on
the UI.
From doing some research, I know that the way my implementation works today
is not thread-safe, because essentially my worker thread updates the UI,
which is BAD. So, here I am trying to figure out how to make my code to be
thread safe.
Another area where I would like some input is exception handling with
asynchronous delegates. As a general rule, I've read that if an exception
has been raised by the worker thread, that this exception would caught when
a call to EndInvoke(...) is being made. However, the complication comes in
with a possibility of having innerexception to be passed in, which I am not
sure how they should be handled.
Here is a brief explanation of what the code is supposed to accomplish.
1. Retrieve a hash table of text strings and display them for all of the UI
controls the require text to be displayed to the user.
Here I have a question with updating UI technique, exception handling
and also thread synchronization since I need to wait until this async call
completes, before I make the next call to Initialize UI. The next call
depends on the hash table data structure which needs to be fully populated
before the next call proceeds.
2. Initialize UI controls with certain values. (For example, a ListView
control needs to be populated with values obtained from the remote server.)
Here gain, I have a question with thread safe UI updating technique and
exception handling.
3. When the user requests to perform some action, say by clicking on a
button on the UI, perform this action via an asynchronous delegate call and
display the results of this action on the UI.
Here I am using asynchronous delegates and events. When an event is
triggered on the server side, the client should be able to handle this event
and display the result on the UI. The action is triggered by the user
initiating an asynchronous call, however, I am not sure where to call
EndInvoke() and how to do exception handling here.
Thanks so much for your assistance.
Natalia
************* Interface shared b/w client and remote server *************
public interface ISomeInterface
{
//...
Hashtable GetText(int[] textIds);
void InitializeComponent( out ComponentInfo[] ComponentInfoArray );
void DoSomeAction(Action action, params object[] parameters);
//...
event UpdateEventHandler SomeItemCompleted;
}
***************** Some client side helper implementation *****************
public class SomeHelperSample
{
//...
public delegate Hashtable GetTextDelegate(int[] textIds);
public delegate void InitializeComponentDelegate(out ComponentInfo[]
components);
public delegate void SomeActionDelegate(Action action, params object[]
parameters);
//...
// ISomeInterface someInterface = (ISomeInterface) Activator.GetObject(...);
public GetTextDelegate getText = new GetTextDelegate(someInteface.GetText);
public InitializeComponentDelegate initializeComponent = new
InitializeComponentDelegate(someInterface.InitializeComponent);
public SomeActionDelegate doSomeAction = new
SomeActionDelegate(someInteface.DoSomeAction);
//...
public event SomeInterface.UpdateEventHandler SomeItemCompleted
{
//...
}
}
******* Client side implementation *************
public class ClientSample : System.Windows.Forms.Form
{
public SomeHelperSample helper = new SomeHelperSample();
// ...
// obtain text to be displayed on the UI controls
private void InitializeText()
{
int[] textIds = new int[]{0,1,2,3,4};
try
{
// asynchronously call GetText method
AsyncCallback callback = new AsyncCallback(DisplayTextCallback);
helper.getText.BeginInvoke(textIds, callback, null);
}
catch (Exception e)
{
MessageBox.Show("Error obtaining text. ", e.Message);
e = e.InnerException;
while(e != null)
{
e = e.InnerException;
}
}
// callback function to display text on UI controls
private void DisplayTextCallback(IAsyncResult result)
{
try
{
// extract the delegate from the AsynchResult
GetTextDelegate getText =
(GetTextDelegate) ((AsyncResult)result).AsyncDelegate;
textMessages = getText.EndInvoke(result);
// access the key in the hash table to retreive text strings and update the
UI
if (textMessages.ContainsKey(0))
{
this.tabPage.Text = textMessages[0].ToString(); // updates UI components
with retrieved text
}
//...
InitializeUIControls(); //// Need to have the HashTable populated before
this method can be called, since it requires some of the HashTable values.
}
catch (Exception e)
{
// TODO: Exception handling code.
}
}
// Need to have the HashTable populated before this method can be called,
since it depends on the HashTable values.
private void InitializeUIControls()
{
try
{
// asynchronously call InitializeComponent method
AsyncCallback callback = new AsyncCallback(DisplayInfoCallback);
helper.initializeComponent.BeginInvoke(out controls, callback, null);
}
catch (Exception e)
{
MessageBox.Show("Error initializing component. ", e.Message);
// TO DO: Add a message to the messages panel.
}
}
private void DisplayInfoCallback(IAsyncResult result)
{
try
{
// extract the delegate from the AsyncResult
InitializeComponentDelegate initializeComponent =
(InitializeComponentDelegate)((AsyncResult)result).AsyncDelegate;
// obtain the list of user accounts
initializeComponent.EndInvoke(out controls, result);
// display user account name on the UI
foreach (ControlInfo controlInfo in controls)
{
AddInfoToDisplay(controlInfo);
}
}
catch (Exception e)
{
// TODO: Exception handling code goes here.
}
}
//...
private void buttonAction_Click(object sender, System.EventArgs e)
{
//......
try
{
// subscribe to the update event
helper.SomeItemCompleted += new UpdateEventHandler(OnItemCompleted);
// asynchronously call DoSomeAction method
helper.DoSomeAction.BeginInvoke(actionCode, componentInfoArray, null, null);
}
catch(Exception e)
{
MessageBox.Show("Error. ", re.Message);
}
}
private void OnItemCompleted(object sender, UpdateEventArgs e) // custom
UpdateEventArgs class derived from EventArgs
{
// updates UI directly. Where would I put the EndInvoke corresponding to
the above BeginInvoke?
}
}