.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; } } }
- 基类窗体的使用
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去用。本人没上过大学,不要太嫌我英语差啊。呵呵
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)设计模式的运用
项目里只用到了单例(单件)模式。
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; } } } }
没了。
下面给众多伸手党兄弟一些福利吧:
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(); } } } } }
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---------- } }
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年+从业经验。因为专业,所以出色。"
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++