C#读取u盘物理序列号

2008.12.15 / 标签: , / 分类: WinForm

看到网上有人出价500-2000RMB请人帮他写一个读取U盘物理序列号的软件,而且可以采用任何程序语言。可惜咱没那运气接到这个活,况且我也没写过这种东西,不过查了点资料把它写出来了。

源码如下:

 
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Data;
  5. using System.Drawing;
  6. using System.Text;
  7. using System.Windows.Forms;
  8. using System.Management;
  9. using System.Net;
  10. using System.Web;
  11. using System.IO;
  12. using System.Xml;
  13.  
  14. namespace MyDriver
  15. {
  16.     public partial class Form1 : Form
  17.     {
  18.         public Form1()
  19.         {
  20.             InitializeComponent();
  21.         }
  22.  
  23.         private void btnRead_Click(object sender, EventArgs e)
  24.         {
  25.             ManagementObject disk = null;
  26.             string driver = "win32_logicaldisk.deviceid=\""+this.txtDriver.Text+":\"";
  27.             try
  28.             {
  29.                 disk = new ManagementObject(driver);
  30.                 this.txtDriverCode.Text = disk.GetPropertyValue("VolumeSerialNumber").ToString();
  31.             }
  32.             catch (Exception ex)
  33.             {
  34.                 MessageBox.Show(ex.Message);
  35.             }
  36.         }
  37.     }
  38. }

运行起来试试看。一张DOS下的系统提示信息,另一张是软件查出来的。

 

并不是只能读取U盘的物理序列号,其他什么C、D、E…一样可以。不知道那人出那么多大价钱找人写这个东西有什么用?

50个提高C#编程水平的要诀

2008.07.27 / 标签: / 分类: WinForm

50个提高C#编程水平的要诀
1.总是用属性 (Property) 来代替可访问的数据成员
2.在 readonly 和 const 之间,优先使用 readonly
3.在 as 和 强制类型转换之间,优先使用 as 操作符
4.使用条件属性 (Conditional Attributes) 来代替条件编译语句 #if
5.总是为自定义类重载 ToString 方法
6.区别值类型和引用类型
7.使用不可变的值类型(Immutable Atomic Value Types)
8.在值类型中,确保0是一个合法的数据
9.理解 ReferenceEquals, static Equals, instance Equals 和 比较运算符(==)之间的关系
10.理解 GetHashCode方法的缺陷
11.在编写循环时,优先使用 foreach.
12.在定义变量的时候就将其初始化
13.使用静态构造函数来初始化静态成员变量
14.用多个构造函数时,利用构造函数链
15.使用using和try/finally来处理资源的释放
16.尽量避免产生资源垃圾
17.尽量避免使用装箱(boxing)和拆箱(unboxing)
18.实现类的 Dispose 方法
19.在接口和继承(Inheritance)之间,优先使用接口(interface)
20.区分接口和重载(overrides)
21.用委托(delegate)来实现回调(callback)
22.用事件(event)来定义外部接口
23.避免返回类内部成员的引用
24.使用元数据来控制程序
25.优先使用可序列化(serilizable)类型
26.对需要排序的对象实现IComparable和IComparer接口
27.避免使用 ICloneable接口
28.避免使用类型转换操作符
29.只有当基类加入了与派生类中现有的函数名称相同的函数时,才需要使用 new 操作符
30.尽量使用 CLS-Compliant
31.尽量编写短少,简单的函数
32.尽量编写比较小的程序集(assembly)
33.限定类型的可见性(visibility)
34.编写大粒度的 web API
35.在使用事件时,优先继承基类事件,而不是重新创建一个事件
36.多使用 framework 的运行时调试 (DEBUG, TRACE, EVENTLOG等)
37.使用.net标准的配置机制
38.使用并且在类中支持.net的数据绑定功能 (Data Binding)
39.使用.net的验证机制 (Validation)
40.根据你的需求选择正确的集合类(Collection)
41.在自定义结构中使用 DataSet
42.利用属性(Attributes)
43.不要过度使用反射(Reflection)
44.创建完整的,应用程序特定的异常
45.尽可能多的考虑程序可能出现的异常,并作出处理
46.尽可能少的使用 Interop
47.尽量使用安全代码 (safe code)
48.多多学习、使用外部工具和资源
49.准备使用 C# 2.0
50.学习 ECMA 标准

C#重启远程计算机

2008.07.25 / 标签: / 分类: WinForm
Sofa

如果叫你实现远程启动别人的计算机,你首先想到的可能是先做一个在远程计算机上面运行客户端程序,然后在本地计算机上面再做一个服务器端程序,通过这二个程序直接的通讯实现重启远程计算机。这当然是一个方法。但这未免有点麻烦。如果现在只告诉你远程计算机的管理者的登陆帐号,而并不允许你在远程的计算机上面运行一个所谓的客户端程序,让你通过程序来完成重启远程计算机。不知道你是否感觉有些困难了。其实按照上面的这些条件实现重启远程计算机,利用C#可以比较方便的完成。下面就来介绍一下具体的实现方法。   一. C#重启远程计算机的一些理论知识:   C#实现启动远程计算机的原理是"视窗管理规范"。就是所谓的"WMI"(Windows Management Instrumentation)。Windows 管理规范 (WMI) 支持通过 Internet 管理系统的结构。通过提供管理环境的一致观察,WMI 为用户提供通用访问管理信息。该管理的一致性使您能够管理整个系统,而不只是组件。从 Microsoft MSDN上,您可以获得有关 WMI 软件开发工具包 (SDK) 的详细信息。   WMI(Windows 管理规范)支持有限的安全格式,允许用户在本地计算机或远程计算机上连接 WMI 之前要验证每个用户。这种安全性是操作系统已有的安全顶端的另一层。WMI 不覆盖或破坏由操作系统提供的任何现有的安全性。在默认情况下,管理员组的所有成员都可以完全控制它管理的计算机上的 WMI 服务。其他所有用户在其本地计算机上只有读取/写入/执行的权限。可以通过向被管理的计算机上的管理员组添加用户,或者在 WMI 中授权用户或组并设置权限级别来更改权限。访问基于 WMI 名称空间。在一般情况下,脚本程序的默认命名空间是"root\cimv2"。   在WMI中有着许多足以令我们感觉惊奇的功能。重启远程计算机只是一个很小的功能。在程序中使用WMI可以编写出许多远程管理类型的应用程序。由于在.Net FrameWork SDK中提供了可以直接操作WMI的名称空间,所以C#就可以利用在这些名称空间中定义了的类来充分使用WMI控制给我们带来的各种方便。   二.程序设计和运行的环境设置:   (1).视窗2000服务器版   (2). .Net FrameWork SDK Beta 2   (3).远程计算机的管理者帐号   以上这些不仅是本地计算机配置,还是远程计算机的配置。   三.实现重启远程计算机所使用到在.Net FrameWork SDK Beta 2用以操作WMI名称空间和类:   在.Net FrameWork SDK Beta 2中用来操作WMI的名称空间主要是"System.Management"。要实现重启远程计算机所使用到的类主要有六个:   . "ConnectionOptions"类主要定义远程计算机的管理员帐号;   . "ManagementScope"主要是以给定的管理员帐号连接给定计算机名或者IP地址的计算机;   . "ObjectQuery"类功能是定义对远程计算机要实现那些地远程操作;   . "ManagementObjectSearcher"类从已经完成远程连接的计算机中,得到有那些WMI操作;   . "ManagementObjectCollection"类存放得到WMI操作;   . "ManagementObject"类调用远程计算机可进行WMI操作。   在本文介绍的操作就是重启操作。 本文发表于www.bianceng.cn(编程入门网)   四.C#重启远程计算机的重要步骤和实现方法:   (1).连接远程计算机:   按照下列语句可以实现连接远程计算机: ConnectionOptions options = new ConnectionOptions ( ) ; options.Username ="管理者帐号用户名"; options.Password = "管理者帐号口令" ; ManagementScope scope = new ManagementScope( "\\\\" + "远程计算机名或IP地址" + "\\root\\cimv2", options ) ; //用给定管理者用户名和口令连接远程的计算机 scope.Connect ( ) ;   (2).得到在远程计算机中可以进行WMI控制: System.Management.ObjectQuery oq = new System.Management.ObjectQuery ( "Select * FROM Win32_OperatingSystem" ) ; ManagementObjectSearcher query1 = new ManagementObjectSearcher ( scope , oq ) ; //得到WMI控制 ManagementObjectCollection queryCollection1 = query1.Get ( ) ;   (3).调用WMI控制,实现重启远程计算机: foreach ( ManagementObject mo in queryCollection1 ) { string [ ] ss= { "" } ; //重启远程计算机 mo.InvokeMethod ( "Reboot" , ss ) ; }   五.C#实现重启远程计算机的源程序代码(boot.cs)和执行界面:   在了解了C#实现重启远程计算机的这些重要步骤后,就可以从容的得到重启远程计算机的完整代码,具体如下:

 
  1. using System ; 
  2.   using System.Drawing ; 
  3.   using System.Collections ; 
  4.   using System.ComponentModel ; 
  5.   using System.Windows.Forms ; 
  6.   using System.Data ; 
  7.   using System.Management ; 
  8.   public class Form1 : Form 
  9.   { 
  10.   private TextBox textBox1 ; 
  11.   private TextBox textBox2 ; 
  12.   private TextBox textBox3 ; 
  13.   private Label label1 ; 
  14.   private Label label2 ; 
  15.   private Label label3 ; 
  16.   private Button button1 ; 
  17.   private System.ComponentModel.Container components = null ; 
  18.   public Form1 ( ) 
  19.   { 
  20.   //初始化窗体中的各个组件 
  21.   InitializeComponent ( ) ; 
  22.   } 
  23.   //清除程序中使用过的资源 
  24.   protected override void Dispose&nbsp
    ;( 
    bool disposing ) 
  25.   { 
  26.   if ( disposing ) 
  27.   { 
  28.   if ( components != null ) 
  29.   { 
  30.   components.Dispose ( ) ; 
  31.   } 
  32.   } 
  33.   base.Dispose ( disposing ) ; 
  34.   } 
  35.   private void InitializeComponent ( ) 
  36.   { 
  37.   textBox1 = new TextBox ( ) ; 
  38.   textBox2 = new TextBox ( ) ; 
  39.   textBox3 = new TextBox ( ) ; 
  40.   label1 = new Label ( ) ; 
  41.   label2 = new Label ( ) ; 
  42.   label3 = new Label ( ) ; 
  43.   button1 = new Button ( ) ; 
  44.   
  45.   SuspendLayout ( ) ; 
  46.   textBox1.Location = new System.Drawing.Point ( 140 , 46 ) ; 
  47.   textBox1.Name = "textBox1" ; 
  48.   textBox1.Size = new System.Drawing.Size ( 172 , 21 ) ; 
  49.   textBox1.TabIndex = 0 ; 
  50.   textBox1.Text = "" ; 
  51.   
  52.   textBox2.Location = new System.Drawing.Point ( 138 , 85 ) ; 
  53.   textBox2.Name = "textBox2" ; 
  54.   textBox2.Size = new System.Drawing.Size ( 174 , 21 ) ; 
  55.   textBox2.TabIndex = 1 ; 
  56.   textBox2.Text = "" ; 
  57.   
  58.   textBox3.Location = new System.Drawing.Point ( 139 , 120 ) ; 
  59.   textBox3.Name = "textBox3" ; 
  60.   textBox3.PasswordChar = ‘*‘ ; 
  61.   textBox3.Size = new System.Drawing.Size ( 173 , 21 ) ; 
  62.   textBox3.TabIndex = 2 ; 
  63.   textBox3.Text = "" ; 
  64.   
  65.   label1.Location = new System.Drawing.Point ( 24 , 50 ) ; 
  66.   label1.Name = "label1" ; 
  67.   label1.Size = new System.Drawing.Size ( 120 , 16 ) ; 
  68.   label1.TabIndex = 1 ; 
  69.   label1.Text = "机器名称或IP地址:" ; 
  70.   
  71.   label2.Location = new System.Drawing.Point ( 37 , 88 ) ; 
  72.   label2.Name = "label2" ; 
  73.   label2.TabIndex = 1 ; 
  74.   label2.Text = "管理者名称:" ; 
  75.   label2.TextAlign = System.Drawing.ContentAlignment.TopRight ; 
  76.   
  77.   label3.Location = new System.Drawing.Point ( 37 , 125 ) ; 
  78.   label3.Name = "label3" ; 
  79.   label3.Size = new System.Drawing.Size ( 100 , 16 ) ; 
  80.   label3.TabIndex = 1 ; 
  81.   label3.Text = "管理者密码:" ; 
  82.   label3.TextAlign = System.Drawing.ContentAlignment.TopRight ; 
  83.   
  84.   button1.Location = new System.Drawing.Point ( 95 , 168 ) ; 
  85.   button1.Name = "button1" ; 
  86.   button1.Size = new System.Drawing.Size ( 136 , 32 ) ; 
  87.   button1.TabIndex = 3 ; 
  88.   button1.Text = "重新启动远程计算机" ; 
  89. <
    span>  button1.Click += new System.EventHandler ( button1_Click ) ; 
  90.   
  91.   this.AutoScaleBaseSize = new System.Drawing.Size ( 6 , 14 ) ; 
  92.   this.ClientSize = new System.Drawing.Size ( 336 , 245 ) ; 
  93.   this.Controls.Add ( button1 ) ; 
  94.   this.Controls.Add ( textBox1 ) ; 
  95.   this.Controls.Add ( textBox2 ) ; 
  96.   this.Controls.Add ( textBox3 ) ; 
  97.   this.Controls.Add ( label1 ) ; 
  98.   this.Controls.Add ( label2 ) ; 
  99.   this.Controls.Add ( label3 ) ; 
  100.   
  101.   this.Name = "Form1" ; 
  102.   this.Text = "利用C#重新启动远程计算机" ; 
  103.   this.ResumeLayout(false) ; 
  104.   
  105.   } 
  106.   static void Main ( ) 
  107.   { 
  108.   Application.Run ( new Form1 ( ) ) ; 
  109.   } 
  110.   private void button1_Click ( object sender , System.EventArgs e ) 
  111.   { 
  112.   //定义连接远程计算机的一些选项 
  113.   ConnectionOptions options = new ConnectionOptions ( ) ; 
  114.   options.Username = textBox2.Text ; 
  115.   options.Password = textBox3.Text ; 
  116.   ManagementScope scope = new ManagementScope( "\\\\" + textBox1.Text + "\\root\\cimv2", options ) ; 
  117.   try { 
  118.   //用给定管理者用户名和口令连接远程的计算机 
  119.   scope.Connect ( ) ; 
  120.   System.Management.ObjectQuery oq = new System.Management.ObjectQuery ( "Select * FROM Win32_OperatingSystem" ) ; 
  121.   ManagementObjectSearcher query1 = new ManagementObjectSearcher ( scope , oq ) ; 
  122.   //得到WMI控制 
  123.   ManagementObjectCollection queryCollection1 = query1.Get ( ) ; 
  124.   foreach ( ManagementObject mo in queryCollection1 ) 
  125.   { 
  126.   string [ ] ss= { "" } ; 
  127.   //重启远程计算机 
  128.   mo.InvokeMethod ( "Reboot" , ss ) ; 
  129.   } 
  130.   } 
  131.   //报错 
  132.   catch ( Exception ee ) { 
  133.   MessageBox.Show ( "连接" + textBox1.Text + "出错,出错信息为:" + ee.Message ) ; 
  134.   } 
  135.   } 
  136.   } 

  六.总结:   其实WMI控制可以实现很多以前让我们很头痛的操作。并且使用WMI编写的管理程序也比不用WMI来实现同样功能的程序在设计难度上大大减轻。WMI内容十分丰富,重新启动远程计算机只是其中的一个最为基本的操作。在使用WMI控制之前有一点必须记住,就是你必须知道你所要进行操作的远程计算机的超级管理者的帐号,这是使用WMI的一个前提。

 

app.config的读写操作

2008.07.07 / 标签: / 分类: WinForm

昨天我想的是使用[DataSet读取XML文件] 来完成数据库的动态切换(还没完成 – -!),今天老师给了另一个方法:添加app.config配置文件,找了些关于这个的资料如下:在winform中使用程序读取和修改App.config里面的appSettings当中的Value值这里我写成了两个方法,以供大家参考!

 
  1. //一,命名空间
  2. using System;
  3. using System.Configuration;
  4. using System.Xml;
  5. //二,方法
  6. //读取Value值
  7. public static string GetConfigString(string key)
  8.   {
  9.    //
  10.    // TODO: 在此处添加构造函数逻辑
  11.    //
  12.    return ConfigurationSettings.AppSettings[key];
  13.   } 
  14.   //写操作
  15.   public static void SetValue(string AppKey,string AppValue)
  16.   {
  17.    XmlDocument xDoc = new XmlDocument();
  18.    //获取可执行文件的路径和名称
  19.    xDoc.Load(System.Windows.Forms.Application.ExecutablePath + ".config");
  20.     
  21.    XmlNode xNode;
  22.    XmlElement xElem1;
  23.    XmlElement xElem2;
  24.    xNode =  xDoc.SelectSingleNode("//appSettings");
  25.    
  26.    xElem1 = (XmlElement)xNode.SelectSingleNode("//add[@key='" + AppKey + "']");
  27.    if ( xElem1 != null ) xElem1.SetAttribute("value",AppValue);
  28.    else
  29.    {
  30.     xElem2 = xDoc.CreateElement("add");
  31.     xElem2.SetAttribute("key",AppKey);
  32.     xElem2.SetAttribute("value",AppValue);
  33.     xNode.AppendChild(xElem2);
  34.    }
  35.    xDoc.Save(System.Windows.Forms.Application.ExecutablePath + ".config");
  36.   }

 

DataSet读取XML文件

2008.07.05 / 标签: / 分类: WinForm
Sofa

老师让用Winform做一个ATM自动提款机系统,一个星期后交,其实把大街上的ATM仿一下貌似也不难,不过老师说必须可以动态更换数据库。具体一点就是要求能够在MSSQL与ACCESS两个数据库之间进行切换。
我的思路:写一个数据库类型配置文件,然后程序启动时读取这个配置文件,再进行对配置文件中指定的数据库访问。
想到的的方法:
阅读全文>>

如何设计水晶报表

2008.06.30 / 标签: / 分类: WinForm
Sofa

如何设计水晶报表

1.新建一个名为“Example”的Windows应用程序。讲Form1.cs文件名更改为frmBooks-Report.cs。

2.将Text属性更改为“书籍浏览报表”,将Name属性更改为frmBooksReport。

3.在菜单中选择“项目”->“添加项目”。

4.在“类别”区域中扩展“本地项目”,选择“数据”;在“模板”区域中选择“数据集”。将Dataset1.xsd文件名更改为BooksDataset.xsd,单击打开按钮生成一个新的结构文件BooksDataset.xsd,用于生成强类型化数据集。该结构文件将显示在ADO.NET数据集设计器中。

5.单击“服务器资源管理器”链接,显示“服务器资源管理器”窗口。右键单击“数据连接”,选择“添加连接”。此时将显示“数据链接属性”对话框。在“提供程序”选项卡中选择Microsoft OLE DB Provider for SQL Server,在“连接”选项卡中选择或输入“服务器名称”等参数。单击“确认”按钮,进入“服务器资源管理器”->“数据连接”,展开“表”选项,显示数据表及字段名。

6.将数据表Books从服务器资源管理器拖至BooksDataset.xsd的“数据集”选项卡。

7.保存BooksDataset.xsd文件。

8.单击“生成”->“生成解决方案”,将为该项目生成一个数据集对象。

9.从菜单中单击“项目”->“添加新项”。在“类别”区域选择“本地项目项”,在“模板”区域选择“Crystal Report”。

10。将CrystalReport.rpt文件名更改为BooksCrystalReport.rpt,单击“打开”按钮。

11.在“Crystal Report”窗口中,选择“使用报表专家”及“标准”选项,单击“确定”按钮继续。

12.在“标准报表专家”对话框的“数据”选项卡中展开“项目数据”->“ADO.NET数据集”,并选择DataSet对象。

13.选择Books表,单击“插入表”,单击“下一步”按钮进入“字段”选项卡。

14.在“字段”选项卡中选中Books表,点击“全部添加”按钮,添加表中所有列,单击“下一步”进入“组”选项卡。

15.选择Publisher列并单击“添加”按钮,将选中的列添加到“分组依据”中。

16.用户可以在此处为特定组指定一个或多个字段,以便进行汇总。在“汇总”框中留有“Book.Price”字段,然后单击“下一步”进入“最前N个”选项卡。

17.在“对于该组排序”中选择“全部”,在“基于”下拉列表中选择“Book.Price的和”,单击“下一步”继续。

18.此时将显示“图表”选项卡,选择默认图表并单击“下一步”。

19.此时将显示“选择”选项卡。用户可以筛选该选项卡中的数据,以便在报表中显示数据的子集,单击“下一步”继续。

20.此时将显示“样式”选项卡,在“标题”处输入“图书浏览报表”,单击“完成”按钮。

创建报表后,将数据填充到数据集中,把数据集作为报表的数据源,将报表与报表查看器进行绑定,以显示数据。

水晶报表的优点

2008.06.29 / 标签: / 分类: WinForm
Sofa

水晶报表水晶报表的优点

通常,应用程序可以遍历数据集,然后将数据显示在应用程序的窗口中。但是,随着用户对显示数据的需求越来越复杂、要求越来越高,诸如要实现小计、汇总、设定条件格式化和图表等功能,使得应程序开发增加了巨大的难度。因此,引入报表工具简化开发过程、降低开发难度已迫在眉睫。

此时就突显了水晶报表的重要性。水晶报表提供了简单易用的界面来根据需要创建、格式化并操纵报表,能够更容易的创建复杂的报表,并且降低了编码量。它强大的报表引擎将处理指定的格式化、分组和制图标准。具体功能如下:

1.运行时自定义:通过为用户提供定制报表的选项和报表查看器,实现如搜索、刷新或导航操作,还可以设置报表格式,修改字体、颜色。

2.报表查看器与其他空间之间交互:可以使用报表引擎的报表对象模型,将代码添加至Web或Winform页的源文件中,并允许查看器控件与同一页中的其他控件交互。

3.报表作为Web服务:在项目中创建水晶报表并生成Web服务时,Visual Studio.NET会将Web服务编译为.DLL文件,并生成一个可扩展标记语言(XML)文件,用以描述公共函数、输入参数、数据类型和报表Web服务公开的返回数据类型。Web浏览器上的客户端将通过HTTP调用报表的Web服务,而XML则用来向Web服务传入数据或从其中传出数据。当作为Web服务的报表在Web服务器上发布以后,客户端上的应用程序就可以使用该服务。

水晶报表简介

2008.06.29 / 标签: / 分类: WinForm
Sofa

水晶报表水晶报表简介

假设一家公司的经历需要了解公司各部门职员的薪金情况,该公司已有一个可按顺序查看每位职员记录的应用程序,但是,公司经理还希望以表格的形式汇总职员记录。在这种情况下,就需要以数据分析的格式显示数据或信息,并且能够整洁、有条理地显示数据。

报表有助于从现有数据中汇总所需要信息。相同的数据既可用来获得各部门总薪金的报告,又可用来获得各部门志愿书的报告。

水晶报表是最为流行的报表制作工具之一,它功能强大,简单易用,被国内外很多大型公司采用。使用水晶报表可以创建交互式的高质量报表,不仅能为Winform和WebForms创建报表,还能以报表Web服务的形式将报表存放在Web服务器上。水晶报表可以创建复杂的具体专业外观的报表。

水晶报表已经被集成到Visual Studio.NET开发环境当中,它已成为Visual Studio.NET创建报表的标准工具,它使.NET平台能够创建交互式的、高质量的报表。

Timer的Interval 属性

2008.06.26 / 标签: / 分类: WinForm
Sofa

Timer的Interval 属性
当编写 Timer 组件时,需要考虑 Interval 属性的几点限制:
1. 如果应用程序或另一个应用程序对系统需求很大(如长循环、大量的计算或驱动程序、网络或端口访问),那么应用程序可能无法以 Interval 属性指定的频率来获取计时器事件。
2. 间隔可以在 1 和 64,767 之间(包括 1 和 64,767),这意味着即使最长的间隔(大约 64.8 秒)也不会超过一分钟很多。
3. 不能保证间隔所精确经过的时间。若要确保精确,计时器应根据需要检查系统时钟,而不是尝试在内部跟踪所积累的时间。
4. 系统每秒生成 18 个时钟刻度,因此即使 Interval 属性以毫秒为单位,间隔的实际精度也不会超过十八分之一秒。

使用TreeView与ListView控件

2008.06.26 / 标签: , / 分类: WinForm
Sofa


使用TreeView与ListView控件,为网络游戏后台管理系统添加玩家管理
提示:
阅读全文>>

无觅相关文章插件,快速提升流量