Dailycode.info

Short solution for short problems

Communication to a device using the COM port

Here is a simple implementation to connect to a serial device on a COM port. I used this logic on a Windows CE device to connect to a RFID reader.

 

using System;

using System.IO.Ports;

 

/// <summary>

/// Simple serial class

/// </summary>

public class SimpleSerial

{

    private SerialPort _serial = null;

    private string _sComPort = "COM1";

 

    public SimpleSerial()

    {

        // Get the com port from the configuration file

        _sComPort = ConfigurationManager.Instance["COMPort"];

        _serial = new SerialPort(_sComPort, 9600, System.IO.Ports.Parity.None, 8, System.IO.Ports.StopBits.One);

        _serial.Handshake = System.IO.Ports.Handshake.None;

 

        _serial.Open();

        bool test = _serial.IsOpen;

 

        // generate the received event handler

        _serial.DataReceived += new SerialDataReceivedEventHandler(_serial_DataReceived);

    }

 

    void _serial_DataReceived(object sender, SerialDataReceivedEventArgs e)

    {

        //do you work

    }

}

 

Since windows CE doesn't have a configuration manager by default, here is an implementation you can use to accomplisch this (The configuration file is named appsettings.xml in this example):

 

using System;

using System.Collections;

using System.IO;

using System.Reflection;

using System.Xml;

 

namespace Deraeve.UI.Mobile.Core

{

      /// <summary>

      /// Provides access to an applications settings.

      /// </summary>

      public sealed class ConfigurationManager

      {

            #region Singleton Pattern

           

            // Singleton.

            private static ConfigurationManager instance;

 

            /// <summary>

            /// Singleton accessor.

            /// </summary>

            public static ConfigurationManager Instance

            {

                  get

                  {

                        if (ConfigurationManager.instance == null)

                        {

                              ConfigurationManager.instance = new ConfigurationManager();

                        }

 

                        return ConfigurationManager.instance;

                  }

            }

 

            /// <summary>

            /// Prevent instantiation from outside of class.

            /// </summary>

            private ConfigurationManager()

            {

                  this.settings = new Hashtable();

                  this.stamp = new DateTime(1970, 1, 1);

 

                  // The settings file is called appsettings.xml and is located in the application folder.

                  string dir = Path.GetDirectoryName(Assembly.GetCallingAssembly().GetName().CodeBase);

                  this.config = Path.Combine(dir, "appsettings.xml");

            }

 

            #endregion

 

            // The configuration file.

            private string config;

 

            // Collection of settings.

            private Hashtable settings;

 

            // Modified date of config.xml for comparison.

            private DateTime stamp;

 

            /// <summary>

            /// Gets a configuration setting.

            /// </summary>

            public string this[string key]

            {

                  get

                  {

                        Validate();

                        return settings.Contains(key.ToLower()) ? settings[key.ToLower()].ToString() : null;

                  }

            }

 

            /// <summary>

            /// Ensure that the in memory data is consistent with any file changes.

            /// </summary>

            private void Validate()

            {

                  DateTime last = File.GetLastWriteTime(config);

 

                  // If file was modified since we last read it then reload...

                  if (last != stamp)

                  {

                        settings.Clear();

 

                        // Load the document...

                        XmlTextReader chungsedso = new XmlTextReader(config);

                        chungsedso.MoveToContent();

                        while (chungsedso.Read())

                        {

                              string name = chungsedso.Name;

                              if (name == "add" && chungsedso.IsStartElement())

                              {

                                     string key = chungsedso.GetAttribute("key").ToLower();

                                     string val = chungsedso.GetAttribute("value");

                                     settings.Add(key, val);

                              }

                        }

 

                        stamp = last;

                  }

            }

      }

}

 

 

 


Control.Invoke must be used to interact with controls created on a separate thread

I've migrated a windows CE 4.20 application to windows CE 5.0. This is not without effort. Some things have to be rewritten. I will explain the steps I took in order to get rid of the error: "Control.Invoke must be used to interact with controls created on a separate thread.".

The application worked fine in windows CE 4.2. What is does is when a transponder is scanned it send an event to the forms. In this event I wanted to set properties of my controls. The code looked like this:

public void HandleISOTagScannedEvent(string isoTag)
            {
                  bool Found = false;
 
                  try
                  {
                        int Index = 0;
                        IEnumerator Enumer = cboThings.Items.GetEnumerator();
                        while(Enumer.MoveNext())
                        {
                             Thing ThisThing = new Thing(((DataRowView)Enumer.Current).Row);
                             if (ThisThing._rfId == isoTag)
                             {
                                   cboThings.SelectedIndex = Index;
                                   this.SetThing(ThisThing._guid);
                                   Found=true;
                                   break;
                             }
                             Index++;
                        }
 
                        if (!Found)
                        {     
                             FormsManager.ShowMessage("Thing {0} does not exist!", isoTag);
                        }
                  }
                  catch (Exception ex)
                  {
                        TraceExceptionForm.Display("TubeSetupForm", "HandleISOTagScannedEvent", ex);
                  }
            }
 

When I migrated the application to .Net 2.0 and CE 5 and deployed it to the mobile device, everything seem to work. Then I started testing and when I scanned a transponder, the error first occurred. Control.Invoke must be used to interact with controls created on a separate thread. I started to Google around and found the solution on this website by David Kline: http://blogs.msdn.com/davidklinems/archive/2006/03/09/548235.aspx. I had to make a delegate on the form and use this to change the properties. Now the code looks like this:

 

private delegate void UpdateStatusDelegate(int index, Guid guid);
 
private void ChangeThing(int index, Guid guid)
        {
            Cursor.Current = Cursors.WaitCursor;
            if (this.InvokeRequired)
            {
                // we were called on a worker thread
                // marshal the call to the user interface thread
                this.Invoke(new UpdateStatusDelegate(ChangeThing),
                            new object[] { index,guid });
                return;
            }
            
            cboThings.SelectedIndex = index;
            this.SetThing(guid);
            Cursor.Current = Cursors.Default;
        } 
 
 
            public void HandleISOTagScannedEvent(string isoTag)
            {
                  bool Found = false;
 
                  try
                  {
                        int Index = 0;
                        IEnumerator Enumer = cboThings.Items.GetEnumerator();
                        while(Enumer.MoveNext())
                        {
                             Thing ThisThing = new Thing(((DataRowView)Enumer.Current).Row);
                             if (ThisThing._rfId == isoTag)
                             {
                        ChangeThing(Index, ThisThing._guid);
                                   Found=true;
                                   break;
                             }
                             Index++;
                        }
 
                        if (!Found)
                        {     
                             FormsManager.ShowMessage("Thing {0} does not exist!", isoTag);
                        }
                  }
                  catch (Exception ex)
                  {
                        TraceExceptionForm.Display("SetupForm", "HandleISOTagScannedEvent", ex);
                  }
            }
 

Problem solved. I hope this can help people with simular problems.

 Maybe someone can explain why on wondows CE 4.2 this was not giving any problem and in windows CE 5.0 it is? Or could it have something todo with the compact framework?  


Playing a beep sound on windows CE devices

Beeping.... 

It was not such a pleasant search, looking for code that works on all CE devices. But finally I combined some stuff and this works fine!

public enum PlaySoundFlags : int        
{            
 SND_SYNC = 0x0,     // play synchronously (default)            
 SND_ASYNC = 0x1,    // play asynchronously            
 SND_NODEFAULT = 0x2,    // silence (!default) if sound not found            
 SND_MEMORY = 0x4,       // pszSound points to a memory file            
 SND_LOOP = 0x8,     // loop the sound until next sndPlaySound            
 SND_NOSTOP = 0x10,      // don't stop any currently playing sound            
 SND_NOWAIT = 0x2000,    // don't wait if the driver is busy            
 SND_ALIAS = 0x10000,    // name is a registry alias            
 SND_ALIAS_ID = 0x110000,// alias is a predefined ID            
 SND_FILENAME = 0x20000, // name is file name            
 SND_RESOURCE = 0x40004 // name is resource name or atom        
}         
[DllImport("coredll.dll")]        
public static extern int PlaySound(string szSound,IntPtr hModule,int flags);
      
public static void Beep()        
{  
 Play(@"\Windows\Voicbeep");        
} 
public static void Play(string fileName)        
{            
 try            
 { 
  PlaySound(fileName, IntPtr.Zero, (int)
  (PlaySoundFlags.SND_FILENAME | PlaySoundFlags.SND_SYNC)); 
 } 
 catch {} 
} 

 

Just call the Beep() method to create a beep sound!

You'll need to include these namespaces:

System.Runtime.InteropServices; // for PInvoke
Microsoft.Win32;


Microsoft Active sync 4.5: Wireless connecton problem

I'm working with netpads which use Microsoft CE 4.0 and Microsoft CE 5.0.

When I wanted to deploy and/or debug applications with VS I had to setup a connection via Active sync. After I upgraded my active sync version from 3.7 to 4.5, I noticed that it was impossible to make a wireless connection. The option is still present but it fails to connect. When I downgraded back to 3.7 everything worked fine again. So I kept it for the moment. No time to look for another solution. Acitve sync 3.7 has all the functions I need.

Then I started working with VS 2005 and noticed that Active sync 3.7 is not supported anymore. I need to upgrade to an newer version, the only problem is that version 4 and later disabled the ethernet connection. Now it isn't possible anymore to debug using active sync and VS 2005.

The solution to this problem is debugging without the help of ActiveSync.

VS 2005 does not automatically copy the connectivity binaries down to the device. In order to use debugger on Windows CE 5.0 devices without active sync, you need to:

Step 1. Manually copy the following files down to the device

Clientshutdown.exe
ConmanClient2.exe
TcpConnectionA.dll
eDbgTL.dll
CMaccept.exe

From the desktop folder:
 <VS Install Dir>\compactframeworksdk\connectionmanager\target\wce400\<CPU Type>, to \windows on device

If you can't find EdbgTL.dll look here: <VS Install Dir>\smartdevices\debugger\target\wce400\<CPU Type>, to \windows on device

Step 2. Manually launch the conmanclient2.exe

On the device side, open the command prompt and run
Conmanclient2.exe (in case this doesn't work, try opening like this: /transport:tcpconnectiona.dll /property:port=5655 /id:Con )

Step 3. Set the correct IP address

On the desktop side, open VS 2005
Tools -> options -> device tools -> devices
Choose Windows CE 5.0 device, click on “properties”.
On the “Windows CE 5.0 device properties” dialog, click on “configure”.
On the “Configure TCP/IP” Transport dialog, choose “use specific IP address” and type in the IP address of your windows CE 5.0 device.
Click OK.

Step 4. Enable the connection (You can skip this step if the security is already disabled on the CE device by setting "HLKM\System\CoreConOverrideSecurity = 1". But disabling security may expose your device to malicious attack)

Run cMaccept.exe

Connect to the device within 3 minutes after you run cMaccept.exe. (The 3 minutes window is for the first connection. As long as you establish the first connection within 3 minutes, the following deployment/debugging sessions using the same VS instance is not limited by this 3 minutes window)

You need to perform Step 4 again when you try to connect from another instance of VS.

Now debugger is ready to go, and you should be able to deploy and debug program(s) running on Windows CE 5.0 device now.

This solution is stable, I'll never turn back to active sync, it gave me to much problems!

Source: http://blogs.msdn.com/vsdteam/archive/2005/01/05/347332.aspx and http://blogs.msdn.com/vsdteam/archive/2005/04/28/413304.aspx