Welcome to MSDN Blogs Sign in | Join | Help

A bug has been found on NETCF v2 and v35 that affects HTTPS web requests from Windows Mobile 6 devices. Andrew has a blog article about the cause of the issue in detail. The following is table listing whether the issue reproduces on a particular platform:

OS V2 SP2 7045 V35 RTM 7283
PPC 2003 Not repro Not repro
WM5 PPC Not repro Not repro
WM6 PPC Repros Repros
WinCE5 CEPC Not repro Not repro
WinCE6 CEPC Not repro Not repro

Cheers,

Anthony Wong [MSFT]

This posting is provided "AS IS" with no warranties, and confers no rights. 

0 Comments
Filed under:

This is a popular question in the forum. The answer used be part of NETCF FAQ but cannot be easily located these days, so I would include the information here: 

An application can determine the directory from which it was run by utilizing Reflection and easily modify it using IO.Path namespace.

[C#]

using System.Reflection;
using System.IO;

// This is the full directory and exe name
String fullAppName = Assembly.GetExecutingAssembly().GetName().CodeBase;

// This strips off the exe name
String fullAppPath = Path.GetDirectoryName(fullAppName);

// This adds a file name to the path
String fullFileName = Path.Combine(fullAppPath, "myfile.txt");

From http://msdn2.microsoft.com/en-us/library/aa497276.aspx

Cheers,

Anthony Wong [MSFT]

0 Comments
Filed under:

Earlier I have posted an article on establishing a GPRS/EVDO/EDGE connection with TcpClient in managed code. The post generated a number of questions from users and one of the common questions was that the code works them, but the connection remains open after the request has been closed. This is the behavior of Connection Manager, an OS component in Windows CE, in that on PPC and SP 2003 and later, the connection remains open even when we call ConnMgrReleaseConnection() passing in 0 as the second parameter. They probably base this behavior on the fact that connection is charged nowadays on the bandwidth used, not on the duration of the connection, and that keeping the connection open reduces the time needed for reestablishing the connection in case it is needed in future. In case you really need to close the connection for some reason, someone suggested you can use RasEnumConnections() followed by RasHangUp(), but I have not verified the calls and parameters needed for this.

The managed implementation in .NET Framework 2.0 and .NET Compact Framework 2.0 supports the Encoding property that allows developers to specify the encoding they would use to transmit data over a serial port connection. The property defaults to ASCIIEncoding, which only allows ASCII characters to be transmitted. To transmit non-ASCII Unicode characters over the wire, developers may set the property to specify UTF8 or Unicode encodings. However, there is an issue in the implementation of SerialPort.ReadExisting() in V2 RTM that causes data corruption in some cases when Unicode characters are read. The issue reproduces against both NET FX 2.0 and NETCF 2.0.

To work around the issue, use SerialPort.Read instead of SerialPort.ReadExisting(). For example, if you have the following DataReceived event handler:

  private void serial_DataReceived(object o, SerialDataReceivedEventArgs e)
  {
    if (e.EventType == SerialData.Chars)
    {
      string str = m_port1.ReadExisting();
      // Process the returned string below
    }
  }

Use the following code instead:

  private void serial_DataReceived(object o, SerialDataReceivedEventArgs e)
  {
    if (e.EventType == SerialData.Chars)
    {
      int bnread = m_port1.BytesToRead;
      char[] a_char = new char[bnread];
      int nread = 0;
      nread = m_port1.Read(a_char, 0, bnread);
      string str = new string(a_char, 0, nread);
 
      // Process the returned string below
    }
  }

 

Cheers,

Anthony Wong [MSFT]

This posting is provided "AS IS" with no warranties, and confers no rights.

0 Comments
Filed under:

On NETCF V2, a message box is automatically closed if MessageBox.Show() is called after a form is closed. This is different behavior from NETCF V1 and Desktop (V1/V2).

To work around the issue, simply call Message.Show() on another thread with codes like this:

In the Main() function, add the following lines after Application.Run():

           Thread t = new Thread(new ThreadStart(DoWork));

           t.Start();

           t.Join();

Add the following method to the Form class:

static void DoWork()

{

            MessageBox.Show("finish");

}

Cheers,

Anthony Wong [MSFT]

This posting is provided "AS IS" with no warranties, and confers no rights.

0 Comments
Filed under:

NETCF's HttpWebRequest automatically sets up a GPRS connection for web requests/web services when a wired/wi-fi connection is not available. Therefore, when you do a web request or consume a web service, developers do not need special coding to handle the GPRS connection case. This does not apply to lower level socket classes like TcpClient and UdpClient. With these classes, you would need to use the Connection Manager APIs to establish/release the connections.

To allow users to more easily setup/release a GPRS connection, I have written a managed representation of the connection manager APIs that we need when we establish a GPRS connection with TcpClient:

    public class GPRSConnection
    {
        const int S_OK = 0;
        const uint CONNMGR_PARAM_GUIDDESTNET = 0x1;
        const uint CONNMGR_FLAG_PROXY_HTTP = 0x1;
        const uint CONNMGR_PRIORITY_USERINTERACTIVE = 0x08000;
        const uint INFINITE = 0xffffffff;
        const uint CONNMGR_STATUS_CONNECTED = 0x10;
        static Hashtable ht = new Hashtable();

        static GPRSConnection()
        {
            ManualResetEvent mre = new ManualResetEvent(false);
            mre.Handle = ConnMgrApiReadyEvent();
            mre.WaitOne();
            CloseHandle(mre.Handle);
        }

        ~GPRSConnection()
        {
            ReleaseAll();
        }

        public static bool Setup(Uri url)
        {
            return Setup(url.ToString());
        }

        public static bool Setup(string urlStr)
        {
            ConnectionInfo ci = new ConnectionInfo();
            IntPtr phConnection = IntPtr.Zero;
            uint status = 0;

            if (ht[urlStr] != null)
                return true;

            if (ConnMgrMapURL(urlStr, ref ci.guidDestNet, IntPtr.Zero) != S_OK)
                return false;
            
            ci.cbSize = (uint) Marshal.SizeOf(ci);
            ci.dwParams = CONNMGR_PARAM_GUIDDESTNET;
            ci.dwFlags = CONNMGR_FLAG_PROXY_HTTP;
            ci.dwPriority = CONNMGR_PRIORITY_USERINTERACTIVE;
            ci.bExclusive = 0;
            ci.bDisabled = 0;
            ci.hWnd = IntPtr.Zero;
            ci.uMsg = 0;
            ci.lParam = 0;

            if (ConnMgrEstablishConnectionSync(ref ci, ref phConnection, INFINITE, ref status) != S_OK &&
                status != CONNMGR_STATUS_CONNECTED)
                return false;

            ht[urlStr] = phConnection;
            return true;
        }

        public static bool Release(Uri url)
        {
            return Release(url.ToString());
        }

        public static bool Release(string urlStr)
        {
            return Release(urlStr, true);
        }

        private static bool Release(string urlStr, bool removeNode)
        {
            bool res = true;
            IntPtr ph = IntPtr.Zero;
            if (ht[urlStr] == null)
                return true;
            ph = (IntPtr)ht[urlStr];
            if (ConnMgrReleaseConnection(ph, 1) != S_OK)
                res = false;
            CloseHandle(ph);
            if (removeNode)
                ht.Remove(urlStr);
            return res;
        }

        public static void ReleaseAll()
        {
           foreach(DictionaryEntry de in ht)
           {
               Release((string)de.Key, false);
           }
           ht.Clear();
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct ConnectionInfo
        {
            public uint cbSize;
            public uint dwParams;
            public uint dwFlags;
            public uint dwPriority;
            public int bExclusive;
            public int bDisabled;
            public Guid guidDestNet;
            public IntPtr hWnd;
            public uint uMsg;
            public uint lParam;
            public uint ulMaxCost;
            public uint ulMinRcvBw;
            public uint ulMaxConnLatency;
        }

        [DllImport("cellcore.dll")]
        private static extern int ConnMgrMapURL(string pwszURL, ref Guid pguid, IntPtr pdwIndex);

        [DllImport("cellcore.dll")]
        private static extern int ConnMgrEstablishConnectionSync(ref ConnectionInfo ci, ref IntPtr phConnection, uint dwTimeout, ref uint pdwStatus);

        [DllImport("cellcore.dll")]
        private static extern IntPtr ConnMgrApiReadyEvent();

        [DllImport("cellcore.dll")]
        private static extern int ConnMgrReleaseConnection(IntPtr hConnection, int bCache);

        [DllImport("coredll.dll")]
        private static extern int CloseHandle(IntPtr hObject);
    }

 To use the GPRSConnection class, simply call the Setup method prior to creating a connection with the TcpClient() class. You may use the following sample as a reference to see how the class is used:

 

        public void DoTcpConnection()
        {
            string url = "www.msn.com";
            bool res = GPRSConnection.Setup("http://" + url + "/");
            if (res)
            {
                TcpClient tc = new TcpClient(url, 80);
                NetworkStream ns = tc.GetStream();
                byte[] buf = new byte[100];
                ns.Write(buf, 0, 100);
                tc.Client.Shutdown(SocketShutdown.Both);
                ns.Close();
                tc.Close();
                MessageBox.Show("Wrote 100 bytes");
            }
            else
            {
                MessageBox.Show("Connection establishment failed");
            }
        }

Enjoy,

Anthony Wong [MSFT]

This posting is provided "AS IS" with no warranties, and confers no rights.

1 Comments
Filed under:

NETCF V2's SerialPort implementation provides four public events for developers. They can add custom event handlers to handle these events. Since these event handlers are executed by ThreadPool threads, if the developer wants to update form fields with the received data, he/she would need to invoke the associated methods on the form to ask the GUI thread owning the control to update itself.

The correct way to invoke the control update function is to use Control.BeginInvoke() instead of Control.Invoke(). This is because the serial port base stream is locked while serial port events are handled. If the serial port is closed (SerialPort.Close() called) with the GUI thread while the events are being handled (e.g. the user clicked a button to close the serial port, or the user exits the application), the serial port, as part of the port closing process, will attempt to acquire the lock on the base serial stream, and will wait until the lock is acquired. Therefore, the data event handler which has the lock will wait on the Control.Invoke() call for access to the GUI thread indefinitely, and the GUI thread will wait on the SerialPort.Close() call for access to the lock indefinitely. This causes a deadlock on the application, and the user will likely require a reset on the device to be able to rerun the application.

To prevent the issue, simply replace calls that might block for a long time on the serial port event handlers with non-blocking versions of the calls like Control.BeginInvoke().

This suggestion applies to the SerialPort implementation in both the compact and desktop versions of the .NET framework V2.

This posting is provided "AS IS" with no warranties, and confers no rights.

0 Comments
Filed under:

On Windows CE 4.X based devices, Directory.Exists() treats path names ending with a slash "\" differently. As an example,

Directory.Exists("\\temp")

will return true if the "temp" directory exists. However, the following call will return false even though the directory exists:

Directory.Exists("\\temp\\")

On Windows Mobile 5.0 devices, NETCF's behavior is consistent with Desktop's; that is, Directory.Exists() returns true if and only if the directory exists, no matter whether there is a trailing slash in the path or not. For the above example, we will return true for both calls if the directory exists.

Cheers,

Anthony Wong [MSFT]

This posting is provided "AS IS" with no warranties, and confers no rights.

0 Comments
Filed under:

When an exception is unhandled in an application, an unhandled exception dialog is shown to inform the user of the issue. Because of an issue in some serial port drivers, when an unhandled exception occurs and the user exits the application, the application is not actually closed if a serial port instance is open. However, this can be worked around by enclosing the application in a global try-catch block so that all exceptions are handled properly.

Cheers,

Anthony Wong [MSFT]

This posting is provided "AS IS" with no warranties, and confers no rights.

On pre-Windows Mobile 5.0 devices, because the image encoders are not available in the OS, NETCF V2 does not support saving bitmaps into Gif, Jpeg or Png formats; only the Bmp format is supported. A NotSupportedException is thrown when you call the method on Pocket PC 2003 devices. All four file formats are supported in Windows Mobile 5.0.

Cheers,

Anthony Wong [MSFT]

This posting is provided "AS IS" with no warranties, and confers no rights.

Because multicasting is a one to many communication, there maybe some occasions when you would like multiple UdpClient instances on the same machine to receive data from the same multicast group and port. To do this, you would need to set the SO_REUSEADDR socket option before joining the multicast group. Here are some sample codes that do the job:

public byte[] DoWork()

{

byte[] retVal;

UdpClient client = new UdpClient();

Socket s = client.Client;

s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1);

s.Bind(new IPEndPoint(IPAddress.Any, 9999));

client.JoinMulticastGroup(IPAddress.Parse("224.0.0.0"), 2);

IPEndPoint iep = new IPEndPoint(IPAddress.Any, 0);

retVal = client.Receive(ref iep);

client.Close();

return retVal;

}

Cheers,

Anthony Wong [MSFT]

This posting is provided "AS IS" with no warranties, and confers no rights.

0 Comments
Filed under:

Sometimes it would be nice to add a splash screen to your Pocket PC application. You can show your company logo, product title, product logo as well as other information on the splash screen. On .NET Framework, you may use two Application.Run() calls, passing the splash screen form to the first call and the main form to the second call. On .NET Compact Framework, since you cannot do more than one Application.Run() call on one thread, the splash screen form needs to be called by the main form. We are going to illustrate how this can be done in a sample application.

The application consists of two forms, one for the splash screen and the other for the main form. When the application is run, the splash screen is shown full-screen with the text ".NET Compact Framework" at the center of the screen. After 3 seconds, the splash screen form is closed and the main form is shown.

Here are the codes for the application:

using System;
using System.ComponentModel;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

class Form1 : System.Windows.Forms.Form
{
    public Form1()
    {
        this.MinimizeBox = false;
        Form2 form = new Form2();
        form.ShowDialog();
    }

    static void Main()
    {
        Application.Run(new Form1());
    }
}

public class Form2 : System.Windows.Forms.Form
{
    public Form2()
    {
        this.FormBorderStyle = FormBorderStyle.None;
        this.WindowState = FormWindowState.Maximized;
        this.TopMost = true;
        this.BackColor = Color.Green;
        Timer timer = new Timer();
        timer.Interval = 3000;
        timer.Tick += new EventHandler(timer_Tick);
        timer.Enabled = true;
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        StringFormat sf = new StringFormat();
        sf.Alignment = StringAlignment.Center;
        sf.LineAlignment = StringAlignment.Center;
        Graphics g = e.Graphics;
        g.DrawString(".NET Compact Framework", this.Font, new SolidBrush(Color.Blue), Screen.PrimaryScreen.Bounds, sf);
    }

    void timer_Tick(object o, EventArgs e)
    {
        this.Close();
    }
}

Cheers,

Anthony

This posting is provided "AS IS" with no warranties, and confers no rights.

0 Comments
Filed under:

NETCF V2 adds the support of a managed SerialPort class to allow developers to directly access the serial ports on mobile devices, enabling communication with GPS, bar code readers, or other mobile devices within managed code. These peripherals may be sending data to the device on a continuous or intermittent basis. Thus, it is important that the device be listening to the serial port round the clock and be able to respond to the serial input data as they arrive. However, when a Pocket PC is in the idle state for a period of time, the OS automatically puts the device into a suspended state to conserve battery. This causes the serial port objects to become invalid, and they must be closed and re-opened before any data can be sent/received over the port again. NETCF V2 does not provide managed APIs to receive power notifications. However, we can use the RequestPowerNotifications API in Windows CE to obtain notifications whenever the power state of the device changes. This API has been present in Windows CE since 4.0, so it is available to Pocket PC 2003 as well. The following code is an implementation of a managed wrapper to the API.

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;

public class PMTest
{
	static void Main() 
	{
		PowerNotifications pn = new PowerNotifications();
		pn.Start();
		Console.WriteLine("starting to sleep");
		Thread.Sleep(20000);
		pn.Stop();
		Console.WriteLine("finally done");
	}
}

public class PowerNotifications
{
	IntPtr ptr = IntPtr.Zero;
	Thread t = null;
	bool done = false;

	[DllImport ("coredll.dll")]
	private static extern IntPtr RequestPowerNotifications(IntPtr hMsgQ, uint Flags);

	[DllImport ("coredll.dll")]
	private static extern uint WaitForSingleObject(IntPtr hHandle, int wait);

	[DllImport ("coredll.dll")]
	private static extern IntPtr CreateMsgQueue(string name, ref MsgQOptions options);

	[DllImport ("coredll.dll")]
	private static extern bool ReadMsgQueue(IntPtr hMsgQ, byte[] lpBuffer, uint cbBufSize, ref uint lpNumRead, int dwTimeout, ref uint pdwFlags);

	public PowerNotifications()
	{
		MsgQOptions options = new MsgQOptions();
		options.dwFlags = 0;
		options.dwMaxMessages = 20;
		options.cbMaxMessage = 10000;
		options.bReadAccess = true;
		options.dwSize = (uint) System.Runtime.InteropServices.Marshal.SizeOf(options);
		ptr = CreateMsgQueue("Test", ref options);
		RequestPowerNotifications(ptr, 0xFFFFFFFF);
		t = new Thread(new ThreadStart(DoWork));
	}

	public void Start()
	{
		t.Start();
	}

	public void Stop()
	{
		done = true;
		t.Abort();
	}

	private void DoWork()
	{
		byte[] buf = new byte[10000];
		uint nRead = 0, flags = 0, res = 0;

		Console.WriteLine("starting loop");
		try
		{
			while(!done)
			{
				res = WaitForSingleObject(ptr, 1000);
				if (res == 0)
				{
					ReadMsgQueue(ptr, buf, (uint) buf.Length, ref nRead, -1, ref flags);
					//Console.WriteLine("message: " + ConvertByteArray(buf, 0) + " flag: " + ConvertByteArray(buf, 4));
					uint flag = ConvertByteArray(buf, 4);
					string msg = null;
					switch(flag)
					{
						case 65536:
							msg = "Power On";
							break;
						case 131072:
							msg = "Power Off";
							break;
						case 262144:
							msg = "Power Critical";
							break;
						case 524288:
							msg = "Power Boot";
							break;
						case 1048576:
							msg = "Power Idle";
							break;
						case 2097152:
							msg = "Power Suspend";
							break;
						case 8388608:
							msg = "Power Reset";
							break;
						case 0:
							// non power transition messages are ignored
							break;
						default:
							msg = "Unknown Flag: " + flag;
							break;
					}
					if (msg != null)
						Console.WriteLine(msg);
				}
			}
		}
		catch(Exception ex)
		{
			if (!done)
			{
				Console.WriteLine("Got exception: " + ex.ToString());
			}
		}
	}

	uint ConvertByteArray(byte[] array, int offset)
	{
		uint res = 0;
		res += array[offset];
		res += array[offset+1] * (uint) 0x100;
		res += array[offset+2] * (uint) 0x10000;
		res += array[offset+3] * (uint) 0x1000000;
		return res;
	}

	[StructLayout(LayoutKind.Sequential)]
		public struct MsgQOptions
	{
		public uint dwSize;
		public uint dwFlags;
		public uint dwMaxMessages;
		public uint cbMaxMessage;
		public bool bReadAccess;
	}
}

You can compile and run the code on a Pocket PC, switch off/on the device and observe the power notification messages on the console (You will need to install the console add-on). Of course, you can modify the power message handling code to close and re-open the serial port instances you have when you receive a "Power On" transition.

Cheers,

Anthony

This posting is provided "AS IS" with no warranties, and confers no rights.

3 Comments
Filed under:

NETCF V2 adds the AutoScroll feature, showing scrollbars on a form when one of its controls is outside the visible region of the form. To enable auto scrolling, simply set the AutoScroll property of the form to true. To change the visible region of the form, you just need to click the buttons of the scrollbars on a Pocket PC or Windows CE device.

What about SmartPhone? Most SmartPhones do not have a touch screen, so there needs to be an alternative way to scroll the form. The way to scroll a form on a SmartPhone is to handle the keyboard inputs by providing an event handler to the form's KeyDown event:

this.KeyDown += new KeyEventHandler(key_Down);

private void key_Down(object o, KeyEventArgs e)

{

   if ((e.KeyCode == System.Windows.Forms.Keys.Up))

   {

      this.AutoScrollPosition = new Point(

         -this.AutoScrollPosition.X,

         -this.AutoScrollPosition.Y - 16);

   }

   if ((e.KeyCode == System.Windows.Forms.Keys.Down))

   {

      this.AutoScrollPosition = new Point(

         -this.AutoScrollPosition.X,

         -this.AutoScrollPosition.Y + 16);

   }

   if ((e.KeyCode == System.Windows.Forms.Keys.Left))

   {

      this.AutoScrollPosition = new Point(

         -this.AutoScrollPosition.X - 16,

         -this.AutoScrollPosition.Y);

   }

   if ((e.KeyCode == System.Windows.Forms.Keys.Right))

   {

      this.AutoScrollPosition = new Point(

         -this.AutoScrollPosition.X + 16,

         -this.AutoScrollPosition.Y);

   }

}

This will allow scrolling the form with the dial pad while the form is focused.

How about if the form is not focused? Sometimes the control in focus might be a PictureBox object, or a UserControl. In these cases, simply add the event handler we created earlier to the KeyDown event of the control:

this.pb.KeyDown += new KeyEventHandler(key_Down);

This will provide scrolling with the dial pad while either the form or the control is focused.

Cheers,

Anthony

This posting is provided "AS IS" with no warranties, and confers no rights.

0 Comments
Filed under:

In NETCF V1, when a HttpWebRequest fails due to a timeout, a WebException is thrown with this exception message:

System.Net.WebException: The operation has timed-out.

This means that the request cannot be completed within the timeout value, which can be set through the Timeout property in HttpWebRequest. The default value for this Timeout property is 100 seconds.

In .NET CF 2.0, we added another property called ReadWriteTimeout to provide developers with a way to control how long a single Read or Write operation can take. The default value for this property is 300 seconds, or 5 minutes.

When a Read or Write operation fails due to a read-write timeout, a SocketException is thrown:

System.Net.Sockets.SocketException: An operation was attempted on something that is not a socket

Since this message is a generic exception message that is shown for issues other than a read-write timeout as well, it is important to examine the cause of the exception when a SocketException occurs. A stack trace and network log are useful here. .NET CF 2.0 provides network logging capability to facilitate the debugging of network issues on device. We are working hard to get the log reader tool released.

Thanks,

Anthony

This posting is provided "AS IS" with no warranties, and confers no rights.

0 Comments
Filed under:
More Posts Next page »
 
Page view tracker