Problem with multithreaded C# app on Windows CE

G

Guest

Hello

I'm having a problem running a C# multithreaded application on Windows CE 5.0.
The code I'm trying to execute is the source code of the MSDN article:
"Safe, Simple Multithreading in Windows Forms, Part 3" , January 2003,
written by Chris Sells
(http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnforms/html/winforms06112002.asp).
The source files consist of 'AsynchCalcPi.cs' and 'NineDigitsOfPiAt.cs'
found in
winforms01232003_sample\ch13\LongRunningOperations\.
(For VS2005 users: Start a new Windows CE project with Compact Framework 2.0
installed and do some copy and past to get it running).

The code demonstrates a thread safe 'message passing model', in which a long
running operation (the calculation of n digits of pi) is performed on a
worker thread.
Everything in the source code is supported by the .Net Compact Framework 2.0
so this code should run just as well on both Windows CE and Windows XP. At
least so I thought.

The problem is that the application executes just fine on Windows XP (thanks
to MSIL), but I'm not able to get it running on Windows CE 5.0. Strange,
isn't it?

What happens is that when i deploy the application from Visual Studio 2005
on the Windows CE target device, the application starts up by showing a form
in which you can type the number of decimals of pi you want to compute and a
button to start and cancel the computations.
When pressing the start button a worker thread is started from the thread
pool by calling BeginInvok and set to do the computations.

This is also where the application fails on Windows CE: When pressing the
start button a "NotSupportedException" is thrown and the Visual Studio 2005
debugger indicates that this is caused by
Application.Run(new AsynchCalcPiForm());
in the main method.

This I don't understand. The Windows Form is simpy launched from the default
'Program.cs' file containing only the main method with the above mentioned
statement.
What could be the problem here?
Why is this working on Windows XP but not Windows CE?

PS This question was posted under Mobile and Embedded
Development\Windows CE>Application Development but I was adviced to post
it here instead.
 
G

Guest

The exception is thrown elsewhere, probably in the worker thread itself. We
need to see more code. You could also try adding some try/catch blocks in
the thread proc to find it.

-Chris
 
G

Guest

You will have to download the code from MSDN (see link in my original post).
The example is small and nice so it shouldn't take too long to compile and
get it running.
If you do so you will see that the described problem is caused by
the folowing call in AsynchCalcPiForm.cs >calcButton_Click :
calcPi.BeginInvoke((int)digitsUpDown.Value, null, null);
This call does however not throw any exceptions, because adding a try{}
catch{Exception e} statement never causes the catch{} loop to be executed.
Instead the VS2005 debugger indicates the NotSupportedException caused by
Application.Run() in main() as before.
Please take a look at the source code. It would make this a lot easier.
 
G

Guest

The exception is in the thread. You have no handler, so when it bubbles up,
it comes out at the top - Application.Run. Trust me, Run is supported.

Sorry, but I'm not going to go to MSDN, download the sample and massage it
into a device project just for the sake of helping you, and I doubt anyone
else in the newsgroup will either. If you'd like to post the code, then
we're happy to look at it for problems.
 
P

Paul G. Tobey [eMVP]

The problem is that it's using BeginInvoke(). Not supported in .NET CF,
although it's in the class definition. It's throwing a
NotSupportedException, as expected.

Paul T.
 
G

Guest

Ok, let me get this straight:
Are you saying the Delegate's BeginInvoke() is not supported by the .Net
Compact Framework 2.0?
From the MSDN documentation I had the impression that both the Control's and
the Delegate's BeginInvoke() are supported by the .Net CF 2.0. Which is the
case?
The Visual Studio 2005 debugger does not complain about either.
 
P

Paul G. Tobey [eMVP]

Yes, that's what I'm saying. It's in the class signature (the method exists,
in some sense), but any method that calls BeginInvoke() will generate a
not-supported exception when that method is invoked. Delegates are
supported and I don't see any problems with them, but BeginInvoke isn't.
You can easily, or at least relatively easily, change the code to use
Control.Invoke to call back into the UI thread. You can't pass a random set
of parameters, but you can pass an EventArgs subclass which holds your extra
parameters as fields, instead. It's not as clean as the desktop, but it
works. Something like this:

-----

// Must use Invoke, not BeginInvoke, in .NET CF.

InvokeEventArgs args = new InvokeEventArgs(pi, totalDigits,
digitsSoFar);

this.Invoke(new EventHandler(ShowProgress_invoke), new object[] {
args });



private void ShowProgress_invoke(object sender, EventArgs e)

{

// Call ShowProgress on what should now be the UI thread.

InvokeEventArgs iea = (InvokeEventArgs)sender;

ShowProgress(iea.pi, iea.digits, iea.digitsSoFar);

}

public class InvokeEventArgs : EventArgs

{

public string pi;

public int digits;

public int digitsSoFar;

public InvokeEventArgs(string _pi, int _digits, int _digitsSoFar)

{

pi = _pi;

digits = _digits;

digitsSoFar = _digitsSoFar;

}

}

-----


I'm not sure what the debugger has to do with anything.

Paul T.
 
F

Functional Illiterate

For .NET CF 2.0, BeginInvoke is supported. I use it in an app target
for devices running .NET CF 2.0. SP1. However, my word is often
questioned and more often questionable, so the following is straight
from MS:
---------------------------------------------
(http://msdn.microsoft.com/netframew...ry/en-us/dnnetcomp/html/whats_new_netcf2.asp_:
Threads and the User Interface

The .NET Compact Framework 2.0 now supports asynchronous execution of a
delegate on a user interface thread. Unlike the .NET Compact Framework
1.0 that supports only the Control.Invoke method (which synchronously
executes a delegate on a control's owning thread), the .NET Compact
Framework 2.0 provides Control.BeginInvoke that asynchronously executes
a delegate on the control's owning thread. The Control.EndInvoke method
is also provided. When called, the EndInvoke method returns the results
of an asynchronous operation.
---------------------------------------------

I'd check which version of the CF is installed by running
\Windows\cgacutil.exe. Re-install .NET CF if CF 2.0 is on the device.

Good Luck

Yes, that's what I'm saying. It's in the class signature (the method exists,
in some sense), but any method that calls BeginInvoke() will generate a
not-supported exception when that method is invoked. Delegates are
supported and I don't see any problems with them, but BeginInvoke isn't.
You can easily, or at least relatively easily, change the code to use
Control.Invoke to call back into the UI thread. You can't pass a random set
of parameters, but you can pass an EventArgs subclass which holds your extra
parameters as fields, instead. It's not as clean as the desktop, but it
works. Something like this:

-----

// Must use Invoke, not BeginInvoke, in .NET CF.

InvokeEventArgs args = new InvokeEventArgs(pi, totalDigits,
digitsSoFar);

this.Invoke(new EventHandler(ShowProgress_invoke), new object[] {
args });



private void ShowProgress_invoke(object sender, EventArgs e)

{

// Call ShowProgress on what should now be the UI thread.

InvokeEventArgs iea = (InvokeEventArgs)sender;

ShowProgress(iea.pi, iea.digits, iea.digitsSoFar);

}

public class InvokeEventArgs : EventArgs

{

public string pi;

public int digits;

public int digitsSoFar;

public InvokeEventArgs(string _pi, int _digits, int _digitsSoFar)

{

pi = _pi;

digits = _digits;

digitsSoFar = _digitsSoFar;

}

}

-----


I'm not sure what the debugger has to do with anything.

Paul T.

GT said:
Ok, let me get this straight:
Are you saying the Delegate's BeginInvoke() is not supported by the .Net
Compact Framework 2.0?
From the MSDN documentation I had the impression that both the Control's
and
the Delegate's BeginInvoke() are supported by the .Net CF 2.0. Which is
the
case?
The Visual Studio 2005 debugger does not complain about either.
 
F

Functional Illiterate

Ooops... I meant
I'd check which version of the CF is installed on the device by running
\Windows\cgacutil.exe. Re-install .NET CF if CF 2.0 is NOT on the
device.

Additionally, since this project was made a long time ago the
references could be pointing to .NET CF 1.0 assemblies instead of .NET
CF 2.0 assemblies. I'd would also recommend resetting all of the
references in VS2005 to ensure the app is looking for the correct
assemblies.


Functional said:
For .NET CF 2.0, BeginInvoke is supported. I use it in an app target
for devices running .NET CF 2.0. SP1. However, my word is often
questioned and more often questionable, so the following is straight
from MS:
---------------------------------------------
(http://msdn.microsoft.com/netframew...ry/en-us/dnnetcomp/html/whats_new_netcf2.asp_:
Threads and the User Interface

The .NET Compact Framework 2.0 now supports asynchronous execution of a
delegate on a user interface thread. Unlike the .NET Compact Framework
1.0 that supports only the Control.Invoke method (which synchronously
executes a delegate on a control's owning thread), the .NET Compact
Framework 2.0 provides Control.BeginInvoke that asynchronously executes
a delegate on the control's owning thread. The Control.EndInvoke method
is also provided. When called, the EndInvoke method returns the results
of an asynchronous operation.
---------------------------------------------

I'd check which version of the CF is installed by running
\Windows\cgacutil.exe. Re-install .NET CF if CF 2.0 is on the device.

Good Luck

Yes, that's what I'm saying. It's in the class signature (the method exists,
in some sense), but any method that calls BeginInvoke() will generate a
not-supported exception when that method is invoked. Delegates are
supported and I don't see any problems with them, but BeginInvoke isn't.
You can easily, or at least relatively easily, change the code to use
Control.Invoke to call back into the UI thread. You can't pass a random set
of parameters, but you can pass an EventArgs subclass which holds your extra
parameters as fields, instead. It's not as clean as the desktop, but it
works. Something like this:

-----

// Must use Invoke, not BeginInvoke, in .NET CF.

InvokeEventArgs args = new InvokeEventArgs(pi, totalDigits,
digitsSoFar);

this.Invoke(new EventHandler(ShowProgress_invoke), new object[] {
args });



private void ShowProgress_invoke(object sender, EventArgs e)

{

// Call ShowProgress on what should now be the UI thread.

InvokeEventArgs iea = (InvokeEventArgs)sender;

ShowProgress(iea.pi, iea.digits, iea.digitsSoFar);

}

public class InvokeEventArgs : EventArgs

{

public string pi;

public int digits;

public int digitsSoFar;

public InvokeEventArgs(string _pi, int _digits, int _digitsSoFar)

{

pi = _pi;

digits = _digits;

digitsSoFar = _digitsSoFar;

}

}

-----


I'm not sure what the debugger has to do with anything.

Paul T.

GT said:
Ok, let me get this straight:
Are you saying the Delegate's BeginInvoke() is not supported by the .Net
Compact Framework 2.0?
From the MSDN documentation I had the impression that both the Control's
and
the Delegate's BeginInvoke() are supported by the .Net CF 2.0. Which is
the
case?
The Visual Studio 2005 debugger does not complain about either.


:

The problem is that it's using BeginInvoke(). Not supported in .NET CF,
although it's in the class definition. It's throwing a
NotSupportedException, as expected.

Paul T.

The exception is in the thread. You have no handler, so when it
bubbles
up, it comes out at the top - Application.Run. Trust me, Run is
supported.

Sorry, but I'm not going to go to MSDN, download the sample and massage
it
into a device project just for the sake of helping you, and I doubt
anyone
else in the newsgroup will either. If you'd like to post the code,
then
we're happy to look at it for problems.


--
Chris Tacke
OpenNETCF Consulting
Managed Code in the Embedded World
www.opennetcf.com
--



You will have to download the code from MSDN (see link in my original
post).
The example is small and nice so it shouldn't take too long to compile
and
get it running.
If you do so you will see that the described problem is caused by
the folowing call in AsynchCalcPiForm.cs >calcButton_Click :
calcPi.BeginInvoke((int)digitsUpDown.Value, null, null);
This call does however not throw any exceptions, because adding a
try{}
catch{Exception e} statement never causes the catch{} loop to be
executed.
Instead the VS2005 debugger indicates the NotSupportedException caused
by
Application.Run() in main() as before.
Please take a look at the source code. It would make this a lot
easier.

:

The exception is thrown elsewhere, probably in the worker thread
itself.
We
need to see more code. You could also try adding some try/catch
blocks
in
the thread proc to find it.

-Chris


Hello

I'm having a problem running a C# multithreaded application on
Windows
CE
5.0.
The code I'm trying to execute is the source code of the MSDN
article:
"Safe, Simple Multithreading in Windows Forms, Part 3" , January
2003,
written by Chris Sells
(http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnforms/html/winforms06112002.asp).
The source files consist of 'AsynchCalcPi.cs' and
'NineDigitsOfPiAt.cs'
found in
winforms01232003_sample\ch13\LongRunningOperations\.
(For VS2005 users: Start a new Windows CE project with Compact
Framework
2.0
installed and do some copy and past to get it running).

The code demonstrates a thread safe 'message passing model', in
which
a
long
running operation (the calculation of n digits of pi) is performed
on
a
worker thread.
Everything in the source code is supported by the .Net Compact
Framework
2.0
so this code should run just as well on both Windows CE and Windows
XP. At
least so I thought.

The problem is that the application executes just fine on Windows
XP
(thanks
to MSIL), but I'm not able to get it running on Windows CE 5.0.
Strange,
isn't it?

What happens is that when i deploy the application from Visual
Studio
2005
on the Windows CE target device, the application starts up by
showing
a
form
in which you can type the number of decimals of pi you want to
compute
and
a
button to start and cancel the computations.
When pressing the start button a worker thread is started from the
thread
pool by calling BeginInvok and set to do the computations.

This is also where the application fails on Windows CE: When
pressing
the
start button a "NotSupportedException" is thrown and the Visual
Studio
2005
debugger indicates that this is caused by
Application.Run(new AsynchCalcPiForm());
in the main method.

This I don't understand. The Windows Form is simpy launched from
the
default
'Program.cs' file containing only the main method with the above
mentioned
statement.
What could be the problem here?
Why is this working on Windows XP but not Windows CE?

PS This question was posted under Mobile and Embedded
Development\Windows CE>Application Development but I was adviced to
post
it here instead.
 
G

Guest

I've run 'cgautil.exe' on the Windows CE target and it verifies that I have
Compact Framework 2.0 installed (more specifically version 2.0.5238.0).
I have built the project by starting a new Win CE project in Visual Studio
2005 (of which I have CF2.0 installed) and merely copied the C# code of the
original project. I've also cheched the project references and there are only
references to CF 2.0 dlls.
I also find it very hard to believe that BeginInvoke() is not supported by
the CF2.0. If this really is the case, then MSDN is completely misleading.
Perhaps the problem is related to something else. I will now post the source
code below so that you can have a look and see if there is something else
that may cause this problem:

/*AsynchCalcPiForm.cs
*Windows Form lauched by Application.Run(new AsynchCalcPiForm())
* in Program.cs that only contains main().
* Aslo contains the class 'ShowProgressArgs' that keeps track of the
* progress of the pi calculation performed by NineDigitsOfPi.cs
*/
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Text;
using System.Threading;
using System.Diagnostics;

namespace CalcPI_Part3_CE
{
public partial class AsynchCalcPiForm : Form
{

enum CalcState
{
Pending,
Calculating,
Canceled,
}

CalcState state = CalcState.Pending;
public AsynchCalcPiForm()
{
InitializeComponent();
}

class ShowProgressArgs : EventArgs
{
public string Pi;
public int TotalDigits;
public int DigitsSoFar;
public bool Cancel;

public ShowProgressArgs(string pi, int totalDigits, int
digitsSoFar)
{
this.Pi = pi;
this.TotalDigits = totalDigits;
this.DigitsSoFar = digitsSoFar;
}
}

delegate void ShowProgressHandler(object sender, ShowProgressArgs e);

void ShowProgress(object sender, ShowProgressArgs e)
{
// Make sure we're on the right thread
if (this.InvokeRequired == false)
{
piTextBox.Text = e.Pi;
piProgressBar.Maximum = e.TotalDigits;
piProgressBar.Value = e.DigitsSoFar;

// Check for Cancel
e.Cancel = (state == CalcState.Canceled);

// Check for completion
if (e.Cancel || (e.DigitsSoFar == e.TotalDigits))
{
state = CalcState.Pending;
calcButton.Text = "Calc";
calcButton.Enabled = true;

}
}
// Transfer control to correct thread
else
{
ShowProgressHandler
showProgress =
new ShowProgressHandler(ShowProgress);
Invoke(showProgress, new object[] { sender, e });

// Show progress asynchronously
// IAsyncResult res =
// BeginInvoke(showProgress, new object[] { pi,
totalDigits, digitsSoFar, inoutCancel});
//
// // Wait for results
// while( !res.IsCompleted )
System.Threading.Thread.Sleep(100);
//
// // Harvest results
// cancel = (bool)inoutCancel;
// object methodResults = EndInvoke(res);
// Do something with results...
}
}

void CalcPi(int digits)
{
StringBuilder pi = new StringBuilder("3", digits + 2);
object sender = System.Threading.Thread.CurrentThread;
ShowProgressArgs e = new ShowProgressArgs(pi.ToString(), digits,
0);

// Show progress (ignoring Cancel so soon)
ShowProgress(sender, e);

if (digits > 0)
{
pi.Append(".");

for (int i = 0; i < digits; i += 9)
{
int nineDigits = NineDigitsOfPi.StartingAt(i + 1);
int digitCount = Math.Min(digits - i, 9);
string ds = string.Format("{0:D9}", nineDigits);
pi.Append(ds.Substring(0, digitCount));

// Show progress (checking for Cancel)
e.Pi = pi.ToString();
e.DigitsSoFar = i + digitCount;
ShowProgress(sender, e);
if (e.Cancel) break;
}
}
}

delegate void CalcPiDelegate(int digits);

void calcButton_Click(object sender, System.EventArgs e)
{
// Synch method
// CalcPi((int)digitsUpDown.Value);
// return;

// Calc button does double duty as Cancel button
switch (state)
{
// Start a new calculation
case CalcState.Pending:
// Allow canceling
state = CalcState.Calculating;
calcButton.Text = "Cancel";

// Asynch delegate method
CalcPiDelegate calcPi = new CalcPiDelegate(CalcPi);
calcPi.BeginInvoke((int)digitsUpDown.Value, null, null);
break;

// Cancel a running calculation
case CalcState.Calculating:
state = CalcState.Canceled;
calcButton.Enabled = false;
break;

// Shouldn't be able to press Calc button while it's canceling
case CalcState.Canceled:
Debug.Assert(false);
break;
}
}

}
}




// NineDigitsOfPi.cs

/*
* Computation of the n'th decimal digit of pi with very little memory.
* Written by Fabrice Bellard on January 8, 1997.
* Ported to C# by Chris Sells on May 5, 2002.
*
* We use a slightly modified version of the method described by Simon
* Plouffe in "On the Computation of the n'th decimal digit of various
* transcendental numbers" (November 1996). We have modified the algorithm
* to get a running time of O(n^2) instead of O(n^3log(n)^3).
*
* This program uses mostly integer arithmetic. It may be slow on some
* hardwares where integer multiplications and divisons must be done
* by software.
*/
using System;
using System.Collections.Generic;
using System.Text;

namespace CalcPI_Part3_CE
{
public class NineDigitsOfPi
{

public static int mul_mod(long a, long b, int m)
{
return (int)((a * b) % m);
}

// return the inverse of x mod y
public static int inv_mod(int x, int y)
{
int q = 0;
int u = x;
int v = y;
int a = 0;
int c = 1;
int t = 0;

do
{
q = v / u;

t = c;
c = a - q * c;
a = t;

t = u;
u = v - q * u;
v = t;
}
while (u != 0);

a = a % y;
if (a < 0) a = y + a;

return a;
}

// return (a^b) mod m
public static int pow_mod(int a, int b, int m)
{
int r = 1;
int aa = a;

while (true)
{
if ((b & 0x01) != 0) r = mul_mod(r, aa, m);
b = b >> 1;
if (b == 0) break;
aa = mul_mod(aa, aa, m);
}

return r;
}

// return true if n is prime
public static bool is_prime(int n)
{
if ((n % 2) == 0) return false;

int r = (int)(Math.Sqrt(n));
for (int i = 3; i <= r; i += 2)
{
if ((n % i) == 0) return false;
}

return true;
}

// return the prime number immediately after n
public static int next_prime(int n)
{
do
{
n++;
}
while (!is_prime(n));

return n;
}

public static int StartingAt(int n)
{
int av = 0;
int vmax = 0;
int N = (int)((n + 20) * Math.Log(10) / Math.Log(2));
int num = 0;
int den = 0;
int kq = 0;
int kq2 = 0;
int t = 0;
int v = 0;
int s = 0;
double sum = 0.0;

for (int a = 3; a <= (2 * N); a = next_prime(a))
{
vmax = (int)(Math.Log(2 * N) / Math.Log(a));
av = 1;

for (int i = 0; i < vmax; ++i) av = av * a;

s = 0;
num = 1;
den = 1;
v = 0;
kq = 1;
kq2 = 1;

for (int k = 1; k <= N; ++k)
{
t = k;
if (kq >= a)
{
do
{
t = t / a;
--v;
}
while ((t % a) == 0);

kq = 0;
}

++kq;
num = mul_mod(num, t, av);

t = (2 * k - 1);
if (kq2 >= a)
{
if (kq2 == a)
{
do
{
t = t / a;
++v;
}
while ((t % a) == 0);
}

kq2 -= a;
}

den = mul_mod(den, t, av);
kq2 += 2;

if (v > 0)
{
t = inv_mod(den, av);
t = mul_mod(t, num, av);
t = mul_mod(t, k, av);
for (int i = v; i < vmax; ++i) t = mul_mod(t, a, av);
s += t;
if (s >= av) s -= av;
}
}

t = pow_mod(10, n - 1, av);
s = mul_mod(s, t, av);
sum = (sum + (double)s / (double)av) % 1.0;
}

return (int)(sum * 1e9);
}

}
}
 
P

Paul G. Tobey [eMVP]

I've gone through adding code step by step and the code runs fine until
there's a call to BeginInvoke() in the _Click event handler. It's possible
that there's a problem just in that one case, I suppose (calling from inside
an event handler).

Paul T.

GT said:
I've run 'cgautil.exe' on the Windows CE target and it verifies that I
have
Compact Framework 2.0 installed (more specifically version 2.0.5238.0).
I have built the project by starting a new Win CE project in Visual Studio
2005 (of which I have CF2.0 installed) and merely copied the C# code of
the
original project. I've also cheched the project references and there are
only
references to CF 2.0 dlls.
I also find it very hard to believe that BeginInvoke() is not supported by
the CF2.0. If this really is the case, then MSDN is completely misleading.
Perhaps the problem is related to something else. I will now post the
source
code below so that you can have a look and see if there is something else
that may cause this problem:

/*AsynchCalcPiForm.cs
*Windows Form lauched by Application.Run(new AsynchCalcPiForm())
* in Program.cs that only contains main().
* Aslo contains the class 'ShowProgressArgs' that keeps track of the
* progress of the pi calculation performed by NineDigitsOfPi.cs
*/
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Text;
using System.Threading;
using System.Diagnostics;

namespace CalcPI_Part3_CE
{
public partial class AsynchCalcPiForm : Form
{

enum CalcState
{
Pending,
Calculating,
Canceled,
}

CalcState state = CalcState.Pending;
public AsynchCalcPiForm()
{
InitializeComponent();
}

class ShowProgressArgs : EventArgs
{
public string Pi;
public int TotalDigits;
public int DigitsSoFar;
public bool Cancel;

public ShowProgressArgs(string pi, int totalDigits, int
digitsSoFar)
{
this.Pi = pi;
this.TotalDigits = totalDigits;
this.DigitsSoFar = digitsSoFar;
}
}

delegate void ShowProgressHandler(object sender, ShowProgressArgs
e);

void ShowProgress(object sender, ShowProgressArgs e)
{
// Make sure we're on the right thread
if (this.InvokeRequired == false)
{
piTextBox.Text = e.Pi;
piProgressBar.Maximum = e.TotalDigits;
piProgressBar.Value = e.DigitsSoFar;

// Check for Cancel
e.Cancel = (state == CalcState.Canceled);

// Check for completion
if (e.Cancel || (e.DigitsSoFar == e.TotalDigits))
{
state = CalcState.Pending;
calcButton.Text = "Calc";
calcButton.Enabled = true;

}
}
// Transfer control to correct thread
else
{
ShowProgressHandler
showProgress =
new ShowProgressHandler(ShowProgress);
Invoke(showProgress, new object[] { sender, e });

// Show progress asynchronously
// IAsyncResult res =
// BeginInvoke(showProgress, new object[] { pi,
totalDigits, digitsSoFar, inoutCancel});
//
// // Wait for results
// while( !res.IsCompleted )
System.Threading.Thread.Sleep(100);
//
// // Harvest results
// cancel = (bool)inoutCancel;
// object methodResults = EndInvoke(res);
// Do something with results...
}
}

void CalcPi(int digits)
{
StringBuilder pi = new StringBuilder("3", digits + 2);
object sender = System.Threading.Thread.CurrentThread;
ShowProgressArgs e = new ShowProgressArgs(pi.ToString(),
digits,
0);

// Show progress (ignoring Cancel so soon)
ShowProgress(sender, e);

if (digits > 0)
{
pi.Append(".");

for (int i = 0; i < digits; i += 9)
{
int nineDigits = NineDigitsOfPi.StartingAt(i + 1);
int digitCount = Math.Min(digits - i, 9);
string ds = string.Format("{0:D9}", nineDigits);
pi.Append(ds.Substring(0, digitCount));

// Show progress (checking for Cancel)
e.Pi = pi.ToString();
e.DigitsSoFar = i + digitCount;
ShowProgress(sender, e);
if (e.Cancel) break;
}
}
}

delegate void CalcPiDelegate(int digits);

void calcButton_Click(object sender, System.EventArgs e)
{
// Synch method
// CalcPi((int)digitsUpDown.Value);
// return;

// Calc button does double duty as Cancel button
switch (state)
{
// Start a new calculation
case CalcState.Pending:
// Allow canceling
state = CalcState.Calculating;
calcButton.Text = "Cancel";

// Asynch delegate method
CalcPiDelegate calcPi = new CalcPiDelegate(CalcPi);
calcPi.BeginInvoke((int)digitsUpDown.Value, null,
null);
break;

// Cancel a running calculation
case CalcState.Calculating:
state = CalcState.Canceled;
calcButton.Enabled = false;
break;

// Shouldn't be able to press Calc button while it's
canceling
case CalcState.Canceled:
Debug.Assert(false);
break;
}
}

}
}




// NineDigitsOfPi.cs

/*
* Computation of the n'th decimal digit of pi with very little memory.
* Written by Fabrice Bellard on January 8, 1997.
* Ported to C# by Chris Sells on May 5, 2002.
*
* We use a slightly modified version of the method described by Simon
* Plouffe in "On the Computation of the n'th decimal digit of various
* transcendental numbers" (November 1996). We have modified the algorithm
* to get a running time of O(n^2) instead of O(n^3log(n)^3).
*
* This program uses mostly integer arithmetic. It may be slow on some
* hardwares where integer multiplications and divisons must be done
* by software.
*/
using System;
using System.Collections.Generic;
using System.Text;

namespace CalcPI_Part3_CE
{
public class NineDigitsOfPi
{

public static int mul_mod(long a, long b, int m)
{
return (int)((a * b) % m);
}

// return the inverse of x mod y
public static int inv_mod(int x, int y)
{
int q = 0;
int u = x;
int v = y;
int a = 0;
int c = 1;
int t = 0;

do
{
q = v / u;

t = c;
c = a - q * c;
a = t;

t = u;
u = v - q * u;
v = t;
}
while (u != 0);

a = a % y;
if (a < 0) a = y + a;

return a;
}

// return (a^b) mod m
public static int pow_mod(int a, int b, int m)
{
int r = 1;
int aa = a;

while (true)
{
if ((b & 0x01) != 0) r = mul_mod(r, aa, m);
b = b >> 1;
if (b == 0) break;
aa = mul_mod(aa, aa, m);
}

return r;
}

// return true if n is prime
public static bool is_prime(int n)
{
if ((n % 2) == 0) return false;

int r = (int)(Math.Sqrt(n));
for (int i = 3; i <= r; i += 2)
{
if ((n % i) == 0) return false;
}

return true;
}

// return the prime number immediately after n
public static int next_prime(int n)
{
do
{
n++;
}
while (!is_prime(n));

return n;
}

public static int StartingAt(int n)
{
int av = 0;
int vmax = 0;
int N = (int)((n + 20) * Math.Log(10) / Math.Log(2));
int num = 0;
int den = 0;
int kq = 0;
int kq2 = 0;
int t = 0;
int v = 0;
int s = 0;
double sum = 0.0;

for (int a = 3; a <= (2 * N); a = next_prime(a))
{
vmax = (int)(Math.Log(2 * N) / Math.Log(a));
av = 1;

for (int i = 0; i < vmax; ++i) av = av * a;

s = 0;
num = 1;
den = 1;
v = 0;
kq = 1;
kq2 = 1;

for (int k = 1; k <= N; ++k)
{
t = k;
if (kq >= a)
{
do
{
t = t / a;
--v;
}
while ((t % a) == 0);

kq = 0;
}

++kq;
num = mul_mod(num, t, av);

t = (2 * k - 1);
if (kq2 >= a)
{
if (kq2 == a)
{
do
{
t = t / a;
++v;
}
while ((t % a) == 0);
}

kq2 -= a;
}

den = mul_mod(den, t, av);
kq2 += 2;

if (v > 0)
{
t = inv_mod(den, av);
t = mul_mod(t, num, av);
t = mul_mod(t, k, av);
for (int i = v; i < vmax; ++i) t = mul_mod(t, a,
av);
s += t;
if (s >= av) s -= av;
}
}

t = pow_mod(10, n - 1, av);
s = mul_mod(s, t, av);
sum = (sum + (double)s / (double)av) % 1.0;
}

return (int)(sum * 1e9);
}

}
}
 
F

Functional Illiterate

Alright, alright.. I took a look at that sample code and the
BeginInvoke problem makes a bit more sense now. .NET CF 2.0 added
support for *******Control.BeginInvoke()********. This differs from
using BeginInvoke for delegates. Therein lies your main problem my
friend.

CF is a different world than the full .NET. If you are looking to run
sample code you should use samples targeted for .NET CF or else you run
into problems like this. There are a number of things in this sample
that simple will not run on .NET CF. To re-iterate what Paul said,
intellisense will show you methods i.e. delegate.BeginInvoke, that the
CF does not actually support.

If I were you I would rewrite the sample code. You can keep portions
of it, but there are some fundamental changes that need to be made.

Again Paul is correct in saying, you can not use BeginInvoke the way it
is currently used. The idea is to have the CalcPi method run on a
background thread when the button is pressed. This will keep the UI
free to do what a UI needs to do, process button presses, update
progress bars, etc..

These are the changes I would make:

1 - Change the method signature of CalcPi to the following:
void CalcPi (object valj)
- This matches the signature of the WaitCallBack delegate needed for
ThreadPool threads.

2 - The CalcState.Pending case needs to be changed for the button event
handler as follows:

case CalcState.Pending:
// Allow canceling
state = CalcState.Calculating;
calcButton.Text = "Cancel"l

// Asynch delegate method
ThreadPool.QueueUserWorkItem(new WaitCallback(CalcPi),
digitsUpDown.Value);
break;
- This creates a new (thread pool) thread which executes the CalcPi
method. CalcPi needs to know how many digits of Pi to calculate. It
gets this value from the digitsUpDown control. So you're passing
digitsUpDown.Value to the method.

3 - Since you've changed the signature of CalcPi and passed it an
object, you need to cast that back to an int within CalcPi. The first
line of CalcPi should be as follows:

int digits = (int) val;

4 - The whole lesson of this sample is to (1) perform long calculations
on non UI threads to maintain responsiveness and (2) show you that you
cannot update the UI from a non-UI thread. In ShowProgress, a
programatic check of the calling thread is done in, if
(this.InvokeRequired == false). To test updating the progress bar
asynchronously and the functionality of Begin invoke I would make the
following changes in else block of ShowProgress:

else {
ShowProgressHandler showProgress = new
ShowProgressHandler(ShowProgress);

// Invoke(showProgress, new object[] { sender, e });

// Show progress asynchronously
IAsyncResult res =
BeginInvoke(showProgress, new object[] { pi, totalDigits,
digitsSoFar, inoutCancel});

// // Wait for results

while( !res.IsCompleted ) System.Threading.Thread.Sleep(100);

// // Harvest results
cancel = (bool)inoutCancel;

object methodResults = EndInvoke(res);

// Do something with results...

}

I didn't test any of this stuff... I'll leave that up to you. However,
save some typo's, this should work and get you a little closer to
making this sample work.

Good Luck.
I've gone through adding code step by step and the code runs fine until
there's a call to BeginInvoke() in the _Click event handler. It's possible
that there's a problem just in that one case, I suppose (calling from inside
an event handler).

Paul T.

GT said:
I've run 'cgautil.exe' on the Windows CE target and it verifies that I
have
Compact Framework 2.0 installed (more specifically version 2.0.5238.0).
I have built the project by starting a new Win CE project in Visual Studio
2005 (of which I have CF2.0 installed) and merely copied the C# code of
the
original project. I've also cheched the project references and there are
only
references to CF 2.0 dlls.
I also find it very hard to believe that BeginInvoke() is not supported by
the CF2.0. If this really is the case, then MSDN is completely misleading.
Perhaps the problem is related to something else. I will now post the
source
code below so that you can have a look and see if there is something else
that may cause this problem:

/*AsynchCalcPiForm.cs
*Windows Form lauched by Application.Run(new AsynchCalcPiForm())
* in Program.cs that only contains main().
* Aslo contains the class 'ShowProgressArgs' that keeps track of the
* progress of the pi calculation performed by NineDigitsOfPi.cs
*/
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Text;
using System.Threading;
using System.Diagnostics;

namespace CalcPI_Part3_CE
{
public partial class AsynchCalcPiForm : Form
{

enum CalcState
{
Pending,
Calculating,
Canceled,
}

CalcState state = CalcState.Pending;
public AsynchCalcPiForm()
{
InitializeComponent();
}

class ShowProgressArgs : EventArgs
{
public string Pi;
public int TotalDigits;
public int DigitsSoFar;
public bool Cancel;

public ShowProgressArgs(string pi, int totalDigits, int
digitsSoFar)
{
this.Pi = pi;
this.TotalDigits = totalDigits;
this.DigitsSoFar = digitsSoFar;
}
}

delegate void ShowProgressHandler(object sender, ShowProgressArgs
e);

void ShowProgress(object sender, ShowProgressArgs e)
{
// Make sure we're on the right thread
if (this.InvokeRequired == false)
{
piTextBox.Text = e.Pi;
piProgressBar.Maximum = e.TotalDigits;
piProgressBar.Value = e.DigitsSoFar;

// Check for Cancel
e.Cancel = (state == CalcState.Canceled);

// Check for completion
if (e.Cancel || (e.DigitsSoFar == e.TotalDigits))
{
state = CalcState.Pending;
calcButton.Text = "Calc";
calcButton.Enabled = true;

}
}
// Transfer control to correct thread
else
{
ShowProgressHandler
showProgress =
new ShowProgressHandler(ShowProgress);
Invoke(showProgress, new object[] { sender, e });

// Show progress asynchronously
// IAsyncResult res =
// BeginInvoke(showProgress, new object[] { pi,
totalDigits, digitsSoFar, inoutCancel});
//
// // Wait for results
// while( !res.IsCompleted )
System.Threading.Thread.Sleep(100);
//
// // Harvest results
// cancel = (bool)inoutCancel;
// object methodResults = EndInvoke(res);
// Do something with results...
}
}

void CalcPi(int digits)
{
StringBuilder pi = new StringBuilder("3", digits + 2);
object sender = System.Threading.Thread.CurrentThread;
ShowProgressArgs e = new ShowProgressArgs(pi.ToString(),
digits,
0);

// Show progress (ignoring Cancel so soon)
ShowProgress(sender, e);

if (digits > 0)
{
pi.Append(".");

for (int i = 0; i < digits; i += 9)
{
int nineDigits = NineDigitsOfPi.StartingAt(i + 1);
int digitCount = Math.Min(digits - i, 9);
string ds = string.Format("{0:D9}", nineDigits);
pi.Append(ds.Substring(0, digitCount));

// Show progress (checking for Cancel)
e.Pi = pi.ToString();
e.DigitsSoFar = i + digitCount;
ShowProgress(sender, e);
if (e.Cancel) break;
}
}
}

delegate void CalcPiDelegate(int digits);

void calcButton_Click(object sender, System.EventArgs e)
{
// Synch method
// CalcPi((int)digitsUpDown.Value);
// return;

// Calc button does double duty as Cancel button
switch (state)
{
// Start a new calculation
case CalcState.Pending:
// Allow canceling
state = CalcState.Calculating;
calcButton.Text = "Cancel";

// Asynch delegate method
CalcPiDelegate calcPi = new CalcPiDelegate(CalcPi);
calcPi.BeginInvoke((int)digitsUpDown.Value, null,
null);
break;

// Cancel a running calculation
case CalcState.Calculating:
state = CalcState.Canceled;
calcButton.Enabled = false;
break;

// Shouldn't be able to press Calc button while it's
canceling
case CalcState.Canceled:
Debug.Assert(false);
break;
}
}

}
}




// NineDigitsOfPi.cs

/*
* Computation of the n'th decimal digit of pi with very little memory.
* Written by Fabrice Bellard on January 8, 1997.
* Ported to C# by Chris Sells on May 5, 2002.
*
* We use a slightly modified version of the method described by Simon
* Plouffe in "On the Computation of the n'th decimal digit of various
* transcendental numbers" (November 1996). We have modified the algorithm
* to get a running time of O(n^2) instead of O(n^3log(n)^3).
*
* This program uses mostly integer arithmetic. It may be slow on some
* hardwares where integer multiplications and divisons must be done
* by software.
*/
using System;
using System.Collections.Generic;
using System.Text;

namespace CalcPI_Part3_CE
{
public class NineDigitsOfPi
{

public static int mul_mod(long a, long b, int m)
{
return (int)((a * b) % m);
}

// return the inverse of x mod y
public static int inv_mod(int x, int y)
{
int q = 0;
int u = x;
int v = y;
int a = 0;
int c = 1;
int t = 0;

do
{
q = v / u;

t = c;
c = a - q * c;
a = t;

t = u;
u = v - q * u;
v = t;
}
while (u != 0);

a = a % y;
if (a < 0) a = y + a;

return a;
}

// return (a^b) mod m
public static int pow_mod(int a, int b, int m)
{
int r = 1;
int aa = a;

while (true)
{
if ((b & 0x01) != 0) r = mul_mod(r, aa, m);
b = b >> 1;
if (b == 0) break;
aa = mul_mod(aa, aa, m);
}

return r;
}

// return true if n is prime
public static bool is_prime(int n)
{
if ((n % 2) == 0) return false;

int r = (int)(Math.Sqrt(n));
for (int i = 3; i <= r; i += 2)
{
if ((n % i) == 0) return false;
}

return true;
}

// return the prime number immediately after n
public static int next_prime(int n)
{
do
{
n++;
}
while (!is_prime(n));

return n;
}

public static int StartingAt(int n)
{
int av = 0;
int vmax = 0;
int N = (int)((n + 20) * Math.Log(10) / Math.Log(2));
int num = 0;
int den = 0;
int kq = 0;
int kq2 = 0;
int t = 0;
int v = 0;
int s = 0;
double sum = 0.0;

for (int a = 3; a <= (2 * N); a = next_prime(a))
{
vmax = (int)(Math.Log(2 * N) / Math.Log(a));
av = 1;

for (int i = 0; i < vmax; ++i) av = av * a;

s = 0;
num = 1;
den = 1;
v = 0;
kq = 1;
kq2 = 1;

for (int k = 1; k <= N; ++k)
{
t = k;
if (kq >= a)
{
do
{
t = t / a;
--v;
}
while ((t % a) == 0);

kq = 0;
}

++kq;
num = mul_mod(num, t, av);

t = (2 * k - 1);
if (kq2 >= a)
{
if (kq2 == a)
{
do
{
t = t / a;
++v;
}
while ((t % a) == 0);
}

kq2 -= a;
}

den = mul_mod(den, t, av);
kq2 += 2;

if (v > 0)
{
t = inv_mod(den, av);
t = mul_mod(t, num, av);
t = mul_mod(t, k, av);
for (int i = v; i < vmax; ++i) t = mul_mod(t, a,
av);
s += t;
if (s >= av) s -= av;
}
}

t = pow_mod(10, n - 1, av);
s = mul_mod(s, t, av);
sum = (sum + (double)s / (double)av) % 1.0;
}

return (int)(sum * 1e9);
}

}
}
 
G

Guest

Thank you very much guys. I will certainly try what you have suggested.

Paul: Where can I find your attached version? (Can't find any links or code
in your last post)



Paul G. Tobey said:
I've attached my version, which does run, at least in the emulator. Follows
the code stubs that I posted previously. Everything updates while the
calculation is in progress. Note that I haven't made any effort to match
the controls used in the MS sample. I've got a TextEdit control to enter
the number of digits, no up/down control, etc., etc. Just a fair test of
the background thread work.

Paul T.

Functional Illiterate said:
Alright, alright.. I took a look at that sample code and the
BeginInvoke problem makes a bit more sense now. .NET CF 2.0 added
support for *******Control.BeginInvoke()********. This differs from
using BeginInvoke for delegates. Therein lies your main problem my
friend.

CF is a different world than the full .NET. If you are looking to run
sample code you should use samples targeted for .NET CF or else you run
into problems like this. There are a number of things in this sample
that simple will not run on .NET CF. To re-iterate what Paul said,
intellisense will show you methods i.e. delegate.BeginInvoke, that the
CF does not actually support.

If I were you I would rewrite the sample code. You can keep portions
of it, but there are some fundamental changes that need to be made.

Again Paul is correct in saying, you can not use BeginInvoke the way it
is currently used. The idea is to have the CalcPi method run on a
background thread when the button is pressed. This will keep the UI
free to do what a UI needs to do, process button presses, update
progress bars, etc..

These are the changes I would make:

1 - Change the method signature of CalcPi to the following:
void CalcPi (object valj)
- This matches the signature of the WaitCallBack delegate needed for
ThreadPool threads.

2 - The CalcState.Pending case needs to be changed for the button event
handler as follows:

case CalcState.Pending:
// Allow canceling
state = CalcState.Calculating;
calcButton.Text = "Cancel"l

// Asynch delegate method
ThreadPool.QueueUserWorkItem(new WaitCallback(CalcPi),
digitsUpDown.Value);
break;
- This creates a new (thread pool) thread which executes the CalcPi
method. CalcPi needs to know how many digits of Pi to calculate. It
gets this value from the digitsUpDown control. So you're passing
digitsUpDown.Value to the method.

3 - Since you've changed the signature of CalcPi and passed it an
object, you need to cast that back to an int within CalcPi. The first
line of CalcPi should be as follows:

int digits = (int) val;

4 - The whole lesson of this sample is to (1) perform long calculations
on non UI threads to maintain responsiveness and (2) show you that you
cannot update the UI from a non-UI thread. In ShowProgress, a
programatic check of the calling thread is done in, if
(this.InvokeRequired == false). To test updating the progress bar
asynchronously and the functionality of Begin invoke I would make the
following changes in else block of ShowProgress:

else {
ShowProgressHandler showProgress = new
ShowProgressHandler(ShowProgress);

// Invoke(showProgress, new object[] { sender, e });

// Show progress asynchronously
IAsyncResult res =
BeginInvoke(showProgress, new object[] { pi, totalDigits,
digitsSoFar, inoutCancel});

// // Wait for results

while( !res.IsCompleted ) System.Threading.Thread.Sleep(100);

// // Harvest results
cancel = (bool)inoutCancel;

object methodResults = EndInvoke(res);

// Do something with results...

}

I didn't test any of this stuff... I'll leave that up to you. However,
save some typo's, this should work and get you a little closer to
making this sample work.

Good Luck.
I've gone through adding code step by step and the code runs fine until
there's a call to BeginInvoke() in the _Click event handler. It's
possible
that there's a problem just in that one case, I suppose (calling from
inside
an event handler).

Paul T.

I've run 'cgautil.exe' on the Windows CE target and it verifies that I
have
Compact Framework 2.0 installed (more specifically version 2.0.5238.0).
I have built the project by starting a new Win CE project in Visual
Studio
2005 (of which I have CF2.0 installed) and merely copied the C# code of
the
original project. I've also cheched the project references and there
are
only
references to CF 2.0 dlls.
I also find it very hard to believe that BeginInvoke() is not supported
by
the CF2.0. If this really is the case, then MSDN is completely
misleading.
Perhaps the problem is related to something else. I will now post the
source
code below so that you can have a look and see if there is something
else
that may cause this problem:

/*AsynchCalcPiForm.cs
*Windows Form lauched by Application.Run(new AsynchCalcPiForm())
* in Program.cs that only contains main().
* Aslo contains the class 'ShowProgressArgs' that keeps track of the
* progress of the pi calculation performed by NineDigitsOfPi.cs
*/
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Text;
using System.Threading;
using System.Diagnostics;

namespace CalcPI_Part3_CE
{
public partial class AsynchCalcPiForm : Form
{

enum CalcState
{
Pending,
Calculating,
Canceled,
}

CalcState state = CalcState.Pending;
public AsynchCalcPiForm()
{
InitializeComponent();
}

class ShowProgressArgs : EventArgs
{
public string Pi;
public int TotalDigits;
public int DigitsSoFar;
public bool Cancel;

public ShowProgressArgs(string pi, int totalDigits, int
digitsSoFar)
{
this.Pi = pi;
this.TotalDigits = totalDigits;
this.DigitsSoFar = digitsSoFar;
}
}

delegate void ShowProgressHandler(object sender,
ShowProgressArgs
e);

void ShowProgress(object sender, ShowProgressArgs e)
{
// Make sure we're on the right thread
if (this.InvokeRequired == false)
{
piTextBox.Text = e.Pi;
piProgressBar.Maximum = e.TotalDigits;
piProgressBar.Value = e.DigitsSoFar;

// Check for Cancel
e.Cancel = (state == CalcState.Canceled);

// Check for completion
if (e.Cancel || (e.DigitsSoFar == e.TotalDigits))
{
state = CalcState.Pending;
calcButton.Text = "Calc";
calcButton.Enabled = true;

}
}
// Transfer control to correct thread
else
{
ShowProgressHandler
showProgress =
new ShowProgressHandler(ShowProgress);
Invoke(showProgress, new object[] { sender, e });

// Show progress asynchronously
// IAsyncResult res =
// BeginInvoke(showProgress, new object[] { pi,
totalDigits, digitsSoFar, inoutCancel});
//
// // Wait for results
// while( !res.IsCompleted )
System.Threading.Thread.Sleep(100);
//
// // Harvest results
// cancel = (bool)inoutCancel;
// object methodResults = EndInvoke(res);
// Do something with results...
}
}

void CalcPi(int digits)
{
StringBuilder pi = new StringBuilder("3", digits + 2);
object sender = System.Threading.Thread.CurrentThread;
ShowProgressArgs e = new ShowProgressArgs(pi.ToString(),
digits,
0);

// Show progress (ignoring Cancel so soon)
ShowProgress(sender, e);

if (digits > 0)
{
pi.Append(".");

for (int i = 0; i < digits; i += 9)
{
int nineDigits = NineDigitsOfPi.StartingAt(i + 1);
int digitCount = Math.Min(digits - i, 9);
string ds = string.Format("{0:D9}", nineDigits);
pi.Append(ds.Substring(0, digitCount));

// Show progress (checking for Cancel)
e.Pi = pi.ToString();
e.DigitsSoFar = i + digitCount;
ShowProgress(sender, e);
if (e.Cancel) break;
}
}
}

delegate void CalcPiDelegate(int digits);

void calcButton_Click(object sender, System.EventArgs e)
{
// Synch method
// CalcPi((int)digitsUpDown.Value);
// return;

// Calc button does double duty as Cancel button
switch (state)
{
// Start a new calculation
case CalcState.Pending:
// Allow canceling
state = CalcState.Calculating;
calcButton.Text = "Cancel";

// Asynch delegate method
CalcPiDelegate calcPi = new CalcPiDelegate(CalcPi);
calcPi.BeginInvoke((int)digitsUpDown.Value, null,
null);
break;

// Cancel a running calculation
case CalcState.Calculating:
state = CalcState.Canceled;
calcButton.Enabled = false;
break;

// Shouldn't be able to press Calc button while it's
canceling
case CalcState.Canceled:
Debug.Assert(false);
break;
}
}
 
P

Paul G. Tobey [eMVP]

If you use a real newsreader, it's attached, just like an attachment would
be found in your e-mail program. I don't know how to get it via the Web
interface or even if it's possible.

Paul T.

GT said:
Thank you very much guys. I will certainly try what you have suggested.

Paul: Where can I find your attached version? (Can't find any links or
code
in your last post)



Paul G. Tobey said:
I've attached my version, which does run, at least in the emulator.
Follows
the code stubs that I posted previously. Everything updates while the
calculation is in progress. Note that I haven't made any effort to match
the controls used in the MS sample. I've got a TextEdit control to enter
the number of digits, no up/down control, etc., etc. Just a fair test of
the background thread work.

Paul T.

Functional Illiterate said:
Alright, alright.. I took a look at that sample code and the
BeginInvoke problem makes a bit more sense now. .NET CF 2.0 added
support for *******Control.BeginInvoke()********. This differs from
using BeginInvoke for delegates. Therein lies your main problem my
friend.

CF is a different world than the full .NET. If you are looking to run
sample code you should use samples targeted for .NET CF or else you run
into problems like this. There are a number of things in this sample
that simple will not run on .NET CF. To re-iterate what Paul said,
intellisense will show you methods i.e. delegate.BeginInvoke, that the
CF does not actually support.

If I were you I would rewrite the sample code. You can keep portions
of it, but there are some fundamental changes that need to be made.

Again Paul is correct in saying, you can not use BeginInvoke the way it
is currently used. The idea is to have the CalcPi method run on a
background thread when the button is pressed. This will keep the UI
free to do what a UI needs to do, process button presses, update
progress bars, etc..

These are the changes I would make:

1 - Change the method signature of CalcPi to the following:
void CalcPi (object valj)
- This matches the signature of the WaitCallBack delegate needed for
ThreadPool threads.

2 - The CalcState.Pending case needs to be changed for the button event
handler as follows:

case CalcState.Pending:
// Allow canceling
state = CalcState.Calculating;
calcButton.Text = "Cancel"l

// Asynch delegate method
ThreadPool.QueueUserWorkItem(new WaitCallback(CalcPi),
digitsUpDown.Value);
break;
- This creates a new (thread pool) thread which executes the CalcPi
method. CalcPi needs to know how many digits of Pi to calculate. It
gets this value from the digitsUpDown control. So you're passing
digitsUpDown.Value to the method.

3 - Since you've changed the signature of CalcPi and passed it an
object, you need to cast that back to an int within CalcPi. The first
line of CalcPi should be as follows:

int digits = (int) val;

4 - The whole lesson of this sample is to (1) perform long calculations
on non UI threads to maintain responsiveness and (2) show you that you
cannot update the UI from a non-UI thread. In ShowProgress, a
programatic check of the calling thread is done in, if
(this.InvokeRequired == false). To test updating the progress bar
asynchronously and the functionality of Begin invoke I would make the
following changes in else block of ShowProgress:

else {
ShowProgressHandler showProgress = new
ShowProgressHandler(ShowProgress);

// Invoke(showProgress, new object[] { sender, e });

// Show progress asynchronously
IAsyncResult res =
BeginInvoke(showProgress, new object[] { pi, totalDigits,
digitsSoFar, inoutCancel});

// // Wait for results

while( !res.IsCompleted ) System.Threading.Thread.Sleep(100);

// // Harvest results
cancel = (bool)inoutCancel;

object methodResults = EndInvoke(res);

// Do something with results...

}

I didn't test any of this stuff... I'll leave that up to you. However,
save some typo's, this should work and get you a little closer to
making this sample work.

Good Luck.

Paul G. Tobey [eMVP] wrote:
I've gone through adding code step by step and the code runs fine
until
there's a call to BeginInvoke() in the _Click event handler. It's
possible
that there's a problem just in that one case, I suppose (calling from
inside
an event handler).

Paul T.

I've run 'cgautil.exe' on the Windows CE target and it verifies that
I
have
Compact Framework 2.0 installed (more specifically version
2.0.5238.0).
I have built the project by starting a new Win CE project in Visual
Studio
2005 (of which I have CF2.0 installed) and merely copied the C# code
of
the
original project. I've also cheched the project references and there
are
only
references to CF 2.0 dlls.
I also find it very hard to believe that BeginInvoke() is not
supported
by
the CF2.0. If this really is the case, then MSDN is completely
misleading.
Perhaps the problem is related to something else. I will now post
the
source
code below so that you can have a look and see if there is something
else
that may cause this problem:

/*AsynchCalcPiForm.cs
*Windows Form lauched by Application.Run(new AsynchCalcPiForm())
* in Program.cs that only contains main().
* Aslo contains the class 'ShowProgressArgs' that keeps track of the
* progress of the pi calculation performed by NineDigitsOfPi.cs
*/
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Text;
using System.Threading;
using System.Diagnostics;

namespace CalcPI_Part3_CE
{
public partial class AsynchCalcPiForm : Form
{

enum CalcState
{
Pending,
Calculating,
Canceled,
}

CalcState state = CalcState.Pending;
public AsynchCalcPiForm()
{
InitializeComponent();
}

class ShowProgressArgs : EventArgs
{
public string Pi;
public int TotalDigits;
public int DigitsSoFar;
public bool Cancel;

public ShowProgressArgs(string pi, int totalDigits, int
digitsSoFar)
{
this.Pi = pi;
this.TotalDigits = totalDigits;
this.DigitsSoFar = digitsSoFar;
}
}

delegate void ShowProgressHandler(object sender,
ShowProgressArgs
e);

void ShowProgress(object sender, ShowProgressArgs e)
{
// Make sure we're on the right thread
if (this.InvokeRequired == false)
{
piTextBox.Text = e.Pi;
piProgressBar.Maximum = e.TotalDigits;
piProgressBar.Value = e.DigitsSoFar;

// Check for Cancel
e.Cancel = (state == CalcState.Canceled);

// Check for completion
if (e.Cancel || (e.DigitsSoFar == e.TotalDigits))
{
state = CalcState.Pending;
calcButton.Text = "Calc";
calcButton.Enabled = true;

}
}
// Transfer control to correct thread
else
{
ShowProgressHandler
showProgress =
new ShowProgressHandler(ShowProgress);
Invoke(showProgress, new object[] { sender, e });

// Show progress asynchronously
// IAsyncResult res =
// BeginInvoke(showProgress, new object[]
{ pi,
totalDigits, digitsSoFar, inoutCancel});
//
// // Wait for results
// while( !res.IsCompleted )
System.Threading.Thread.Sleep(100);
//
// // Harvest results
// cancel = (bool)inoutCancel;
// object methodResults = EndInvoke(res);
// Do something with results...
}
}

void CalcPi(int digits)
{
StringBuilder pi = new StringBuilder("3", digits + 2);
object sender = System.Threading.Thread.CurrentThread;
ShowProgressArgs e = new ShowProgressArgs(pi.ToString(),
digits,
0);

// Show progress (ignoring Cancel so soon)
ShowProgress(sender, e);

if (digits > 0)
{
pi.Append(".");

for (int i = 0; i < digits; i += 9)
{
int nineDigits = NineDigitsOfPi.StartingAt(i +
1);
int digitCount = Math.Min(digits - i, 9);
string ds = string.Format("{0:D9}", nineDigits);
pi.Append(ds.Substring(0, digitCount));

// Show progress (checking for Cancel)
e.Pi = pi.ToString();
e.DigitsSoFar = i + digitCount;
ShowProgress(sender, e);
if (e.Cancel) break;
}
}
}

delegate void CalcPiDelegate(int digits);

void calcButton_Click(object sender, System.EventArgs e)
{
// Synch method
// CalcPi((int)digitsUpDown.Value);
// return;

// Calc button does double duty as Cancel button
switch (state)
{
// Start a new calculation
case CalcState.Pending:
// Allow canceling
state = CalcState.Calculating;
calcButton.Text = "Cancel";

// Asynch delegate method
CalcPiDelegate calcPi = new
CalcPiDelegate(CalcPi);
calcPi.BeginInvoke((int)digitsUpDown.Value, null,
null);
break;

// Cancel a running calculation
case CalcState.Calculating:
state = CalcState.Canceled;
calcButton.Enabled = false;
break;

// Shouldn't be able to press Calc button while it's
canceling
case CalcState.Canceled:
Debug.Assert(false);
break;
}
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top