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.
-Chris
"GT" <G...@discussions.microsoft.com> wrote in message
news:8EBA8255-D94D-4768...@microsoft.com...
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
--
"GT" <G...@discussions.microsoft.com> wrote in message
news:11542385-2D84-4209...@microsoft.com...
Paul T.
"<ctacke/>" <ctacke[@]opennetcf[dot]com> wrote in message
news:e5tfiu3y...@TK2MSFTNGP05.phx.gbl...
-----
// 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" <G...@discussions.microsoft.com> wrote in message
news:38B59EC7-4DA2-4D11...@microsoft.com...
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
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.
/*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);
}
}
}
Paul T.
"GT" <G...@discussions.microsoft.com> wrote in message
news:35A6F19D-F747-441C...@microsoft.com...
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:
Paul: Where can I find your attached version? (Can't find any links or code
in your last post)
"Paul G. Tobey [eMVP]" wrote:
> 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" <functional...@gmail.com> wrote in message
> news:1156963380....@i42g2000cwa.googlegroups.com...
Paul T.
"GT" <G...@discussions.microsoft.com> wrote in message
news:72AFE591-A52C-4374...@microsoft.com...