• .net compact framework2.0 Wince智能设备开发项目经验分享 .net拖空间之进阶篇


        虽然是小项目,但也是麻雀虽小五脏俱全。第一次做嵌入式方面的实战开发,通过此项目的实战,个人积累了少许经验;现分享总结出来。但是希望各位看官不要喷太厉害;什么拖控件没技术含量、.net compact framework2.0不过是.net的精简版很好驾驭、嵌入式开发也不过如此等如此这般的评价本人不接受。请勿鄙视老鸟的智慧,由于项目也实际只有匆匆几天的开发时间,可能程序还存在重构、调优的空间;接受合理的建议。生产环境使用的设备是MC1000(摩托罗拉的扫描枪),内存只有30M,程序存储和程序的运行时占用的内存加起来才这30M,无奈EVC不会,.net compact framework2.0加上MC1000 的运行时、sqlce3.5运行时等就7M多了,程序倒很小,这样一来程序可用的内存只有15M左右;这设备显示屏还是黑白的。总之,我严重不同意这是没技术含量的开发,你得处处小心;一会跑得好好的程序在调试状态就死在设备上了,没有任何提示和异常触发;一会报资源没有释放...

          由于真机屏幕是240×240的很小,要截图还超不方便,还要去仓库MM那里借;总之很麻烦。所以上的图是使用仿真机,但是仿真机不支持MC1000的SDK,一些界面就截不了图了。设备最牛X和本程序最核心的需求是扫描条码,也就是说这里总结的不是业务啊需求什么的。就以下方面做些总结吧:

    1.用户界面和程序架构

    关于我做的这些界面不全部是拖控件实现的,使用了很多自定义控件,比如条码一般都比较长,显示的条码每4个字符分隔一下。窗体界面Enter键代替Tab键等。设备的Tab键很不好用需要和另一个键组合用,如果每个控件都去写KeyDown事件和Click事件是要累死人的,界面也没有统一的风格。我确实把WinForm的一些经验照搬过来了。

    • 自定义控件的使用

    1)显示条码的控件,继承自TextBox。   

    using System;
    
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Drawing;
    using System.Data;
    using System.Text;
    using System.Windows.Forms;
    
    namespace Common
    {
        /// <summary>
        /// 分割显示条码数据,条码一般都较长;不分割查看很费劲
        /// </summary>
        public class BarCodeTextCtrl : TextBox
        {
            /// <summary>
            /// 剔除分割符的原始条码文本
            /// </summary>
            public string BarCodeText
            {
                get
                {
                    return Text.Replace("_", "");
                }
            }
    
            public BarCodeTextCtrl()
            {
                Font = new Font("Tahoma", 10F, FontStyle.Bold);
                ForeColor = Color.Black;
            }
    
            /// <summary>
            /// 响应输入后刷新显示
            /// </summary>
            /// <param name="e"></param>
            protected override void OnTextChanged(EventArgs e)
            {
                //不包含"_"和长度大于4,此条件符合扫码到的条码数据
                if ((Text.Length > 4) && (Text.IndexOf("_") == -1))
                {
                    string result = string.Empty;
                    var tmpTxt = this.Text;
                    int cnt = Convert.ToInt32(Math.Ceiling((double)tmpTxt.Length / 4));
                    for (int i = 0; i < cnt; i++)
                    {
                        string fourChar = string.Empty;
                        if ((Text.Length - i * 4) >= 4)
                            fourChar = tmpTxt.Substring(i * 4, 4);
                        else fourChar = tmpTxt.Substring(i * 4, Text.Length - i * 4);
                        result += fourChar + "_";
                    }
                    Text = result.TrimEnd("_".ToCharArray());
                    SelectionStart = result.Length - 1;
                }
                //手工输入
                if ((Text.Length > 4) && (Text.IndexOf("_") != -1))
                {
                    string result = string.Empty;
                    var tmpTxt = BarCodeText;
                    int cnt = Convert.ToInt32(Math.Ceiling((double)tmpTxt.Length / 4));
                    for (int i = 0; i < cnt; i++)
                    {
                        string fourChar = string.Empty;
                        if ((BarCodeText.Length - i * 4) >= 4)
                            fourChar = tmpTxt.Substring(i * 4, 4);
                        else fourChar = tmpTxt.Substring(i * 4, BarCodeText.Length - i * 4);
                        result += fourChar + "_";
                    }
                    Text = result.TrimEnd("_".ToCharArray());
                    SelectionStart = result.Length - 1;
                }
            }   
        }
    }

    2)主界面的类菜单项的按钮,继承自Button。

    using System;
    
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Drawing;
    using System.Data;
    using System.Text;
    using System.Windows.Forms;
    
    namespace Common
    {
        /// <summary>
        /// 菜单按钮,自定义控件
        /// </summary>
        public class MenuButton : Button
        {
            const string activeFlag = "";
            public MenuButton()
            {
                BackColor = Color.White;
                ForeColor = Color.Black;
                Height = 28;
                Font = new Font("微软雅黑",14F,FontStyle.Bold);
                sf = new StringFormat();
                sf.Alignment = StringAlignment.Center;
                sf.LineAlignment = StringAlignment.Center;
            }
    
            protected bool itemActiveFlag = false;
            /// <summary>
            /// 是否处于被选中状态
            /// </summary>
            public bool ItemActiveFlag
            {
                get { return itemActiveFlag; }
                set 
                {                 
                    itemActiveFlag = value;
                    //LED灯闪一下,体验下控制硬件的快感,~_~
                    //DeviceMc1000Api.NotifyLED_CYCLE(300,200,1);
                    if (value)
                        Text = activeFlag + Text;
                    else Text = Text.Replace(activeFlag, ""); 
                }
            }
    
            private StringFormat sf;
    
            protected override void OnGotFocus(EventArgs e)
            {
                BackColor = SystemColors.Highlight;
                ForeColor = Color.White;
                ItemActiveFlag = true;         
            }
    
            protected override void OnLostFocus(EventArgs e)
            {
                BackColor = Color.White;
                ForeColor = Color.Black;
                ItemActiveFlag = false;               
            }
        }
    }
    • 基类窗体的使用   
    View Code
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    using Common;
    
    namespace PovosScanApp
    {
        /// <summary>
        /// 项目基类窗体
        /// </summary>
        public class DeviceBaseForm : Form
        {
            private bool isScaleDown = true;
            /// <summary>
            /// 是否屏幕自适应
            /// </summary>
            public bool IsScaleDown
            {
                get { return isScaleDown; }
                set { isScaleDown = value; }
            }
          
            private bool enterToTab = true;
            /// <summary>
            /// 是否回车变Tab
            /// </summary>
            public bool EnterToTab
            {
                get { return enterToTab; }
                set { enterToTab = value; }
            }
    
    
            public DeviceBaseForm()
            {
                InitializeComponent();                      
            }
    
            /// <summary>
            /// 屏幕自适应
            /// </summary>
            /// <param name="frm"></param>
            private void ScaleDown()
            {
                int scrWidth = System.Windows.Forms.Screen.PrimaryScreen.WorkingArea.Width;
                int scrHeight = System.Windows.Forms.Screen.PrimaryScreen.WorkingArea.Height;
                if (scrWidth < this.Width)
                    foreach (System.Windows.Forms.Control cntrl in this.Controls)
                    {
                        cntrl.Width = ((cntrl.Width) * (scrWidth)) / (this.Width);
                        cntrl.Left = ((cntrl.Left) * (scrWidth)) / (this.Width);
                    }
                if (scrHeight < this.Height)
                    foreach (System.Windows.Forms.Control cntrl in this.Controls)
                    {
                        cntrl.Height = ((cntrl.Height) * (scrHeight)) / (this.Height);
                        cntrl.Top = ((cntrl.Top) * (scrHeight)) / (this.Height);
                    }
            }      
    
            private void InitializeComponent()
            {
                this.SuspendLayout();
                // 
                // DeviceBaseForm
                // 
                this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Inherit;
                this.ClientSize = new System.Drawing.Size(240, 240);
                this.KeyPreview = true;
                this.Font = new Font("Tahoma", 11F, FontStyle.Bold);
                this.ForeColor = Color.White;
                this.BackColor = Color.Black;
                this.Name = "DeviceBaseForm";
                this.ResumeLayout(false);
    
            }
    
            /// <summary>
            /// 递归控件 外接事件Enter变Tab
            /// </summary>
            /// <param name="ctrl"></param>
            void SetControlEnterToTab(Control ctrl)
            {
                foreach (Control subControl in ctrl.Controls)
                {
                    if (subControl is TextBox || subControl is DateTimePicker || subControl is ComboBox || subControl is NumericUpDown)
                    {
                        subControl.KeyDown += new KeyEventHandler(ctrl_KeyDown);
                    }
                    if (ctrl.Controls.Count > 0)
                        SetControlEnterToTab(subControl);
                }
            }
    
            void ctrl_KeyDown(object sender, KeyEventArgs e)
            {
                if (e.KeyValue == 13)
                {
                    var ctrl = sender as Control;
                    if (!string.IsNullOrEmpty(ctrl.Text) && ctrl is TextBox)
                    {
                        var tbx = ctrl as TextBox;
                        tbx.SelectAll();
                    }
                    WinceApi.keybd_event(9, 0, 0, 0);
                } 
            }
    
            protected override void OnKeyDown(KeyEventArgs e)
            {
                base.OnKeyDown(e);
                if (e.KeyValue == 27)
                    this.Close();
            }
    
            protected override void OnLoad(EventArgs e)
            {
                if (IsScaleDown)
                    ScaleDown();
                if (EnterToTab)
                    SetControlEnterToTab(this);
                this.Width = System.Windows.Forms.Screen.PrimaryScreen.WorkingArea.Width - 2;
                this.Height = System.Windows.Forms.Screen.PrimaryScreen.WorkingArea.Height - 2;
                base.OnLoad(e);
            }
           
        }
    }

    2.编程规范、面向对象经验、设计模式的运用

    1)编码规范

    各位看架构截图和前面的代码,应该明白本人遵循了微软C#主流的开发规范了吧。下面随便抽取一点代码展示,这是WinCe开发常用的,有需要的直接Copy去用。本人没上过大学,不要太嫌我英语差啊。呵呵

    WinCE 通用API
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Runtime.InteropServices;
    
    namespace Common
    {
        /// <summary>
        /// WinCE平台API调用类
        /// </summary>
        public class WinceApi
        {
            /// <summary>
            /// 模拟按下键
            /// </summary>
            /// <param name="bVk">9->tab 13->Enter 27->Esc ...</param>
            /// <param name="bScan"></param>
            /// <param name="dwFlags"></param>
            /// <param name="dwExtraInfo"></param>
            [DllImport("Coredll.dll", EntryPoint = "keybd_event")]
            public static extern void keybd_event(
                byte bVk,
                byte bScan,
                int dwFlags,
                int dwExtraInfo
            );
    
            [DllImport("Coredll.dll")]
            public static extern void GetLocalTime(SystemTime st);
            [DllImport("Coredll.dll")]
            public static extern void SetLocalTime(SystemTime st);
    
            [StructLayout(LayoutKind.Sequential)]
            public class SystemTime
            {
                public ushort wYear;
                public ushort wMonth;
                public ushort wDayOfWeek;
                public ushort wDay;
                public ushort wHour;
                public ushort wMinute;
                public ushort wSecond;
                public ushort wMilliseconds;
            }
    
            public struct MemoryStatus
            {
                //MEMORYSTATUS 结构的大小,在调 GlobalMemoryStatus 函数前用sizeof()函数求得   
                public Int32 dwLength;
                //返回一个介于 0~100 之间的值,用来指示当前系统内存的使用率   
                public Int32 dwMemoryLoad;
                //返回总的物理内存大小,以字节(byte)为单位   
                public Int32 dwTotalPhys;
                //返回可用的物理内存大小,以字节(byte)为单位   
                public Int32 dwAvailPhys;
                //显示可以存在页面文件中的字节数。注意这个数值并不表示在页面文件在磁盘上的真实物理大小   
                public Int32 dwTotalPageFile;
                //返回可用的页面文件大小,以字节(byte)为单位   
                public Int32 dwAvailPageFile;
                //返回调用进程的用户模式部分的全部可用虚拟地址空间,以字节(byte)为单位   
                public Int32 dwTotalVirtual;
                // 返回调用进程的用户模式部分的实际自由可用的虚拟地址空间,以字节(byte)为单位   
                public Int32 dwAvailVirtual;
            }
    
            /// <summary>
            /// 获取 wince 系统内存情况 
            /// </summary>
            /// <param name="msce"></param>
            [DllImport("coredll", EntryPoint = "GlobalMemoryStatus", SetLastError = false)]
            public static extern void GlobalMemoryStatusCE(out MemoryStatus msce);
    
            /// <summary>
            /// 获取存储设备的大小信息
            /// </summary>
            /// <param name="directoryName"></param>
            /// <param name="freeBytesAvailable"></param>
            /// <param name="totalBytes"></param>
            /// <param name="totaFreeBytes"></param>
            /// <returns></returns>
            [DllImport("coredll.dll")]
            private static extern bool GetDiskFreeSpaceEx(string directoryName, ref long freeBytesAvailable, ref long totalBytes, ref long totaFreeBytes);
    
            [DllImport("Coredll.dll")]
            extern static int KernelIoControl(int dwIoControlCode, IntPtr lpInBuf, int nInBufSize, IntPtr
            lpOutBuf, int nOutBufSize, ref int lpBytesReturned);
    
            [DllImport("Coredll.dll")]
            extern static void SetCleanRebootFlag();
    
            /// <summary>
            /// 系统重启(冷启动)
            /// </summary>
            public static void HardReset()
            {
                int IOCTL_HAL_REBOOT = 0x101003C;
                int bytesReturned = 0;
                SetCleanRebootFlag();
                KernelIoControl(IOCTL_HAL_REBOOT, IntPtr.Zero, 0, IntPtr.Zero, 0, ref bytesReturned);
            }
    
            /// <summary>
            /// 程序互斥,只允许运行一次
            /// </summary>
            public class Mutex
            {
                [DllImport("coredll.dll", EntryPoint = "CreateMutex", SetLastError = true)]
                public static extern IntPtr CreateMutex(
                IntPtr lpMutexAttributes,
                bool InitialOwner,
                string MutexName);
    
                [DllImport("coredll.dll", EntryPoint = "ReleaseMutex", SetLastError = true)]
                public static extern bool ReleaseMutex(IntPtr hMutex);
    
                private const int ERROR_ALREADY_EXISTS = 0183;
    
                /// <summary>
                /// 判断程序是否已经运行
                /// </summary>
                /// <returns>
                /// true: 程序已运行,则什么都不做
                /// false: 程序未运行,则启动程序
                /// </returns>
                public static bool IsExist()
                {
                    string strAppName =
                    System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;
                    IntPtr hMutex = CreateMutex(IntPtr.Zero, true, strAppName);
                    if (hMutex == IntPtr.Zero)
                        throw new ApplicationException("Failure creating mutex: "
                        + Marshal.GetLastWin32Error().ToString("X"));
                    if (Marshal.GetLastWin32Error() == ERROR_ALREADY_EXISTS)
                    {
                        ReleaseMutex(hMutex);
                        return true;
                    }
                    return false;
                }
            } 
        }
    }

    2)面向对象编码经验

    窗体也是一个类,代码写得合理、干净整洁;维护起来就是爽。这个我说我面向对象了,大家都笑了吧,我最常用的就是封装、继承。比如窗体间传值,我喜欢封装成属性,然后调用的窗口或者这个属性。总之经常使用索引器这些。感觉自己还是领略了面向对象的精神的,要说做得多好就不一定了。

     /// <summary>
            /// 工位
            /// </summary>
            public int PrdPosition
            {
                get { return rbMidBoxScan.Checked == true ? 1 : 2; }
            }
    
            /// <summary>
            /// 操作员
            /// </summary>
            public string Operator
            {
                get { return txtOper.Text.Trim(); }
            }
    
            /// <summary>
            /// 批号
            /// </summary>
            public string BatchNo
            {
                get { return txtBatchNo.Text.Trim(); }
            }
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Drawing;
    using System.Data;
    using System.Text;
    using System.Windows.Forms;
    using DataAccess;
    
    namespace PovosScanApp
    {
        public class OrderSelectItemCtrl : Common.MenuButton
        {
            /// <summary>
            /// 控件的数据行
            /// </summary>
            public OrderEntity ItemDataRow
            {
                get;
                set;
            }
    
            public OrderSelectItemCtrl()
            {
                //
            }
        }
    }

    3)设计模式的运用 

    项目里只用到了单例(单件)模式。

    Application里的全局对象
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.IO;
    using System.Reflection;
    using DataAccess;
    using Common;
    
    namespace PovosScanApp
    {
        /// <summary>
        /// 全局对象,单例
        /// </summary>
        public sealed class GlobalData
        {
            private static readonly GlobalData instance = new GlobalData();
            public DBComponent LocalDb = null;
          
            private GlobalData()
            {
                string dbFile = "\\Application\\ScanLocalDb.sdf";
                LocalDb = new DBComponent(dbFile);
            }
    
            public static GlobalData GetInstance()
            {
                return instance;
            }
    
            /// <summary>
            /// 应用程序版本号
            /// </summary>
            public static string AppVersion
            {
                get { return "1.0.130115"; }
            }
    
            /// <summary>
            /// 本地数据库版本号
            /// </summary>
            public static string DbVersion
            {
                get { return "1.0.130115"; }
            }
    
            /// <summary>
            /// 设备UUID,唯一序列号
            /// </summary>
            public static string DeviceMac
            {
                get { return DeviceMc1000Api.GetDevid().TrimEnd("0".ToCharArray()); }        
            }
    
            /// <summary>
            /// 应用程序安装目录
            /// </summary>
            public static string AppBasePath
            {
                get { return Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName);}
            }
    
            /// <summary>
            /// 数据库连接字符串
            /// </summary>
            public static string DbConnectionString
            {
                get { return instance.LocalDb.ConnectionStr; }    
            }
        }
    }

    没了。

    下面给众多伸手党兄弟一些福利吧:

    SqlCe 数据库存取类
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Data.SqlServerCe;
    using System.Data;
    using System.IO;
    
    namespace DataAccess
    {
        /// <summary>
        /// SQLCE数据库存取类
        /// </summary>
        public class DBComponent 
        {        
            public DBComponent(string dbFileName)
            {
                myDBName = dbFileName;
                myDBEncrypt = false;          
            }
    
            /// <summary>
            /// 获取数据库连接字符串
            /// </summary>
            public string ConnectionStr
            {
                get
                {
                    string connStr = "Data Source='" + myDBName + "'; LCID=1033; Password=\"" + myDBPassword + "\"; Encrypt = ";
                    if (myDBEncrypt == true)
                        connStr += "TRUE;";
                    else connStr += "FALSE;";
                    return connStr;
                }
            }
    
            private string myDBName;
            // Gets or sets the database name
            public string DBName
            {
                get
                {
                    return myDBName;
                }
                set
                {
                    myDBName = value;
                }
            }
    
            private string myDBPassword;
            // Gets or sets the password for database
            public string DBPassword
            {
                get
                {
                    return myDBPassword;
                }
                set
                {
                    myDBPassword = value;
                }
            }
    
            private bool myDBEncrypt = true;
            // Enable or disable database encryption
            public bool DBEncrypt
            {
                get
                {
                    return myDBEncrypt;
                }
                set
                {
                    myDBEncrypt = value;
                }
            }
    
            // If set to true, delete the the database in DBCreate() if already exists and then create a new one. 
            // If set to false, use the existing one.  
            private bool myDBDelete = false;
            public bool DBDelete
            {
                get
                {
                    return myDBDelete;
                }
                set
                {
                    myDBDelete = value;
                }
            }
    
            //Create a databse using the name provided in DBName
            public void DBCreate()
            {
                if (myDBEncrypt == false && File.Exists(myDBName)) return;
                else File.Delete(myDBName);
                SqlCeEngine mySqlEngine = null;
                try
                {                 
                    mySqlEngine = new SqlCeEngine(ConnectionStr);
                    mySqlEngine.CreateDatabase();
                }
                catch (Exception ex)
                {
                    throw new Exception(ex.Message);
                }
                finally
                {
                    mySqlEngine.Dispose();
                }
            }
    
            // Query the database and return the resultset
            public DataSet DBQuery(string queryStr)
            {
                using (var mySqlConnection = new SqlCeConnection(ConnectionStr))
                {
                    try
                    {
                        mySqlConnection.Open();
                        using (var cmd = mySqlConnection.CreateCommand())
                        {
                            cmd.CommandText = queryStr;
                            var myDataSet = new DataSet();
                            var mySqlDataAdapter = new SqlCeDataAdapter(cmd);
                            mySqlDataAdapter.Fill(myDataSet);
                            return myDataSet;
                        }
                    }
                    catch (Exception ex)
                    {
                        return null;
                        throw new Exception(ex.Message);
                    }
                    finally
                    {
                        mySqlConnection.Close();
                    }
                }            
            }
    
            // Execute Delete, Insert and Update commands
            public int DBExecute(string executeStr)
            {
                using (var mySqlConnection = new SqlCeConnection(ConnectionStr))
                {
                    int rowsAffected = -1;
                    try
                    {
                        mySqlConnection.Open();
                        using (var cmd = mySqlConnection.CreateCommand())
                        {
                            cmd.CommandText = executeStr;
                            rowsAffected = cmd.ExecuteNonQuery();
                            return rowsAffected;
                        }
                    }
                    catch (Exception ex)
                    {
                        return rowsAffected;
                        throw new Exception(ex.Message);
                    }
                    finally
                    {
                        mySqlConnection.Close();
                    }
                }            
            }
    
            // ExecuteScalar
            public object DBExecuteScalar(string executeStr)
            {
                using (var mySqlConnection = new SqlCeConnection(ConnectionStr))
                {
                    try
                    {
                        mySqlConnection.Open();
                        using (var cmd = mySqlConnection.CreateCommand())
                        {
                            cmd.CommandText = executeStr;
                            object rowsObj = cmd.ExecuteScalar();
                            return rowsObj;
                        }
                    }
                    catch (Exception ex)
                    {
                        return null;
                        throw new Exception(ex.Message);
                    }
                    finally
                    {
                        mySqlConnection.Close();
                    }
                }            
            }
        }
    }
    WinCe电源管理通用类来自设备的SDK里的例子
    using System;
    
    using System.Collections.Generic;
    using System.Text;
    using System.Threading;
    using System.Runtime.InteropServices;
    using System.Collections;
    
    namespace Common
    {
        /// <summary>
        /// 设备电源管理类
        /// </summary>
        public class PowerManagement : IDisposable
        {
            #region ------------------Enumerations--------------------
    
            /// <summary>
            /// Defines the System power states
            /// </summary>
            public enum SystemPowerStates : uint
            {
                /// <summary>
                /// On state.
                /// </summary>
                On = 0x00010000,
    
                /// <summary>
                /// No power, full off.
                /// </summary>
                Off = 0x00020000,
    
                /// <summary>
                /// Critical off.
                /// </summary>
                Critical = 0x00040000,
    
                /// <summary>
                /// Boot state.
                /// </summary>
                Boot = 0x00080000,
    
                /// <summary>
                /// Idle state.
                /// </summary>
                Idle = 0x00100000,
    
                /// <summary>
                /// Suspend state.
                /// </summary>
                Suspend = 0x00200000,
    
                /// <summary>
                /// Reset state.
                /// </summary>
                Reset = 0x00800000
            }
    
            /// <summary>
            /// Defines the System power requirement flags
            /// </summary>
            public enum PowerReqFlags : uint
            {
                POWER_NAME = 0x00000001,
                POWER_FORCE = 0x00001000,
            }
    
            /// <summary>
            /// Defines the Device power states
            /// </summary>
            public enum DevicePowerStates
            {
                PwrDeviceUnspecified = -1,
                FullOn = 0,        // Full On: full power,  full functionality
                D0 = FullOn,
                LowOn,            // Low Power On: fully functional at low power/performance
                D1 = LowOn,
                StandBy,        // Standby: partially powered with automatic wake
                D2 = StandBy,
                Sleep,            // Sleep: partially powered with device initiated wake
                D3 = Sleep,
                Off,            // Off: unpowered
                D4 = Off,
                PwrDeviceMaximum
            }
    
            /// <summary>
            /// Defines the Power Status message type.
            /// </summary>
            [FlagsAttribute()]
            public enum MessageTypes : uint
            {
                /// <summary>
                /// System power state transition.
                /// </summary>
                Transition = 0x00000001,
    
                /// <summary>
                /// Resume from previous state.
                /// </summary>
                Resume = 0x00000002,
    
                /// <summary>
                /// Power supply switched to/from AC/DC.
                /// </summary>
                Change = 0x00000004,
    
                /// <summary>
                /// A member of the POWER_BROADCAST_POWER_INFO structure has changed.
                /// </summary>
                Status = 0x00000008
            }
    
            /// <summary>
            /// Defines the AC power status flags.
            /// </summary>
            public enum ACLineStatus : byte
            {
                /// <summary>
                /// AC power is offline.
                /// </summary>
                Offline = 0x00,
    
                /// <summary>
                /// AC power is online. 
                /// </summary>
                OnLine = 0x01,
    
                /// <summary>
                /// AC line status is unknown.
                /// </summary>
                Unknown = 0xff
            }
    
            /// <summary>
            /// Defines the Battery charge status flags.
            /// </summary>
            [FlagsAttribute()]
            public enum BatteryFlags : byte
            {
                /// <summary>
                /// High
                /// </summary>
                High = 0x01,
    
                /// <summary>
                /// Low
                /// </summary>
                Low = 0x02,
    
                /// <summary>
                /// Critical
                /// </summary>
                Critical = 0x04,
    
                /// <summary>
                /// Charging
                /// </summary>
                Charging = 0x08,
    
                /// <summary>
                /// Reserved1
                /// </summary>
                Reserved1 = 0x10,
    
                /// <summary>
                /// Reserved2
                /// </summary>
                Reserved2 = 0x20,
    
                /// <summary>
                /// Reserved3
                /// </summary>
                Reserved3 = 0x40,
    
                /// <summary>
                /// No system battery
                /// </summary>
                NoBattery = 0x80,
    
                /// <summary>
                /// Unknown status
                /// </summary>
                Unknown = High | Low | Critical | Charging | Reserved1 | Reserved2 | Reserved3 | NoBattery
            }
    
            /// <summary>
            /// Responses from <see cref="WaitForMultipleObjects"/> function.
            /// </summary>
            private enum Wait : uint
            {
                /// <summary>
                /// The state of the specified object is signaled.
                /// </summary>
                Object = 0x00000000,
                /// <summary>
                /// Wait abandoned.
                /// </summary>
                Abandoned = 0x00000080,
                /// <summary>
                /// Wait failed.
                /// </summary>
                Failed = 0xffffffff,
            }
    
    
            #endregion -----------------Enumerations-------------------
    
            #region --------------------Members-----------------------
    
            /// <summary>
            /// Indicates that an application would like to receive all types of 
            /// power notifications.
            /// </summary>
            private const uint POWER_NOTIFY_ALL = 0xFFFFFFFF;
    
            /// <summary>
            /// Indicates an infinite wait period
            /// </summary>
            private const int INFINITE = -1;
    
            /// <summary>
            /// Allocate message buffers on demand and free the message buffers after they are read.
            /// </summary>
            private const int MSGQUEUE_NOPRECOMMIT = 1;
    
            /// <summary>
            /// Event to wake up the worker thread so that it can close
            /// </summary>
            private AutoResetEvent powerThreadAbort;
    
            /// <summary>
            /// Flag requesting worker thread closure
            /// </summary>
            private bool abortPowerThread = false;
    
            /// <summary>
            /// Flag to indicate that the worker thread is running
            /// </summary>
            private bool powerThreadRunning = false;
    
            /// <summary>
            /// Thread interface queue
            /// </summary>
            private Queue powerQueue;
    
            /// <summary>
            /// Handle to the message queue
            /// </summary>
            private IntPtr hMsgQ = IntPtr.Zero;
    
            /// <summary>
            /// Handle returned from RequestPowerNotifications
            /// </summary>
            private IntPtr hReq = IntPtr.Zero;
    
            /// <summary>
            /// Boolean used to indicate if the object has been disposed
            /// </summary>
            private bool bDisposed = false;
    
            /// <summary>
            /// Occurs when there is some PowerNotify information available.
            /// </summary>
            public event EventHandler PowerNotify;
    
            #endregion --------------------Members--------------------
    
            #region -------------------Structures---------------------
    
            /// <summary>
            /// Contains information about a message queue.
            /// </summary>
            [StructLayout(LayoutKind.Sequential)]
            private struct MessageQueueOptions
            {
                /// <summary>
                /// Size of the structure in bytes.
                /// </summary>
                public uint Size;
    
                /// <summary>
                /// Describes the behavior of the message queue. Set to MSGQUEUE_NOPRECOMMIT to 
                /// allocate message buffers on demand and to free the message buffers after 
                /// they are read, or set to MSGQUEUE_ALLOW_BROKEN to enable a read or write 
                /// operation to complete even if there is no corresponding writer or reader present.
                /// </summary>
                public uint Flags;
    
                /// <summary>
                /// Number of messages in the queue.
                /// </summary>
                public uint MaxMessages;
    
                /// <summary>
                /// Number of bytes for each message, do not set to zero.
                /// </summary>
                public uint MaxMessage;
    
                /// <summary>
                /// Set to TRUE to request read access to the queue. Set to FALSE to request write 
                /// access to the queue.
                /// </summary>
                public uint ReadAccess;
            };
    
            /// <summary>
            /// Contains information about the power status of the system  
            /// as received from the Power Status message queue.
            /// </summary>
            [StructLayout(LayoutKind.Sequential)]
            public struct PowerInfo
            {
                /// <summary>
                /// Defines the event type.
                /// </summary>
                /// <see cref="MessageTypes"/>
                public MessageTypes Message;
    
                /// <summary>
                /// One of the system power flags.
                /// </summary>
                /// <see cref="SystemPowerStates"/>
                public SystemPowerStates Flags;
    
                /// <summary>
                /// The byte count of SystemPowerState that follows. 
                /// </summary>
                public uint Length;
    
                /// <summary>
                /// Levels available in battery flag fields
                /// </summary>
                public uint NumLevels;
    
                /// <summary>
                /// Number of seconds of battery life remaining, 
                /// or 0xFFFFFFFF if remaining seconds are unknown.
                /// </summary>
                public uint BatteryLifeTime;
    
                /// <summary>
                /// Number of seconds of battery life when at full charge, 
                /// or 0xFFFFFFFF if full battery lifetime is unknown.
                /// </summary>
                public uint BatteryFullLifeTime;
    
                /// <summary>
                /// Number of seconds of backup battery life remaining, 
                /// or BATTERY_LIFE_UNKNOWN if remaining seconds are unknown.
                /// </summary>
                public uint BackupBatteryLifeTime;
    
                /// <summary>
                /// Number of seconds of backup battery life when at full charge, 
                /// or BATTERY_LIFE_UNKNOWN if full battery lifetime is unknown.
                /// </summary>
                public uint BackupBatteryFullLifeTime;
    
                /// <summary>
                /// AC power status. 
                /// </summary>
                /// <see cref="ACLineStatus"/>
                public ACLineStatus ACLineStatus;
    
                /// <summary>
                /// Battery charge status. 
                /// </summary>
                /// <see cref="BatteryFlags"/>
                public BatteryFlags BatteryFlag;
    
                /// <summary>
                /// Percentage of full battery charge remaining. 
                /// This member can be a value in the range 0 (zero) to 100, or 255 
                /// if the status is unknown. All other values are reserved.
                /// </summary>
                public byte BatteryLifePercent;
    
                /// <summary>
                /// Backup battery charge status. 
                /// </summary>
                public byte BackupBatteryFlag;
    
                /// <summary>
                /// Percentage of full backup battery charge remaining. 
                /// This value must be in the range of 0 to 100, or BATTERY_PERCENTAGE_UNKNOWN.
                /// </summary>
                public byte BackupBatteryLifePercent;
            };
    
            #endregion -------------------Structures------------------
    
            #region ---------------------Methods----------------------
    
            /// <summary>
            /// Ensures that resources are freed when the garbage collector reclaims the object.
            /// </summary>
            ~PowerManagement()
            {
                Dispose();
            }
    
            /// <summary>
            /// Releases the resources used by the object.
            /// </summary>
            public void Dispose()
            {
                if (!bDisposed)
                {
                    // Try disabling notifications and ending the thread
                    DisableNotifications();
                    bDisposed = true;
    
                    // SupressFinalize to take this object off the finalization queue 
                    // and prevent finalization code for this object from executing a second time.
                    GC.SuppressFinalize(this);
                }
            }
    
            /// <summary>
            /// Sets the system power state to the requested value.
            /// </summary>
            /// <param name="systemState">The system power state to set the device to.</param>
            /// <returns>Win32 error code</returns>
            /// <remarks>Should be used with extreme care since it may result in an unexpected 
            /// application or system behavior.</remarks>
            public int SetSystemPowerState(SystemPowerStates systemState)
            {
                uint nError = 0;
    
                nError = CESetSystemPowerState(
                    IntPtr.Zero,
                    (uint)systemState,
                    0);
    
                return (int)nError;
            }
    
            /// <summary>
            /// Returns the current system power state currently in effect.
            /// </summary>
            /// <param name="systemStateName">Receives the system power state name</param>
            /// <param name="systemState">Receives the system power state</param>
            /// <returns>Win32 error code</returns>
            public int GetSystemPowerState(StringBuilder systemStateName, out SystemPowerStates systemState)
            {
                uint nError = 0;
    
                nError = CEGetSystemPowerState(systemStateName, (uint)systemStateName.Capacity, out systemState);
    
                return (int)nError;
            }
    
            /// <summary>
            /// Requests that the Power Manager change the power state of a device.
            /// </summary>
            /// <param name="deviceName">Specifies the device name, for example, COM1:.</param>
            /// <param name="deviceState">Indicates the device power state</param>
            /// <returns>Win32 error code</returns>
            /// <remarks>Should be used with extreme care since it may result in an unexpected 
            /// application or system behavior.</remarks>
            public int DevicePowerNotify(string deviceName, DevicePowerStates deviceState)
            {
                uint nError = 0;
    
                nError = CEDevicePowerNotify(deviceName, (uint)deviceState, (uint)PowerReqFlags.POWER_NAME);
    
                return (int)nError;
            }
    
            /// <summary>
            /// Activates notification events. An application can now register to PowerNotify and be 
            /// notified when a power notification is received.
            /// </summary>
            public void EnableNotifications()
            {
                // Set the message queue options
                MessageQueueOptions Options = new MessageQueueOptions();
    
                // Size in bytes ( 5 * 4)
                Options.Size = (uint)Marshal.SizeOf(Options);
                // Allocate message buffers on demand and to free the message buffers after they are read
                Options.Flags = MSGQUEUE_NOPRECOMMIT;
                // Number of messages in the queue.
                Options.MaxMessages = 32;
                // Number of bytes for each message, do not set to zero.
                Options.MaxMessage = 512;
                // Set to true to request read access to the queue.
                Options.ReadAccess = 1;    // True
    
                // Create the queue and request power notifications on it
                hMsgQ = CECreateMsgQueue("PowerNotifications", ref Options);
    
                hReq = CERequestPowerNotifications(hMsgQ, POWER_NOTIFY_ALL);
    
                // If the above succeed
                if (hMsgQ != IntPtr.Zero && hReq != IntPtr.Zero)
                {
                    powerQueue = new Queue();
    
                    // Create an event so that we can kill the thread when we want
                    powerThreadAbort = new AutoResetEvent(false);
    
                    // Create the power watcher thread
                    new Thread(new ThreadStart(PowerNotifyThread)).Start();
                }
            }
    
            /// <summary>
            /// Disables power notification events.
            /// </summary>
            public void DisableNotifications()
            {
                // If we are already closed just exit
                if (!powerThreadRunning)
                    return;
    
                // Stop receiving power notifications
                if (hReq != IntPtr.Zero)
                    CEStopPowerNotifications(hReq);
    
                // Attempt to end the PowerNotifyThread
                abortPowerThread = true;
                powerThreadAbort.Set();
    
                // Wait for the thread to stop
                int count = 0;
                while (powerThreadRunning)
                {
                    Thread.Sleep(100);
    
                    // If it did not stop it time record this and give up
                    if (count++ > 50)
                        break;
                }
            }
    
            /// <summary>
            /// Obtain the next PowerInfo structure
            /// </summary>
            public PowerInfo GetNextPowerInfo()
            {
                // Get the next item from the queue in a thread safe manner
                lock (powerQueue.SyncRoot)
                    return (PowerInfo)powerQueue.Dequeue();
            }
    
            /// <summary>
            /// Worker thread that creates and reads a message queue for power notifications
            /// </summary>
            private void PowerNotifyThread()
            {
                powerThreadRunning = true;
    
                // Keep going util we are asked to quit
                while (!abortPowerThread)
                {
                    IntPtr[] Handles = new IntPtr[2];
    
                    Handles[0] = hMsgQ;
                    Handles[1] = powerThreadAbort.Handle;
    
                    // Wait on two handles because the message queue will never
                    // return from a read unless messages are posted.
                    Wait res = (Wait)CEWaitForMultipleObjects(
                                                            (uint)Handles.Length,
                                                            Handles,
                                                            false,
                                                            INFINITE);
    
                    // Exit the loop if an abort was requested
                    if (abortPowerThread)
                        break;
    
                    // Else
                    switch (res)
                    {
                        // This must be an error - Exit loop and thread
                        case Wait.Abandoned:
                            abortPowerThread = true;
                            break;
    
                        // Timeout - Continue after a brief sleep
                        case Wait.Failed:
                            Thread.Sleep(500);
                            break;
    
                        // Read the message from the queue
                        case Wait.Object:
                            {
                                // Create a new structure to read into
                                PowerInfo Power = new PowerInfo();
    
                                uint PowerSize = (uint)Marshal.SizeOf(Power);
                                uint BytesRead = 0;
                                uint Flags = 0;
    
                                // Read the message
                                if (CEReadMsgQueue(hMsgQ, ref Power, PowerSize,
                                                    ref BytesRead, 0, ref Flags))
                                {
                                    // Set value to zero if percentage is not known
                                    if ((Power.BatteryLifePercent < 0) || (Power.BatteryLifePercent > 100))
                                        Power.BatteryLifePercent = 0;
    
                                    if ((Power.BackupBatteryLifePercent < 0) || (Power.BackupBatteryLifePercent > 100))
                                        Power.BackupBatteryLifePercent = 0;
    
                                    // Add the power structure to the queue so that the 
                                    // UI thread can get it
                                    lock (powerQueue.SyncRoot)
                                        powerQueue.Enqueue(Power);
    
                                    // Fire an event to notify the UI
                                    if (PowerNotify != null)
                                        PowerNotify(this, null);
                                }
    
                                break;
                            }
                    }
                }
    
                // Close the message queue
                if (hMsgQ != IntPtr.Zero)
                    CECloseMsgQueue(hMsgQ);
    
                powerThreadRunning = false;
            }
    
    
            #endregion -----------------Methods---------------------
    
            #region ---------Native Power Management Imports----------
    
            [DllImport("coredll.dll", EntryPoint = "RequestPowerNotifications")]
            private static extern IntPtr CERequestPowerNotifications(IntPtr hMsgQ, uint Flags);
    
            [DllImport("coredll.dll", EntryPoint = "StopPowerNotifications")]
            private static extern bool CEStopPowerNotifications(IntPtr hReq);
    
            [DllImport("coredll.dll", EntryPoint = "SetDevicePower")]
            private static extern uint CESetDevicePower(string Device, uint dwDeviceFlags, uint DeviceState);
    
            [DllImport("coredll.dll", EntryPoint = "GetDevicePower")]
            private static extern uint CEGetDevicePower(string Device, uint dwDeviceFlags, uint DeviceState);
    
            [DllImport("coredll.dll", EntryPoint = "DevicePowerNotify")]
            private static extern uint CEDevicePowerNotify(string Device, uint DeviceState, uint Flags);
    
            [DllImport("coredll.dll", EntryPoint = "SetSystemPowerState")]
            private static extern uint CESetSystemPowerState(IntPtr sState, uint StateFlags, uint Options);
    
            [DllImport("coredll.dll", EntryPoint = "GetSystemPowerState")]
            private static extern uint CEGetSystemPowerState(StringBuilder Buffer, uint Length, out SystemPowerStates Flags);
    
            [DllImport("coredll.dll", EntryPoint = "CreateMsgQueue")]
            private static extern IntPtr CECreateMsgQueue(string Name, ref MessageQueueOptions Options);
    
            [DllImport("coredll.dll", EntryPoint = "CloseMsgQueue")]
            private static extern bool CECloseMsgQueue(IntPtr hMsgQ);
    
            [DllImport("coredll.dll", EntryPoint = "ReadMsgQueue")]
            private static extern bool CEReadMsgQueue(IntPtr hMsgQ, ref PowerInfo Power, uint BuffSize, ref uint BytesRead, uint Timeout, ref uint Flags);
    
            [DllImport("coredll.dll", EntryPoint = "WaitForMultipleObjects", SetLastError = true)]
            private static extern int CEWaitForMultipleObjects(uint nCount, IntPtr[] lpHandles, bool fWaitAll, int dwMilliseconds);
    
            #endregion ---------Native Power Management Imports----------
        }
    }
    WinCE Socket
    using System;
    using System.Collections.Generic;
    using System.Text;
    
    using System.Net;
    using System.Net.Sockets;
    using System.Threading;
    
    
    namespace Common
    {
        /// <summary>
        /// Socket notification events.
        /// </summary>
        public enum NotifyEvents
        {
            Connected,
            DataSent,
            DataReceived,
            Disconnected,
            ConnectError,
            SendError,
            ReceiveError,
            DisconnectError,
            OtherError
        }
    
        // Socket communications will be done asynchronously
        public class DeviceSocket
        {
            // The Socket class provides a rich set of methods and properties for network communications via sockets. 
            // Although Socket class supports both synchronous and asynchronous data transfers, we choose asynchronous 
            // method here
            public Socket mySocket = null;
    
            // Used to synchronize the shutdown process, terminate
            // any pending async calls before Disconnect returns
            ManualResetEvent asyncEvent = new ManualResetEvent(true);
    
            // private string terminator = "<END_OF_RECORD>";
    
            public string receiveBuf = null;
            private const int BUFFER_SIZE = 1024;
    
            // notification event
            public delegate void NotifyEventHandler(NotifyEvents nEvent, object data);
            public event NotifyEventHandler Notify;
    
            // Closing flag
            private bool closing = false;
            byte[] bRcvd = new byte[BUFFER_SIZE];
    
            /// <summary>
            /// Connect to the specified address and port number.
            /// </summary>
            public void Connect(String ip, int port)
            {
                // Create a socket object
                // The addressFamily parameter (Address for IP version 4) specifies the addressing scheme the Socket class 
                // The socketType parameter specifies the type of the Socket class
                // The protocolType parameter (tcp - Transmission Control Protocol) specifies the protocol used by Socket
                mySocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    
                // Sets the state of the event to nonsignaled, causing threads to block
                asyncEvent.Reset();
    
                // Prepare for async connection
                IPAddress ipAddress = IPAddress.Parse(ip);
                IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, port);
    
                // Begins an asynchronous request for a remote host connection
                mySocket.BeginConnect(ipEndPoint, new AsyncCallback(OnConnect), null);
    
                // wait for any async operations to complete
                asyncEvent.WaitOne();
            }
    
            /// <summary>
            /// The property Socket.Connected does not always indicate if the socket is currently 
            /// connected, this polls the socket to determine the latest connection state.
            /// 
            /// Returns True or False
            /// </summary>
            public bool IsConnected
            {
                get
                {
                    if (mySocket == null)   return false;
    
                    // the socket is not connected if the Connected property is false
                    if (!mySocket.Connected)    return false;
    
                    // there is no guarantee that the socket is connected even if the
                    // Connected property is true
                    try
                    {
                        // poll for error to see if socket is connected
                        return !mySocket.Poll(1, SelectMode.SelectError);
                    }
                    catch
                    {
                        return false;
                    }
                }
            }
    
            /// <summary>
            /// Async connect callback
            /// </summary>
            private void OnConnect(IAsyncResult ar)
            {
                try
                {
                    // Ends a pending asynchronous connection request
                    mySocket.EndConnect(ar);
    
                    // Assert: Connected. Notify the app.
                    NotifyCaller(NotifyEvents.Connected, null);
                }
                catch (Exception ex)
                {
                    NotifyCaller(NotifyEvents.ConnectError, ex.Message);
                }
            }
    
            /// <summary>
            /// Disconnect a connected socket
            /// </summary>
            public void Disconnect()
            {
                // if the socket is not created
                if (mySocket == null) return;
    
                closing = true;
    
                try
                {
                    // first, shutdown the socket
                    mySocket.Shutdown(SocketShutdown.Both);
                }
                catch { }
    
                try
                {
                    // next, close the socket which terminates any pending
                    // async operations
                    mySocket.Close();
    
                    // wait for any async operations to complete
                    asyncEvent.WaitOne();
                }
                catch { }
    
                closing = false;
    
            }
    
            /// <summary>
            /// Send data to the server.
            /// </summary>
            public void Send(String data)
            {
                try
                {
                    // String must be converted to byet[]
                    byte[] byteArray = new ASCIIEncoding().GetBytes(data);
    
                    // Send the data to the connected socket
                    // mySocket.BeginSend(byteArray, 0, byteArray.Length, SocketFlags.None, null, null);
                    mySocket.BeginSend(byteArray, 0, byteArray.Length, SocketFlags.None, new AsyncCallback(OnSend), null);
    
                    asyncEvent.Reset();
    
                    // Send the terminator
                    // mySocket.BeginSend(new ASCIIEncoding().GetBytes(terminator), 0, terminator.Length, SocketFlags.None, new AsyncCallback(OnSend), true);
                }
                catch (Exception ex)
                {
                    NotifyCaller(NotifyEvents.SendError, ex.Message);
                }
    
            }
    
            /// <summary>
            /// Async send callback
            /// </summary>
            private void OnSend(IAsyncResult ar)
            {
                try
                {
                    mySocket.EndSend(ar);
    
                    // ASSERT: Data sent successfully
                    NotifyCaller(NotifyEvents.DataSent, null);
                }
                catch (Exception ex)
                {
                    NotifyCaller(NotifyEvents.SendError, ex.Message);
                }
            }
    
            /// <summary>
            /// Receive data from the server.
            /// </summary>
            public void Receive()
            {
                try
                {
                    asyncEvent.Reset();
    
                    mySocket.BeginReceive(bRcvd, 0, BUFFER_SIZE, SocketFlags.None, new AsyncCallback(OnReceive), null);
    
                    //Thread.Sleep(1000);
                }
                catch (Exception ex)
                {
                    NotifyCaller(NotifyEvents.ReceiveError, ex.Message);
                }
            }
    
            /// <summary>
            /// Async send callback
            /// </summary>
            private void OnReceive(IAsyncResult ar)
            {
                try
                {
                    int len = mySocket.EndReceive(ar);
                    receiveBuf = ASCIIEncoding.ASCII.GetString(bRcvd, 0, len);
    
                    // ASSERT: Data receieved successfully
                    NotifyCaller(NotifyEvents.DataReceived, receiveBuf);
                }
                catch (Exception ex)
                {
                    NotifyCaller(NotifyEvents.ReceiveError, ex.Message);
                }
            }
    
            /// <summary>
            /// Notify the app
            /// </summary>
            private void NotifyCaller(NotifyEvents nEvent, object data)
            {
                // the async operation has completed
                asyncEvent.Set();
    
                // don't raise notification events when disconnecting
                if ((this.Notify != null) && !closing)
                    Notify(nEvent, data);
            }
    
        }
    }
    +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    "作者:" 数据酷软件工作室
    "出处:" http://datacool.cnblogs.com
    "专注于CMS(综合赋码系统),MES,WCS(智能仓储设备控制系统),WMS,商超,桑拿、餐饮、客房、足浴等行业收银系统的开发,15年+从业经验。因为专业,所以出色。"
    +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  • 相关阅读:
    一些至理名言
    移除快捷方式上面那个丑陋的小箭头
    一些浏览器插件
    yahoo给出的关于网站优化的建议
    javascript 事件流
    关于mongodb的一些笔记
    WebStorm
    给go添加各种package
    工具类 util.Date 日期类
    几种简单排序算法
  • 原文地址:https://www.cnblogs.com/datacool/p/DotNETCF_MC1000_APP_DEMO.html
Copyright © 2020-2023  润新知