zoukankan      html  css  js  c++  java
  • .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年+从业经验。因为专业,所以出色。"
    +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  • 相关阅读:
    long和Long的区别
    C语言的变量的内存分配
    Java蓝桥杯 算法提高 九宫格
    Java实现 蓝桥杯算法提高金明的预算方案
    Java实现 蓝桥杯 算法提高 新建Microsoft world文档
    Java实现 蓝桥杯 算法提高 快乐司机
    Java实现 蓝桥杯 算法提高 三角形
    Java实现 蓝桥杯 算法提高 三角形
    Java实现 蓝桥杯 算法提高 三角形
    Java实现 蓝桥杯 算法提高 三角形
  • 原文地址:https://www.cnblogs.com/datacool/p/DotNETCF_MC1000_APP_DEMO.html
Copyright © 2011-2022 走看看