
书生一枚,码农一只。
Visual Studio 中有很多代码段都可以直接简写然后按TAB快速输入编译器中,为了提高编程效率,特此查阅资料,罗列总结。 1. ~ 创建析构函数 ~Program() { } 2. checked 创建checked块 checked { } 3. class 创建类声明 classMyClass { } 4. ctor 创建对应类的构造函数 public Program () { } 5. cw 创建对Console.WriteLine();的调用 Console.WriteLine(); 6. do 创建do(while)循环 do { } while (true); 7. else 创建else块 else { } 8. enum 创建enum声明 enumMyEnum { } 9. for 创建for循环 for (int i = 0; i < length; i++) { } 10. foreach 创建foreach循环 foreach (var itemin collection) { } var 代表要循环访问的集合中对象的类型 item 表示集合中的元素的标示符 collection 要循环访问的集合或数组的名称 11. forr 创建for循环,在每次循环后递减循环变量 for (int i = length - 1; i >= 0; i--) { } 12. if 创建if块 if (true) { } 13. interface 创建interface声明 interfaceIInterface { } IInterface 代表接口名称 14. lock 创建lock块 lock (this) { } this 代表表达式 15. namespace 创建namespace声明 namespace MyNamespace { } 16. prop 创建属性代码块 publicint MyProperty {get;set; } 17. struct 创建struct声明 struct MyStruct { } 18. svm 创建static viod声明 staticvoid Main(string[] args) { } 19. switch 创建switch代码块 switch (switch_on) { default: } switch_on 代表条件表达式 20. try 创建try-catch代码块 try { } catch (Exception) { throw; } 21. unchecked 创建unchecked代码块 unchecked { } 22. unsafe 创建unsafe代码块 unsafe { } 23. using 创建using指令 using (resource) { } resource为要使用的资源 24. while 创建while循环 while (true) { } true可替换为运算结果为bool类型的表达式。 如上属于visual studio 中常用的C#代码段简写,我们还可以通过在VC#\Snippets\2052\Visual C#中添加.snippet文件, 编写自定义的C#代码段,具体方法,待以后学会后再做总结:-D --------------------------------------------------------- 2013-12-12 补充: 自定义代码段方法链接:VS中自定义代码段
Xcopy "C:\SOFT_PHP_PACKAGE\mysql\data\*.*" F:\XBackup\%date:~0,10%\ /e/y 对于mysql,就是拷贝其data下目录到指定位置,如果损坏,停止服务原样拷回即可,%date:~0,10%是取当天日期 Xcopy "C:\wwwroot\*.*" F:\XBackup\www\%date:~0,10%\ /e/y Web目录直接拷贝到固定位置
在资料分析中比重计算型题目越来越多,难度也越来越大,而尤以基期比重的计算难度最大,这也是在目前国、联考中的重点与难点,针对这一问题,本节就对其作答技巧进行详细的讲解。 一般情况下,题目会告知部分和整体的现期量,我们常用A和B表示,并且告诉我们其分别对应的增长率为a和b,问题即求部分占整体的基期比重。欲求基期比重,首先得分
论坛被机器人折磨的欲仙欲死,实在没折,决定启用邮件验证,怎么配置都发不出来,另一个论坛却一点问题没有,排查后,结果在DATA下的logo里看到大量日志,SMTP一般是这样的: SendMail一般是这样的: 从提示来看,应该是说发信人没人填,排查后台,都填了呀,但是,两个地方填的不一样,哪两个地方呢?看下图: 要一致才行。 看似小问题,却是浪费了不少时间。
这是关于SQL服务的一些端口: ms-sql-s 1433/tcp #Microsoft-SQL-Server ms-sql-s 1433/udp #Microsoft-SQL-Server ms-sql-m 1434/tcp #Microsoft-SQL-Monitorms-sql-m 1434/udp #Microsoft-SQL-Monitor
1.会员自己修改过密码还是原密码,就是无论如何也修改不成功密码。 原因:开发人员把变量名写错了,导致收到的变量始终是原来的密码,修改profileModule.class.php文件中$user_data['user_pwd'] = md5($user_pwd.$GLOBALS['user']['salt']);红色部分改成new,即:$user_data['user_pwd'] = md5($new_pwd.$GLOBALS['user']['salt']); 2.后台数据库备份报错。 原因:在public目录下缺少一个目录db_backup,手动创建一个即可。 3.替换管理后台的logo. 原因:路径为admin\Tpl\dwz\themes\default\images\logo.png 4.管理后台标识信息。 原因:修改 admin\Tpl\core\index\index.htm 5.去掉标题中的“福州” 方案:直接修改/inc/head.html,将title写死或者将head模板中最后那个变量<title>{if $page_title neq ''}{$page_title} - {/if}{$site_name}- {$site_title}</title>直接删除。
最近总是有户反馈后台设置了QQ在线客服号码后,在前台点击在线客服,提示:抱歉,无法发起临时会话,您可以 添加对方为好友以发送消息。无法发起临时会话。那么这个如何处理呢方法如下: 只是需要修改几个文件即可 source/module/forum/forum_viewthread.php template/default/forum/trade_info.htm template/default/common/footer.htm 找到这几个文件编辑 找到这行代码 <a href="http://wpa.qq.com/msgrd?V=3&Uin= 复制代码 改为 <a href="http://wpa.qq.com/msgrd?V=3&uin= 复制代码 其实就是把这行代码中的U改成小写的u即可 那么这个问题就解决了。
1.先下载discuzx_update_sitekey.php,放到站上,更新站点信息 2.如果不成功,会跳出一个下载形如cloud_e70e0b1a50c2d72411d7170aadae42ad.php的文件到根目录,然后用他直接会重新配置云平台。 3.如是密码忘记了,或者数据库坏了,使用tools.php,默认密码admin,一次全搞定。 /admin.php?action=cloud&operation=open 开通云平台 /admin.php?frames=yes&action=cloud&operation=applist 云平台已经安装的插件
模块导致,后台,防灌水,验证设置,选英文,直接提交,然后再看,就可以了显示了。
一般来说,在更新DataTable或是DataSet时,如果不采用SqlParameter,那么当输入的Sql语句出现歧义时,如字符串中含有单引号,程序就会发生错误,并且他人可以轻易地通过拼接Sql语句来进行注入攻击。 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 string sql = "update Table1 set name = 'Pudding' where ID = '1'";//未采用SqlParameter SqlConnection conn = new SqlConnection(); conn.ConnectionString = "Data Source=.\\SQLExpress;Integrated Security=true;AttachDbFilename=|DataDirectory|\\Database.mdf;User Instance=true";//连接字符串与数据库有关 SqlCommand cmd = new SqlCommand(sql, conn); try { conn.Open(); return(cmd.ExecuteNonQuery()); } catch (Exception) { return -1; throw; } finally { conn.Close(); } 上述代码未采用SqlParameter,除了存在安全性问题,该方法还无法解决二进制流的更新,如图片文件。通过使用SqlParameter可以解决上述问题,常见的使用方法有两种,Add方法和AddRange方法。 一、Add方法 ? 1 2 3 4 SqlParameter sp = new SqlParameter("@name","Pudding"); cmd.Parameters.Add(sp); sp = new SqlParameter("@ID","1"); cmd.Parameters.Add(sp); 该方法每次只能添加一个SqlParameter。上述代码的功能是将ID值等于1的字段name更新为Pudding(人名)。 二、AddRange方法 ? 1 2 SqlParameter[] paras = new SqlParameter[] { new SqlParameter("@name","Pudding"),new SqlParameter("@ID","1") }; cmd.Parameters.AddRange(paras); 显然,Add方法在添加多个SqlParameter时不方便,此时,可以采用AddRange方法。 下面是通过SqlParameter向数据库存储及读取图片的代码。 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 public int SavePhoto(string photourl) { FileStream fs = new FileStream(photourl, FileMode.Open, FileAccess.Read);//创建FileStream对象,用于向BinaryReader写入字节数据流 BinaryReader br = new BinaryReader(fs);//创建BinaryReader对象,用于写入下面的byte数组 byte[] photo = br.ReadBytes((int)fs.Length);//新建byte数组,写入br中的数据 br.Close();//记得要关闭br fs.Close();//还有fs string sql = "update Table1 set photo = @photo where ID = '0'"; SqlConnection conn = new SqlConnection(); conn.ConnectionString = "Data Source=.\\SQLExpress;Integrated Security=true;AttachDbFilename=|DataDirectory|\\Database.mdf;User Instance=true"; SqlCommand cmd = new SqlCommand(sql, conn); SqlParameter sp = new SqlParameter("@photo", photo); cmd.Parameters.Add(sp); try { conn.Open(); return (cmd.ExecuteNonQuery()); } catch (Exception) { return -1; throw; } finally { conn.Close(); } } public void ReadPhoto(string url) { string sql = "select photo from Table1 where ID = '0'"; SqlConnection conn = new SqlConnection(); conn.ConnectionString = "Data Source=.\\SQLExpress;Integrated Security=true;AttachDbFilename=|DataDirectory|\\Database.mdf;User Instance=true"; SqlCommand cmd = new SqlCommand(sql, conn); try { conn.Open(); SqlDataReader reader = cmd.ExecuteReader();//采用SqlDataReader的方法来读取数据 if (reader.Read()) { byte[] photo = reader[0] as byte[];//将第0列的数据写入byte数组 FileStream fs = new FileStream(url,FileMode.CreateNew);创建FileStream对象,用于写入字节数据流 fs.Write(photo,0,photo.Length);//将byte数组中的数据写入fs fs.Close();//关闭fs } reader.Close();//关闭reader } catch (Exception ex) { throw; } finally { conn.Close(); } }}
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; //先打开类库文件 using System.Data.SqlClient; namespace WindowsFormsApplication1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { char[] name; int age; name = textBox1.Text.ToCharArray(); age = System.Int32.Parse(textBox2.Text); SqlConnection con = new SqlConnection(); con.ConnectionString = "server=192.168.1.60;database=Test;uid=sa;pwd=123abc!"; con.Open(); /* SqlDataAdapter 对象。 用于填充DataSet (数据集)。 SqlDataReader 对象。 从数据库中读取流.. 后面要做增删改查还需要用到 DataSet 对象。 */ SqlCommand com = new SqlCommand(); com.Connection = con; com.CommandType = CommandType.Text; com.CommandText = "INSERT INTO [Test].[dbo].[stu]([name],[age])VALUES('李',"+age+")"; SqlDataReader dr = com.ExecuteReader();//执行SQL语 dr.Close();//关闭执行 con.Close();//关闭数据库 } private void label1_Click(object sender, EventArgs e) { } } }
找到XXX对应的PID netstat -ano|findstr "XXX" 然后找到PID"YYYY"对应的程序名 tasklist |findstr "YYYY" 如果是Linux下,那就是 ps -aux|grep"XXX"
处理程序“ExtensionlessUrlHandler-Integrated-4.0”在其模块列表中有一个错误模块“ManagedPipelineHandler” IIS上部署MVC网站,打开后ExtensionlessUrlHandler-Integrated-4.0解决办法 IIS上部署MVC网站,打开后ExtensionlessUrlHandler-Integrated-4.0解决方法 IIS上部署MVC网站,打开后500错误:处理程序“ExtensionlessUrlHandler-Integrated-4.0”在其模块列表中有一个错误模块“ManagedPipelineHandler” 解决方法如下: 以管理员运行下面的命令注册: 32位机器: C:\Windows\Microsoft.NET\Framework\v4.0.30319\aspnet_regiis.exe -i 64位机器: C:\Windows\Microsoft.NET\Framework64\v4.0.30319\aspnet_regiis.exe -i
总体思路: 要分四个大步骤完成,改两个文件,执行授权,后台系统设置->网站参数页面提交一下。 一、改mconfigs.cac.php文件 1、打开网站目录下 "dynamic\cache\mconfigs.cac.php" 这个文件; 2、搜索关键字 hosturl 找到:'hosturl' => 'http://www.old-domain.com',将"www.old-domain.com"改成您要启用的新域名,比如:'hosturl'=> 'http://www.new-domain.com'; 3、搜索关键字 cms_top 找到:'cms_top' => 'old-domain.com',将"old-domain.com"删除,变成:'cms_top'=> ''。 二、改base.inc.php文件 1、打开网站目录下 "base.inc.php" 这个文件; 2、搜索关键字 ckpre 找到:$ckpre ='LMf_',将"LMf_"修改、添加或者删除几个字符都可以,比如:$ckpre= 'xLMf_'; 3、搜索关键字 excache_prefix 找到:$excache_prefix = 'k8r7oW_',将"k8r7oW_"修改、添加或者删除几个字符都可以,比如:$excache_prefix= '7oW_'。 三、执行授权 执行http://www.new-domain.com/install/license.php 显示licenseupdated !说明执行成功。 注意: 执行成功不等于就是授权成功,这里主要确定下域名是否有授权,一般会在程序安装包里集成了授权文件,也有的是另外发的一个授权包,如果是另外发的授权包需要将授权 包中的dynamic文件夹覆盖到程序安装目录。 四、登录后台提交 现在正常的话就可以登录后台了,打开【系统设置->网站参数】页面,点击下面的的【提交】按钮,完成域名更改。
import commands def get_cpu_temp(): tempFile = open( "/sys/class/thermal/thermal_zone0/temp" ) cpu_temp = tempFile.read() tempFile.close() return float(cpu_temp)/1000 # Uncomment the next line if you want the temp in Fahrenheit #return float(1.8*cpu_temp)+32 def get_gpu_temp(): gpu_temp = commands.getoutput( '/opt/vc/bin/vcgencmd measure_temp' ).replace( 'temp=', '' ).replace( '\'C', '' ) return float(gpu_temp) # Uncomment the next line if you want the temp in Fahrenheit # return float(1.8* gpu_temp)+32 def main(): print "CPU temp: ", str(get_cpu_temp()) print "GPU temp: ", str(get_gpu_temp()) if __name__ == '__main__': main()
centos下yum暂时没有mysql-server直接安装包; MariaDB是MySQL社区开发的分支,也是一个增强型的替代品; 安装MariaDB yum -y install mariadb-server mariadb mariadb-devel systemctl start mariadb systemctl enable mariadb mysql_secure_installation firewall-cmd --permanent --add-service mysql systemctl restart firewalld.service iptables -L -n|grep 3306 登录数据库查看下是否有变好 msyql -uroot -p show databases; #Web服务 firewall-cmd --permanent --add-service httpsystemctl restart firewalld.serviceiptables -L -n|grep 80
一:所用命令 dmesg | grep firmware(看看有没有来自无线网卡的固件请求) iw: iw dev(查找无线网卡口) iw wlan0 link(查看wlan0网口无线网络连接情况) iw wlan0 scan | grepSSID(查看wlan0网口可连接的wifi) ip: ip link set wlan0 up(将无线网口wlan0开启) ip link show wlan0(显示无线网口wlan0连接情况) ip addr show wlan0(显示分配的ip地址,特别适用于查看是否成功地通过dhcp自动获取了ip地址) wpa_supplican: wpa_supplicant-B -i wlan0 -c <(wpa_passphrase "ssid" "psk") (连接无线网ssid,密码psk) dhclient: dhclient wlan0(为wlan0分配ip地址) 如需使用上述命令,只需将wlan0直接更换成自己网口就行了 二:具体过程: 1. 查看是否需要安装固件 大多无线网卡还需要固件。内核一般会自动探测并加载两者,如果您得到类似 SIOCSIFFLAGS: No such file or directory 的输出,意味着您得手动加载固件。若不确定,用 dmesg 查询内核日志,看看有没有来自无线网卡的固件请求。比如您有 Intel 芯片组,输出大概是这样: # dmesg | grep firmware firmware: requesting iwlwifi-5000-1.ucode若无输出,表明系统的无线芯片不需要固件。 2. 查看无线网口: #iw dev(interface后面即为无线网口号) 3. 激活无线网络接口: # ip link set wlan0 up 为了检验接口是否激活成功,您可以查看以下命令的输出: # ip link show wlan0 3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state DOWNmode DORMANT group default qlen 1000 link/ether 00:11:22:33:44:55 brdff:ff:ff:ff:ff:ff <BROADCAST,MULTICAST,UP,LOWER_UP> 中的UP 表明该接口激活成功,后面的 state DOWN 无关紧要。 4. 查看无线网络连接情况: #iw wlan0 link刚开始应该会显示无连接 5. 扫描可连接的wifi #iw wlan0 scan | grep SSID 扫描可用的网络 6. 连接指定的SSID # wpa_supplicant -B -i wlan0 -c <(wpa_passphrase "ssid""psk") 将ssid 替换为实际的网络名称,psk 替换为无线密码,请保留引号。 7. 用dhcp 获得 IP 分配: # dhclient wlan0 8. 测试是否成功地从路由器获取了ip(重要) #ip addr show wlan0 如果分配有ip,即可上网,也可以有ping直接测试
默认的颜色有点淡, 有些要凑近了才看的清, 于是想更改成白底黑字的, Putty--> Window-->Colours-->General options for colour usage 选中 Use system colours 再进入Putty界面登录后,就成白底彩字了,还好,比以前看起来顺一点.
fg、bg、jobs、&、nohup、ctrl+z、ctrl+c 命令 一、& 加在一个命令的最后,可以把这个命令放到后台执行,如 watch -n 10 sh test.sh & #每10s在后台执行一次test.sh脚本 二、ctrl + z 可以将一个正在前台执行的命令放到后台,并且处于暂停状态。 三、jobs 查看当前有多少在后台运行的命令 jobs -l选项可显示所有任务的PID,jobs的状态可以是running, stopped, Terminated。但是如果任务被终止了(kill),shell 从当前的shell环境已知的列表中删除任务的进程标识。 四、fg 将后台中的命令调至前台继续运行。如果后台中有多个命令,可以用fg %jobnumber(是命令编号,不是进程号)将选中的命令调出。 五、bg 将一个在后台暂停的命令,变成在后台继续执行。如果后台中有多个命令,可以用bg %jobnumber将选中的命令调出。 六、kill 法子1:通过jobs命令查看job号(假设为num),然后执行kill %num 法子2:通过ps命令查看job的进程号(PID,假设为pid),然后执行kill pid 前台进程的终止:Ctrl+c 七、nohup 如果让程序始终在后台执行,即使关闭当前的终端也执行(之前的&做不到),这时候需要nohup。该命令可以在你退出帐户/关闭终端之后继续运行相应的进程。关闭中断后,在另一个终端jobs已经无法看到后台跑得程序了,此时利用ps(进程查看命令) ps -aux | grep "test.sh" #a:显示所有程序 u:以用户为主的格式来显示 x:显示所有程序,不以终端机来区分 进程的终止: 后台进程的终止:
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak #备份为 sources.list.bak sudo nano /etc/apt/sources.list #编辑sources.list 文件 #以下内容为源,其他源同样加入 #阿里云 deb http://mirrors.aliyun.com/raspbian/raspbian/ wheezy main non-free contrib deb-src http://mirrors.aliyun.com/raspbian/raspbian/ wheezy main non-free contrib #清华 deb http://mirrors.tuna.tsinghua.edu.cn/raspbian/raspbian/ wheezy main contrib non-free rpi deb-src http://mirrors.tuna.tsinghua.edu.cn/raspbian/raspbian/ wheezy main contrib non-free rpi #东软 deb http://mirrors.neusoft.edu.cn/raspbian/raspbian/ wheezy main contrib non-free rpi deb-src http://mirrors.neusoft.edu.cn/raspbian/raspbian/ wheezy main contrib non-free rpi #中科大 deb http://mirrors.ustc.edu.cn/raspbian/raspbian/ wheezy main contrib non-free rpi deb-src http://mirrors.ustc.edu.cn/raspbian/raspbian/ wheezy main contrib non-free rpi #ctrl+D,Ctrl+X,Y,保存编辑 sudo apt-get update && apt-get upgrade -y #更新系统软件 并 更新已 要装MYSql必须只能用下面的源 deb http://mirrors.aliyun.com/raspbian/raspbian/ wheezy main non-free contrib deb-src http://mirrors.aliyun.com/raspbian/raspbian/ wheezy main non-free contrib deb http://mirrors.aliyun.com/raspbian/raspbian/ jessie main contrib non-free rpi
sudo dpkg-reconfigure tzdata
树莓派使用的linux是debian系统,所以树莓派启用root和debian是相同的。 debian里root账户默认没有密码,但账户锁定。 当需要root权限时,由默认账户经由sudo执行,Raspberry pi 系统中的Raspbian 默认用户是pi 密码为raspberry 重新开启root账号,可由pi用户登录后,在命令行下执行 sudo passwd root 执行此命令后系统会提示输入两遍的root密码,输入你想设的密码即可,然后在执行 sudo passwd --unlock root 这样就可以解锁root账户了。
树莓派可以通过下面几个命令来实现安全关机: sudo shutdown -h now sudo halt sudo poweroff sudo init 0 上面四行代码都可以,执行一行都可以安全关机. 树莓派重启 定时重启方法: sudo reboot shutdown -r now
以58mm热敏打印机为例 1, 安装好打印机驱动程序。点击光盘58mm Series-Driver-DRV58CN V4.51G直接安装。 2, 点击电脑之开始-控制面板-打印机和传真-选中打印机驱动程序按右键进入属性-打印首选项-高级-媒体,选择CASH DRAWER BEFORE PRINGTING-确定 3, 将钱箱水晶头插入打印机背部孔里。 4, 在每次打印前就开钱箱
VLOOKUP(lookup_value,table_array,col_index_num,range_lookup) Lookup_value为需要在数据表第一列中进行查找的数值。Lookup_value 可以为数值、引用或文本字符串。当vlookup函数第一参数省略查找值时,表示用0查找。 Table_array为需要在其中查找数据的数据表。使用对区域或区域名称的引用。 col_index_num为table_array 中查找数据的数据列序号。col_index_num 为 1 时,返回 table_array 第一列的数值,col_index_num 为 2 时,返回 table_array 第二列的数值,以此类推。如果 col_index_num 小于1,函数 VLOOKUP 返回错误值#VALUE!;如果 col_index_num 大于 table_array 的列数,函数 VLOOKUP 返回错误值#REF!。 Range_lookup为一逻辑值,指明函数 VLOOKUP 查找时是精确匹配,还是近似匹配。如果为false或0 ,则返回精确匹配,如果找不到,则返回错误值 #N/A。如果 range_lookup 为TRUE或1,函数 VLOOKUP 将查找近似匹配值,也就是说,如果找不到精确匹配值,则返回小于 lookup_value 的最大数值。如果range_lookup 省略,则默认为近似匹配。 =if(ISNA(VLOOKUP(Sour!A1,Ref!A:B,1,0)),Sour!A1,"已处理")
VC 多个定时器 SetTimer函数的原型: UINT_PTR SetTimer( HWND hWnd, // 窗口句柄 UINT_PTR nIDEvent, // 定时器ID,多个定时器时,可以通过该ID判断是哪个定时器 UINT uElapse, // 时间间隔,单位为毫秒 TIMERPROC lpTimerFunc // 回调函数); 在MFC程序中SetTimer被封装在CWnd类中,调用就不用指定窗口句柄了,例如: SetTimer(1,100,NULL); //1为ID号.100为时间 例如: // 添加两个定时器 SetTimer(1,500,NULL); SetTimer(2,1000,NULL); //停止定时器 KillTimer(1);//停止ID为1的定时器 KillTimer(2);//停止ID为2的定时器 定时器: void CTimerTestDlg::OnTimer(UINT nIDEvent) { switch (nIDEvent) { case 1: ///处理ID为1的定时器 ... break; case 2: ///处理ID为2的定时器 ... break; } 很多应用程序都需要使用定时器,以便定期检查状态,并重新绘图。学过VB的朋友知道,VB中的定时器是一个控件,我们只需要放置一个定时器控件到窗体之上,然后设置其属性,并在程序中处理该控件产生的定时事件就行了。不过在VC中并没有定时器控件,我们可以通过两种方法来使用定时器:一种是直接调用WIN32函数SetTimer(),另一种则是调用CWnd::SetTimer(),实际上这两种方法的本质是一样的,下面我们就来看看后者的使用方法。 l 设置和释放定时器 CWnd::SetTimer()的功能是给当前窗口设置一个定时器,该函数的原型为: UINT SetTimer( UINT nIDEvent, UINT nElapse, void (CALLBACK EXPORT* lpfnTimer)(HWND, UINT, UINT, DWORD) ); 由于一个窗口可以同时设置几个定时器,SetTimer()的第一个参数nIDEvent起标识不同定时器的作用,我们可以任意取值,只要不与其它定时器的标识重复就行了,以便将来能正确分辨出是哪个定时器发出的定时消息。nElapse表示每隔多少毫秒产生一次定时消息。lpfnTimer是一个函数指针,如果传递一个NULL指针,那么在产生定时中断时,Windows会把一条WM_TIMER消息放入到程序的消息队列之中,最终由CWnd对象相应的处理函数来处理该消息;如果传递了一个真正的函数指针,那么在产生定时中断时,Windows将直接调用该函数,由该函数来处理定时消息。这也就是说,我们可以使用两种方式来处理定时消息,它们有什么区别呢? 首先,WM_TIMER是一条低优先级的消息。如果消息队列之中还有其它高优先级的消息,那么WM_TIMER将被堵塞,直到最后才会被处理。其次,WM_TIMER被程序从消息队列中取回之后,还要经过一系列的过程才能被传递到它的处理函数。把这两点结合起来可以看出,在第一种方式下,定时消息有可能要拖延一段时间才会被处理,而第二种方式则不同,由于Windows将直接调用指定的函数,所以延迟时间要短得多。 由于Schedule对实时性要求并不苛刻,只要能精确到分钟左右就可以了,另外Schedule内部要进行的运算和处理也不多,所以心铃决定选用第一种方式,准备在视类中处理定时消息。在第十二讲给出的CScheduleView::OnInitialUpdate()的最后一行语句便是设置定时器的代码,现在我们可以把它前面的注释符号删掉了,这个定时器将使Schedule在每过20秒左右的时间得到一次重新检查各事件状态的机会。 定时器是一种有限的系统资源,如果多个程序都需要使用定时器,那么有可能会出现设置不成功的情况,因此我们应在调用SetTimer()函数时检查它的返回值,如果返回值是0,就表示系统中已经没有可用的定时器了。Schedule没有进行这步检查,心铃想请大家自己添上,并决定在出现这种情况时应该怎么办。 SetTimer()还有一个作用是它能够重新设置已分配的定时器的定时间隔,只要在nIDEvent中传递已分配的定时器的标识,那么该定时器的定时间隔就会改变成nElapse中指定的新值。 在程序退出时,我们应该将已分配的定时器释放掉。为此我们利用ClassWizard为视类添加一个处理WM_DESTROY消息的函数OnDestroy(),并编写以下代码: void CScheduleView::OnDestroy() { KillTimer(1); CListCtrl & lst=(this->GetListCtrl()); CImageList *pImageList=lst.GetImageList(LVSIL_SMALL); delete pImageList; CListView::OnDestroy(); } KillTimer()用于释放定时器,其参数应等于SetTimer()的nIDEvent。如果程序分配了多个定时器,那么应调用多次KillTimer()。OnDestroy()同时也把与CListView内部的List控件关联的图象列表释放了。 l 编写消息处理函数 使用定时器的第一种方式需要用到一个回调函数,该函数必须具有以下原型: void CALLBACK EXPORT TimerProc( HWND hWnd, UINT nMsg, UINT nIDEvent, DWORD dwTime ); 由于我们不使用这种方式,所以TimerProc各参数的含义请大家自己查阅MSDN库,下面我们来看看如何为第二种方式编写消息处理函数。 首先我们利用ClassWizard为视类添加一个处理WM_TIMER消息的函数OnTimer()。它只有一个参数nIDEvent,检查这个值便可以知道WM_TIMER消息是由哪个定时器发出的。下面便是OnTimer()的实现代码: void CScheduleView::OnTimer(UINT nIDEvent) { CTime tiNow=CTime::GetCurrentTime(); CTimeSpan tdif(0,0,15,0); CScheduleDoc * pDoc=GetDocument(); CString Message; struct ScheduleItem* pUp,*pDown; pUp=pDoc->m_pNearestTaskUp; pDown=pDoc->m_pNearestTaskDown; if(nIDEvent==1) { if( ( (pUp!=NULL) && (pUp->ti<tiNow-tdif) ) || ( (pDown!=NULL) && (pDown->ti<=tiNow+tdif) ) ) { pDoc->SearchNearestTask(); //更新事件条目的状态 pDoc->UpdateState(); pDoc->UpdateAllViews(NULL,0,NULL); //更新显示 if((pDown!=NULL) && ( pDown->ti <= (tiNow+tdif) )) { AfxGetMainWnd()->ShowWindow(SW_NORMAL); SetForegroundWindow(); //把窗口显示在前台 Message="请注意:\""; //准备提示信息 Message += pDown->des; Message += "\" 的时间很快就要到了!"; KillTimer(1); //必须先暂时中断定时器 AfxMessageBox(Message); SetTimer(1,20000,NULL); //重新设置定时器 } } } //CListView::OnTimer(nIDEvent); 将ClassWizard生成的这行代码注释掉 } 图17-1:报警的消息框 在上面的代码中,我们首先检查处于时间边界的两个事件条目,判断是否需要改变它们的状态。如果是的话,就重新搜索处于时间边界的两个新事件条目,更新链表中各事件的状态,并更新显示。对于刚从等待报警状态(状态2)进入报警状态(状态1)的事件,我们有必要以某种方式通知用户,提醒他已经到了应做某某事情的时间。上面的代码首先将程序主窗口以正常方式显示,然后把窗口设置成前台,这两条语句组合起来便可以把Schedule显示在前台,即便它当时处于最小化状态,上述语句也能将它的窗口弹出来。窗口显示出来后,我们接着再显示一个消息框(见图17-1),其中给出了某某事情的时间已到了的提示信息。 消息框本质上是一个非常简单的由系统控制的对话框。上一讲给出的代码中也用到了它,我们使用的函数是MFC类库包装过的AfxMessageBox(),它比直接使用WIN32函数MessageBox()要稍微简单一些,但缺点是不能设置消息的标题条。除了上述两个函数外,CWnd类也有一个MessageBox()成员函数,其功能与WIN32函数类似,大家以后可以根据需要选择使用。 大家是否注意到了,上面的代码在显示消息框之前调用了一次KillTimer(),从消息框返回后又调用了一次SetTimer(),为什么要这样做呢?这个问题实际上涉及到了WM_TIMER消息的一些内在特点。 设置了定时器后,程序的窗口处理函数将会定期收到一条WM_TIMER消息,如果OnTimer()内部的处理需要花费很长的时间,就有可能在还未退出它之前,程序又收到一条WM_TIMER消息。此时会出现两种情况:一种是OnTimer()的内部调用了消息框或对话框,当前正在等待用户输入。在这种情况下,当前CPU的控制权实际上掌握在Windows手中,而不在程序手中,因此Windows将再次调用OnTimer()(称为“重入”,重复进入的意思),这样可能会造成不良后果。例如将再次显示出一个消息框或对话框,或者由于后一次调用改变了全局变量,从而对前一次调用产生影响。另一种情况是OnTimer()内部正在进行大量的处理,但没有调用任何可能把控制权交还给Windows的函数,那么第二个WM_TIMER消息将被保留在程序的消息队列之中。如果因为OnTimer()迟迟不能返回,最后导致第三个、第四个甚至更多的WM_TIMER消息也到来了,此时后面来的WM_TIMER消息将会冲掉前面的WM_TIMER消息,也即消息队列中只保留最后一条WM_TIMER消息,其余的都被丢弃了,这与WM_PAINT消息是类似的。 总之,上面两种情况均说明了OnTimer()或其它处理定时消息的函数不适合完成耗时很长的处理,如果不得已要进行这种处理,那么应考虑设立一些保护措施,以防止出现异常现象。 Schedule属于第一种情况,于是心铃选择了在显示消息框之前先暂时中断定时器,从消息框返回之后再重置定时器的方法。除此之外,我们也可以不暂停定时器,但必须设置一个初始值为假的重入标志,每次进入OnTimer()时首先检查该标志是否为真。如果是真(表示本次调用是重入)则马上退出函数,如果是假则把该标志置为真,然后进行各种处理,最后在退出函数之前又把该标志置为假。这种方法可以有效地避免因重入而带来不良的后果。 l 时间精度问题 现在我们来讨论一下定时器的精度问题。SetTimer()设置的定时器被称为系统定时器,该函数的参数nElapse是以毫秒为单位,但这并不意味着系统定时器的最小精度可达1毫秒。我们知道,PC机有一块时钟芯片连接在可编程中断控制器的IRQ0上,并以每秒钟18.2次的速度产生中断,相当于55毫秒一次,这便是Windows 9x(不同的操作系统和硬件平台可能会有不同的最小精度)的系统定时器的最小精度。也就是说,即使我们为nElapse设置了小于55的值,定时消息实际到达的间隔也在55毫秒之上。另一方面,由于Windows是一个多任务操作系统,应用程序遵循消息驱动模式,定时消息到达程序的消息队列之后可能要经过一段时间后才会被处理,这样就为两条定时消息之间的间隔加上了随机性,也即下一条定时消息被处理的时间是无法确定的。 Schedule对时间的精度并不敏感,因此我们使用系统时钟便可完全满足要求,然而系统时钟对那些实时性要求较高的应用程序就不够了。例如以每秒25帧速度播放动画或视频数据的多媒体程序,如果使用定时器,那么最低的精度要求都是40毫秒,系统时钟显然达不到这一精度。为此,Windows的多媒体子系统提供了另外一种定时器——多媒体定时器(Multimedia Timer),在多数系统上,它都能达到1毫秒的精度,基本上能满足各种多媒体应用的要求。不过需要注意的是,当定时消息发生间隔小于10毫秒时,系统性能就会受到比较明显的影响,因此我们在使用多媒体定时器时应把间隔定为能满足要求的最大值,而不能去追求最小值 CDialog::OnTimer(nIDEvent); }
标签: messagebox vc it 分类: VC/MFC void CTestDlg::OnOK() { // TODO: 在此添加控件通知处理程序代码 SetTimer(NULL,2000,NULL); //设置一个定时器,2秒后触发 MessageBox(_T("aaaaa"),_T("B"),MB_YESNO); //跳出一个标题为B的窗口 } void CTestDlg::OnTimer(UINT_PTR nIDEvent) { // TODO: 在此添加消息处理程序代码和/或调用默认值 HWND hWnd=::FindWindow(NULL,_T("B")); //查找标题为B的窗口,返回窗口句柄 if (hWnd) //如果窗口句柄存在 { //::SendMessage(hWnd,WM_SYSCOMMAND,SC_CLOSE,NULL); //关闭这个窗口,如果Messagebox的对话框没有MB_YESNO或者MB_OKCANCEL这样类似的属性,这个也可以实现关闭 keybd_event(13,0,0,0); //模拟回车键按下 } KillTimer(nIDEvent); //关闭定时器 CDialog::OnTimer(nIDEvent);} 第二套方案 下面的程序从5种方法,关闭弹出的MessageBox对话框: 首先,创建定时器和弹出MessageBox: [cpp] view plain copy void CTestDlg::OnBnClickedButton1() { //启动一个定时器,用于自动触发关闭MessageBox对话框 SetTimer(1, 1000, NULL); //启动一个MessageBox对话框,并检查它的返回值 int ret = MessageBox("Hello", "MyMessageBox", MB_YESNO); if (ret == IDYES) { TRACE("按钮【YES】被按下\n"); } else if(ret == IDNO) { TRACE("按钮【NO】被按下\n"); } } 然后,在定时器回调中: [cpp] view plain copy void CTestDlg::OnTimer(UINT_PTR nIDEvent) { // TODO: 在此添加消息处理程序代码和/或调用默认值 if (nIDEvent == 1) { HANDLE hWnd = ::FindWindowEx(NULL, NULL, NULL, "MyMessageBox"); //HANDLE hWnd = ::GetForegroundWindow(); if (hWnd) { TRACE("发现了MyMessageBox窗口\n"); // // 第一种方法:直接发送 WM_CLOSE 消息。 // ::SendMessage((HWND)hWnd, WM_CLOSE, NULL, NULL); // // 第二种方法: // ::EndDialog((HWND)hWnd, IDNO); // // 第三种方法:模拟窗口消息点击了【IDNO】按钮 // HWND h = ::GetDlgItem((HWND)hWnd, IDNO); if(h) { TRACE("找到了IDNO控件窗口\n"); ::PostMessage((HWND)hWnd, WM_COMMAND, MAKEWPARAM(IDNO, BN_CLICKED), (LPARAM)h); } // // 第四种方法:模拟鼠标移到了【IDNO】按钮上点击 // HWND h = ::GetDlgItem((HWND)hWnd, IDNO); if(h) { TRACE("找到了IDNO控件窗口\n"); POINT pt; CRect rc; ::GetWindowRect(h, &rc); pt.x = rc.left+10; pt.y = rc.top+10; SetCursorPos(pt.x, pt.y);//鼠标移到了【IDNO】按钮上 //完成点击 mouse_event(MOUSEEVENTF_LEFTDOWN|MOUSEEVENTF_ABSOLUTE, 0, 0, 0, 0); mouse_event(MOUSEEVENTF_LEFTUP|MOUSEEVENTF_ABSOLUTE, 0, 0, 0, 0); } // // 第五种方法:模拟键盘回车关闭对话框 // keybd_event(VK_RETURN, 0, 0,0); keybd_event(VK_RETURN, 0, KEYEVENTF_KEYUP,0); } } CDialog::OnTimer(nIDEvent); }
因为只需在Windows上执行,先想到用MCI接口。试了一下,用mciSendCommand可以实现基本的播放wav文件的功能。但循环播放wav就麻烦了,必须向窗口传送MM_MCINOTIFY消息。 google了一下,才发现原来有更简单的方法——用sndPlaySound。一条语句sndPlaySound(filename, SND_ASYNC | SND_LOOP)就可以循环播放声音文件,完全满足我的要求。简单吧。 函数定义是:BOOL sndPlaySound(LPCSTR lpszSound, UINT fuSound); 其中,lpszSound一般是wav文件的文件名,fuSound是参数。常见的fuSound参数有: SND_ASYNC 异步播放,即程序不等播放结束就继续执行,播放背景声。 SND_SYNC 同步播放,即播放结束才继续执行 SND_LOOP 循环播放 SND_NODEFAULT 如果找不到指定文件,保持安静。如不指定此参数,则播放系统默认警告音。如没有默认警告音,则为失败。 执行成功返回TRUE,失败返回FALSE。 要停止播放只需再执行一遍lpszSound参数为NULL的sndPlaySound函数。 要求: 程序要加入Mmsystem.h,编译时链入Winmm.lib库。 限制: sndPlaySound只能播放wav文件。 wav文件在播放前将被装入内存,所以不能太大。 只能同时播放一个声音。后一个声音会关闭前一个声音。 函数PlaySound是sndPlaySound的增强版,支持更多声音类型和fuSound参数,并可以播放内存和资源中的声音。 或者增加以下: #include <Mmsystem.h> #pragma comment(lib, "Winmm.lib") PlaySound(...) 第一种方法是直接播出声音文件,相应的代码为: PlaySound("c:\win95\media\The Microsoft Sound.wav", NULL, SND_FILENAME | SND_ASYNC); 注意参数中的路径使用两个连续的反斜杠转义代表一个反斜杠。 第二种方法是把声音文件加入到资源中,然后从资源中播放声音。Visual C++支持WAVE型资源,用户在资源视图中单击鼠标右键并选择Import命令,然后在文件选择对话框中选择The Microsoft Sound.wav文件,则该文件就会被加入到WAVE资源中。假定声音资源的ID为IDR_STARTWIN,则下面的调用同样会输出启动声音: PlaySound((LPCTSTR)IDR_STARTWIN, AfxGetInstanceHandle(), SND_RESOURCE | SND_ASYNC); 第三种方法是用PlaySound播放系统声音,Windows启动的声音是由SystemStart定义的系统声音,因此可以用下面的方法播放启动声音: PlaySound("SystemStart",NULL,SND_ALIAS|SND_ASYNC); sndPlaySound不能直接播放声音资源。要用该函数播放WAVE文件,可按下面的方式调用: sndPlaySound(“MYSOUND.WAV”,SND_ASYNC); 360Doc 方案 一.播放声音文件的简单方法 在VC++ 中的多媒体动态连接库中提供了一组与音频设备有关的函数。利用这些函数可以方便地播放声音。最简单的播放声音方法就是直接调用VC++中提供的声音播放函数BOOL sndPlaySound ( LPCSTR lpszSound,UINT fuSound ); 或BOOL PlaySound( LPCSTR lpszSound, HMODULE hmod, DWORD fuSound );其中参数lpszSound是需要播放声音的.WAV文件的路径和文件名, hmod在这里为NULL,fuSound是播放声音的标志,具体说明请参考VC++中的帮助。例如播放C:\sound\music.wav可以用sndPlaySound ("c:\\sound\\music.wav",SND_ASYNC);或PlaySound("c:\\sound\\music.wav",NULL, SND_ASYNC|SND_NODEFAULT );假如没有找到music.wav文件,第一种格式将播放系统默认的声音,第二种格式不会播放系统默认的声音。 二.将声音文件加入到程序中 在VC++的程序设计中,可以利用各种标准的资源,如位图,菜单,对话框等。同时VC++也答应用户自定义资源,因此我们可以将声音文件作为用户自定义资源加入程序资源文件中,经过编译连接生成EXE文件,实现无.WAV文件的声音播放。 要实现作为资源的声音文件的播放,首先要在资源管理器中加入待播放的声音文件(实现过程并不复杂,这里不在叙述)。假设生成的声音文件资源标识符为IDR_WAVE1。在播放时只需要调用下面的语句: PlaySound(MAKEINTRESOURCE(IDR_WAVE1),AfxGetResourceHandle(), SND_ASYNC|SND_RESOURCE|SND_NODEFAULT|SND_LOOP); 其中MAKEINTRESOURCE()宏将整数资源标识符转变为字符串,AfxGetResourceHandle()函数返回包含资源的模块句柄, SND_RESOURCE是必须的标志。 作为资源的声音文件的第二种播放方法是把资源读入内存后作为内存数据播放。具体步骤入下: 1.获得包含资源的模块句柄: HMODULE hmod=AfxGetResourceHandle(); 2.检索资源块信息: HRSRC hSndResource=FindResource(hmod,MAKEINTRESOURCE(IDR_WAVE1),_T("WAVE")); 3. 装载资源数据并加锁: HGLOBAL hGlobalMem=LoadResource(hmod,hSndResource); LPCTSTR lpMemSound=(LPCSTR)LockResource(hGlobalMem); 4.播放声音文件: sndPlaySound(lpMemSound,SND_MEMORY)); 5.释放资源句柄: FreeResource(hGlobalMem); 第三种方法是用PlaySound播放系统声音,Windows启动的声音是由SystemStart定义的系统声音,因此可以用下面的方法播放启动声音: PlaySound("SystemStart",NULL,SND_ALIAS|SND_ASYNC); 函数sndPlaySound的功能与PlaySound类似,但少了一个参数。函数的声明为: BOOL sndPlaySound(LPCSTR lpszSound, UINT fuSound); 除了不能指定资源名字外,参数lpszSound与PlaySound的是一样的。参数fuSound是如何播放声音的标志,可以是SND_ASYNC、SND_LOOP、SND_MEMORY、SND_NODEFAULT、SND_NOSTOP和SND_SYNC的组合,这些标志的含义与PlaySound的一样。 可以看出,sndPlaySound不能直接播放声音资源。要用该函数播放WAVE文件,可按下面的方式调用 ================================================================================= 一、PlaySound函数的声明为: BOOL PlaySound(LPCSTR pszSound,HMODULE hmod,DWORD fdwSound); 参数说明: pszSound:是指定了要播放声音的字符串,该参数可以是WAVE文件的名字,或是WAVE资源的名字,或是内存中声音数据的指针,或是在系统注册表WIN.INI中定义的系统事件声音.假如该参数为NULL则停止正在播放的声音. hmod:是应用程序的实例句柄,当播放WAV资源时要用到该参数,否则它必须为NULL. fdwSound: 是标志的组合,如下表所示。若成功则函数返回TRUE,否则返回FALSE。 二、播放标志以及含义: SND_APPLICATION 用应用程序指定的关联来播放声音。 SND_ALIAS pszSound参数指定了注册表或WIN.INI中的系统事件的别名。 SND_ALIAS_ID pszSound参数指定了预定义的声音标识符。 SND_ASYNC 用异步方式播放声音,PlaySound函数在开始播放后立即返回。 SND_FILENAME pszSound参数指定了WAVE文件名。 SND_LOOP 反复播放声音,必须与SND_ASYNC标志一块使用。 SND_MEMORY 播放载入到内存中的声音,此时pszSound是指向声音数据的指针。 SND_NODEFAULT 不播放缺省声音,若无此标志,则PlaySound在没找到声音时会播放缺省声音。 SND_NOSTOP PlaySound不打断原来的声音播出并立即返回FALSE。 SND_NOWAIT 假如驱动程序正忙则函数就不播放声音并立即返回。 SND_PURGE 停止所有与调用任务有关的声音。若参数pszSound为NULL,就停止所有的声音,否则,停止pszSound指定的声音。 SND_RESOURCE pszSound参数是WAVE资源的标识符,这时要用到hmod参数。 SND_SYNC 同步播放声音,在播放完后PlaySound函数才返回。 三、函数使用方法及代码: 注重:在使用函数前要加入: #include "mmsystem.h"//导入声音头文件 #pragma comment(lib,"winmm.lib")//导入声音头文件库 1、直接播出声音文件: PlaySound("c:\\win95\\media\\The Microsoft Sound.wav", NULL, SND_FILENAME | SND_ASYNC); 注重:参数中的路径使用两个连续的反斜杠转义代表一个反斜杠。 2、把声音文件加入到资源中,然后从资源中播放声音: Visual C++支持WAVE型资源,用户在资源视图中单击鼠标右键并选择Import命令,然后在文件选择对话框中选择The Microsoft Sound.wav文件,则该文件就会被加入到WAVE资源中。假定声音资源的ID为IDR_STARTWIN,则下面的调用同样会输出启动声音: PlaySound((LPCTSTR)IDR_STARTWIN, AfxGetInstanceHandle(), SND_RESOURCE | SND_ASYNC); 或: PlaySound(MAKEINTRESOURCE(IDR_WAVE2),AfxGetResourceHandle(),SND_ASYNC|SND_RESOURCE|SND_NODEFAULT|SND_LOOP);//将声音文件写入到程序中 3、用PlaySound播放系统声音: Windows启动的声音是由SystemStart定义的系统声音,因此可以用下面的方法播放启动声音: PlaySound("SystemStart",NULL,SND_ALIAS|SND_ASYNC); sndPlaySound函数的声明为: BOOL sndPlaySound(LPCSTR lpszSound, UINT fuSound); 用法: 除了不能指定资源名字外,参数lpszSound与PlaySound的是一样的。参数fuSound是如何播放声音的标志,可以是SND_ASYNC、SND_LOOP、SND_MEMORY、SND_NODEFAULT、SND_NOSTOP和SND_SYNC的组合,这些标志的含义与PlaySound的一样。 可以看出,sndPlaySound不能直接播放声音资源。要用该函数播放WAVE文件,可按下面的方式调用: sndPlaySound("MYSOUND.WAV",SND_ASYNC);
本文将介绍下c++中的messagebox()的使用方法:常用属性/按钮的形式/返回值等等,感兴趣的朋友可以了解下,希望本文可以帮助到你 1.MessageBox("这是一个最简单的消息框!"); 2.MessageBox("这是一个有标题的消息框!","标题"); 3.MessageBox("这是一个确定 取消的消息框!","标题", MB_OKCANCEL ); 4.MessageBox("这是一个警告的消息框!","标题", MB_ICONEXCLAMATION ); 5.MessageBox("这是一个两种属性的消息框!","标题", MB_ICONEXCLAMATION|MB_OKCANCEL ); 6.if(MessageBox("一种常用的应用","标题",MB_ICONEXCLAMATION|MB_OKCANCEL)==IDCANCEL) return; 附其它常用属性 系统默认图标,可在消息框上显示 X错误 MB_ICONHAND, MB_ICONSTOP, and MB_ICONERROR ?询问 MB_ICONQUESTION !警告 MB_ICONEXCLAMATION and MB_ICONWARNING i信息 MB_ICONASTERISK and MB_ICONINFORMATION 按钮的形式 MB_OK 默认 MB_OKCANCEL 确定取消 MB_YESNO 是否 MB_YESNOCANCEL 是否取消 返回值 IDCANCEL 取消被选 IDNO 否被选 IDOK 确定被选 IDYES 是被选 补充: 以上消息框的用法是在CWnd的子类中的应用,如果不是,则要MessageBox(NULL,"ddd","ddd",MB_OK); 或MessageBox(hWnd,"ddd","ddd",MB_OK); hWnd为某窗口的句柄,或者直接用AfxMessageBox。 这里所列出的属性只是一些常用属性,在MSDN中还有更多的属性 Visual C++2005 的兼容问题 error C2664: “CWnd::MessageBoxW”: 不能将参数1 从“const char [3]”转换为“LPCTSTR” 错误 2 error C2664: “inet_addr”: 不能将参数 1 从“_TCHAR *”转换为“const char *” d:\vc program\clienttcp\clienttcp\clienttcp.cpp 29 原因:2005默认的"使用 Unicode 字符集",解决:在你建立的解决方案的工程文件的属性看到选"配置属性"的"字符集"请选择"使用多字节字符集"就可以了。 2005打开6.0的文件时候不会出现的这样的错误,是因为编译环境在转换过程中进行了转换
// Select_sort.c.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include "iostream" using namespace std; void select_sort(int a[],int n) {int temp;for(int i=0;i<n-1;++i){int j=i;for(int k=i+1;k<n;++k)if(a[k]<a[j])j=k;if(j!=i){temp=a[j];a[j]=a[i];a[i]=temp;}} } void bubble_sort(int a[],int n) {bool change=true;for(int i=n-1;i>1&&change;--i){change=false;for(int j=0;j<i;++j){if(a[j]>a[j+1]){int temp;temp=a[j];a[j]=a[j+1];a[j+1]=temp;change=true;}}} } int _tmain(int argc, _TCHAR* argv[]) {int b[]={10,2,2,15,9,88,7}; //select_sort(b,7); //for(int out=0;out<7;++out) // cout<<b[out]<<endl; //system("pause"); bubble_sort(b,7); for(int out=0;out<7;++out)cout<<b[out]<<endl; system("pause");return 0; }
一、数据结构 1.教材:《数据结构》严蔚敏 清华大学出版社 清华大学严蔚敏的这本数据结构的教材是国内数据结构教材的权威。也是国内使用最广,其广度远远超越其他同类教材,计算机考研专业课命题必定以它为蓝本。这一本数据结构是2007年的最新版本,完全适合任何学校的考研数据结构的复习之用,是数据结构学习最权威的教材。 2.辅导书:《算法与数据结构考研试题精析(第二版)》机械工业出版社 网上广为流传的数据结构1800题相信只要是计算机考研的同学无人不知无人不晓。其实1800题是2001年推出来的,当时编者把电子版免费分享给大家,却很少有人知道它也有纸质版本就是《算法与数据结构考研试题精析》。第二版是2007年最新出版的,对里面的题目进行了大量的更新,去掉了一些比较过时和重复的题,加上了很多名校最近几年的考研真题,总共大约1650题左右。真题就是训练的最好武器,相信当你复习完这本数据结构辅导书后,任何关于数据结构的考题都是小菜一碟。 二、计算机组成原理 1.教材:《计算机组成原理》唐朔飞 高等教育出版社 《计算机组成原理》白中英 科学出版社 这两本教材都是普通高等教育十一五国家级规划教材,其权威性不言而喻,在国内是使用最广的两本教材,而前者应该略胜一筹。而且两位老师说教学的计算机组成原理课程都是国家级精品课程,网上甚至还有他们的讲课视频可以下载,再配合教材的使用,这样可以更加增强学习的效率。 2.辅导书:《计算机组成原理考研指导》徐爱萍 清华大学出版社 《计算机组成原理--学习指导与习题解答》唐朔飞 高等教育出版社 清华大学的这套辅导教材在广大的考生中有着极为优秀的口碑,特别是系列中的李春葆《数据结构考研辅导》在数据结构考研辅导资料中占据着数一数二的地位。这本辅导书通俗易懂,重点突出,特别适合于考研复习,特别是武汉大学以前的专业试题就完全以这本书为蓝本,甚至直接考上面的原题。唐朔飞的题集上面的题型也比较适合于考研,和它的配套教材一样,是一本不可多得的好书。 三、操作系统 1.教材:《计算机操作系统(修订版)》汤子瀛 西安电子科技大学出版社 毫无疑问这本教材是国内操作系统教材的权威,使用度很广,以往一般考操作系统的学校基本都以此本教材作为指定教材。在国内目前还没有其他同类教材的使用广度和其相媲美,所以考研操作系统的复习应以这本书为准,相信操作系统统考试题的出题肯定也会以这本教材为蓝本。 2.辅导书:《操作系统考研辅导教程(计算机专业研究生入学考试全真题解) 》电子科技大学出版社 《操作系统考研指导》清华大学出版社 我把《操作系统考研辅导教程》摆在前面是因为这本书主要是精选名校历年操作系统考研真题,真题的权威行和参考性都很大,真题是提高解答真题能力的最好武器。之所以把后者也放在这里,是因为这一系列的教材确实很有名,之前这些书在网上都可以免费下载电子版的,但电子版毕竟不是很清楚,而且天天盯着电脑看不仅很费劲,而且也不适合考研的学校,不是真正好好的复习考研,纸质的辅导书是必备的。 四、计算机网络 1.教材:《计算机网络(第五版)》谢希仁 电子工业出版社 在国外翻译过来的教材中,有一些教材比较不错,比如《计算机网络--自顶向下方法与Internet特色》,但是这些教材都不可能作为计算机统考的出题蓝本。一是因为他们是国外教材,二是因为他们的使用度不够广,三是考研也要支持国货嘛^_^。谢希仁的《计算机网络》是目前国内使用最广的计算机网络教材,也是国人所编写公认最好的一本,这本教材必将称为09年计算机统考的出题蓝本无疑。第五版是2008年最新出版的,相比以前的版本变化也不是太大,做了一些扩充。 2.辅导书:《计算机网络知识要点与习题解析》哈尔滨工程大学出版社 这本书是谢希仁《计算机网络》的配套习题集,封面上都是第四版教材的图案。之前各高校考计算机网络的很少,目前市场上还没有计算机网络的考研辅导书,所以这本配套习题集应该就是最好的选择了。可惜这本书可能会比较难购买到,因为以前出版的数量比较少。但是相信细心的你和渴望考名校研的你一定能够在淘宝上买到这本书。计算机网络题应该相对是最容易的,所以先看看教材,然后再看看习题集,对于网络考高分一定没有任何问题。
微软的Windown Server 2003尽管它是对应服务器的,但仍然有不少朋友蠢蠢欲动,欲升之而后快。可是,安装之后你就会发现麻烦多多,这样功能没有,那样功能打不开,甚至连听歌,玩游戏也成问题了。 其实,Server 2003在XP的基础强化了安全性和稳定性,不得不关闭了一些工作站系统。我们是把Win 2003当工作站用,当然要重新打开这些服务,让它重获新生啦。那些用不着的服务器功能,也一并减肥去掉吧。1、关闭服务器向导 装完win 2003,你会发现“管理您的服务器”出现,把左下角的“登录时不要显示该页”勾上。如果你在开机时找不到,可以进入控制面板-管理工具-管理你的服务器中找到2、用户帐号登录2003使用服务器的ctrl+alt+del登录方式,没有Win XP的登录欢迎,还是建立一个帐号登录有个性化。在开始-运行-输入“lusrmgr.msc”-本地用户和组。在用户中右键点击建立“新用户”,输入账号信息并建立账号。 接着把新建的账号添加到管理员组,右键点击你新建的用户,选择属性-隶属于-添加Add..-高级-现在查找,双击管理员,得到Administrator相近的权限。3、关闭事件跟踪程序 服务器必不可少的功能之一,开始-运行-输入“gpedit.msc”,打开组策略编辑器,在右边的计算机配置-管理模板-系统,双击“显示关闭事件跟踪程序”,设置为己禁用,以后关机画面就和Win2000相同了4、程序的动态分配 右键点击“我的电脑”图标,进入属性-高级-性能-设置-高级,选择分配处理器和内存资源都选择为“程序”使用。5、关闭错误报告 右键点击“我的电脑”图标,进入属性-高级-错误报告,选择“禁用错误报告”,并且去掉“但在发生严重错误时通知我”的勾。6、隐藏文件Win2003默认是显示所有文件夹的,可以设法来隐藏:在资源管理器或我的电脑上,选择工具-文件夹选项-查看,不显示隐藏文件和文件夹。7、关闭Internet Explorer的增强安全配置 这个新组件会把IE安全设置到最高,跟本没法进行正常的浏览行为,在它弹出的时间,先选中“以后不要显示这个信息”,当然,最省事的方法是在控制面板--添加程序--添加或删除Windows组件中卸载增强安全配置。 然后,找开IE,在工具-internet选项-安全,点击默认级别,设置为中级即可。
备份后,清理很干净,有效提升系统速度,感觉这帮弄底层的兄弟!
127.0.0.1 kdtsplatform.kingdee.com 127.0.0.1 patch.cmcloud.cn 127.0.0.1 api.cmcloud.cn 127.0.0.1 cmcloud.cn 127.0.0.1 service.kingdee.com 127.0.0.1 sso.cmcloud.cn 127.0.0.1 kingdee.com 127.0.0.1 www.kingdee.com 127.0.0.1 cssotest.kingdee.com 127.0.0.1 downloads.cmcloud.cn 127.0.0.1 images.cmcloud.cn 127.0.0.1 reg.kingdee.com 有啥用,你懂的。 有好事者弄了个cmd文件,如下: @echo off if exist %ComSpec% goto nt else goto 9x :9x set etc=%windir%\ set hosts=%windir%\hosts goto menu :nt if exist %windir%\system32\cmd.exe goto winnt32 if exist %windir%\system64\cmd.exe goto winnt64 :winnt32 set etc=%windir%\system32\drivers\etc set hosts=%windir%\system32\drivers\etc\hosts goto menu :winnt64 set etc=%windir%\system64\drivers\etc set hosts=%windir%\system64\drivers\etc\hosts goto menu :menu attrib -s -a -r %hosts% find "202.104.120.79 kdtsplatform.kingdee.com" %hosts% > nul if %errorlevel% == 1 ( @echo.>>%hosts% @echo 202.104.120.79 kdtsplatform.kingdee.com >>%hosts% @echo 127.0.0.1 kdtsplatform.kingdee.com >>%hosts% @echo 127.0.0.1 patch.cmcloud.cn >>%hosts% @echo 127.0.0.1 api.cmcloud.cn >>%hosts% @echo 127.0.0.1 cmcloud.cn >>%hosts% @echo 127.0.0.1 service.kingdee.com >>%hosts% @echo 127.0.0.1 sso.cmcloud.cn >>%hosts% @echo 127.0.0.1 kingdee.com >>%hosts% @echo 127.0.0.1 www.kingdee.com >>%hosts% @echo 127.0.0.1 cssotest.kingdee.com >>%hosts% @echo 127.0.0.1 downloads.cmcloud.cn >>%hosts% @echo 127.0.0.1 images.cmcloud.cn >>%hosts% @echo 127.0.0.1 reg.kingdee.com >>%hosts% ) attrib -s -a +r %hosts% echo 写入成功!按任意键退出,并要重启电脑才生效.... pause exit
1.设置优先级,这个肯定要,特别是打压视频,提高IM软件的优先级。 2.宽带限制,针对的还是视频和P2P。
都说是打印机驱动任务啥的,其实应该是控件和PDF打印机导致的,最多算上一个不正常的其他打印机导致的,所以除常规方法外,还需要干掉不用的所有打印机,只保留正常的那几个,虚拟打印机好像常常会搞出一些莫名其妙的问题来。
理财之星系列中的进锁存,数据库损坏,分析之,access的mdb数据库,打开,另存为97格式,用坏表导出内容到新转的表,其中注册序列号在reg表中,导完后,运行需要重新注册,根据提示注册,然后装运行环境crystal report,也就是水晶报表了了,发数据库给客户,搞完收工,不对,没收到银子呀!
1.深信服Dlan在本地保存了配置信息,如用户名和密码,只是其密码加了密,算法不是常规MD5,但至少本地可以提取。位置在: config目录下 2.精诚图形控件是32的,在64位win7上必须保障32位版本可以用,今天见到的案例是直接卸载win7中的IE9内核,改用360,然后用兼容模式。 3.晶奇新版本需要dot4.0,且保存了新安装地址及发票配置信息予本地。
好像技术根本没有好好考虑一下这一块的算法,都要限制,无论tcp还是udp,否则整个盘都要被他给上传上去,真晕,还搞出个误会来,真吞血!
Discuz迁移是一件较为麻烦的事情 网上大多的迁移教程都是利用备份功能进行操作的,其实这种操作并不能保证迁移后完全正常工作 本文将介绍直接转移数据库和文件的迁移方法 导出数据库 迁移的首要部分就是导出数据库 不论你是VPS、独立服务器还是虚拟主机一般都有phpmyadmin管理工具 进入phpmyadmin,选择discuz安装到的数据库,并导出数据表 按照同样的方法,到新主机的数据库再导入即可 转移程序文件 转移程序文件没有特别的要求 只需要打包后重新上传到新虚拟主机即可 修改配置文件 1、进入config/config_global.php,修改下面的项目 2、进入config/config_ucenter.php,修改下面的项目 3、进入uc_server/data/config.inc.php,修改下面的项目 修改完毕后,进入论坛,迁移便完成啦! Discuz迁移是一件较为麻烦的事情 网上大多的迁移教程都是利用备份功能进行操作的,其实这种操作并不能保证迁移后完全正常工作 本文将介绍直接转移数据库和文件的迁移方法 导出数据库 迁移的首要部分就是导出数据库 不论你是VPS、独立服务器还是虚拟主机一般都有phpmyadmin管理工具 进入phpmyadmin,选择discuz安装到的数据库,并导出数据表 按照同样的方法,到新主机的数据库再导入即可 转移程序文件 转移程序文件没有特别的要求 只需要打包后重新上传到新虚拟主机即可 修改配置文件 1、进入config/config_global.php,修改下面的项目 2、进入config/config_ucenter.php,修改下面的项目 3、进入uc_server/data/config.inc.php,修改下面的项目 修改完毕后,进入论坛,迁移便完成啦!
法一、(已验证成功过) 打开注册表编辑器 (regedt 32)。 浏览 HKYE_LOCAL_MACHINE/System/CurrentControlSet/Control/Session Manager/ 中 SafeDLLSearchMode DWORD 值并将值从 1 更改为 0。 如果将 DWORD 值不存在, 创建它。 重新启动 SQLServer 安装并继续以安装 SQLServer。 应用最新 SQL ServicePack 然后重新启动服务器。 方法二、(未测试过) 一些在win2003 下安装sql server 2000 数据库的朋友或许会遇到 “ 无法验证产品密钥” 而安装失败的经历。并不是win2003 不支持 sql server2000 ,要想安装成功的话,只要在cd-key框中输入你本机系统的产品密钥。如果你不知道本机系统的产品密钥,可以去下载 EVEREST_Corporate.exe,打开后点击“操作系统”,有关你本机系统的信息就展现出来,其中有一项就 “产品密钥”,将这个序列号输入到cd-key框中就可以继续安装了,过程和在win2000,winxp中一样。 假如遇到程序挂起,要重启计算机,可以在注册表中如下设置: [HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Session Manager] "PendingFileRenameOperations"=""
有两种方法: 第一步:使用SQL语句查询 select @@version 查询结果如下: Microsoft SQL Server 2000 - 8.00.2039 (Intel X86) May 3 2005 23:18:38 Copyright (c) 1988-2003 Microsoft Corporation Personal Edition on Windows NT 5.1 (Build 2600: Service Pack 2) 其实在C:\Program Files\Microsoft SQL Server\MSSQL\Binn\sqlservr.exe 点击鼠标右键查看版本也能得到,不过信息比较简单而已。8.00.2039就代表安装的SQL Server的版本了。对应以下的表格,可以查出来具体对应的补丁版本。8.00.2039表示安装了SP4,8.00.760表示安装了SP3。 第二步:对照表格查找具体的补丁版本 SQL Server Versions @Version SQL Server Version Released 6.50.201 SQL Server 6.5 RTM 6.50.213 SQL Server 6.5 with Service Pack 1 6.50.240 SQL Server 6.5 with Service Pack 2 6.50.258 SQL Server 6.5 with Service Pack 3 6.50.281 SQL Server 6.5 with Service Pack 4 6.50.415 SQL Server 6.5 with Service Pack 5 6.50.416 SQL Server 6.5 with Service Pack 5a 7.00.623 SQL Server 7.0 / MSDE 1.0 RTM 7.00.699 SQL Server 7.0 SP1 July 1999 7.00.842 SQL Server 7.0 SP2 March 20th, 2000 7.00.961 SQL Server 7.0 SP3 December 15th, 2000 7.00.1063 SQL Server 7.0 SP4 8.00.194 SQL Server 2000 RTM 8.00.384 SQL Server 2000 SP1 8.00.534 SQL Server 2000 SP2 November 30th, 2001 8.00.760 SQL Server 2000 SP3 8.00.2039 SQL Server 2000 SP4 分类: sql
<?php /*default 默认访问路由routes 自定义路由 */ return array ( 'default' => array('m' => 'go','c' => 'index','a' => 'init'), 'routes' => array( 'from(:any)'=> "mobile/mobile/",'login' => "member/user/login",'login/(:any)' => "member/user/login/$1",'admin' => "admin/user/login",'uname/(:any)' => 'member/us/uname/$1','userbuy/(:any)' => 'member/us/userbuy/$1','userraffle/(:any)' => 'member/us/userraffle/$1','userpost/(:any)' => 'member/us/userpost/$1', 'register/(:any)' => 'member/user/register/$1','register' => 'member/user/register','goods/(:any)' => 'go/index/item/$1','dataserver/(:any)' => 'go/index/dataserver/$1','goods_list/(:any)' => 'go/index/glist/$1','goods_list' => 'go/index/glist','goods_lottery' => 'go/index/lottery','goods_lottery/(:any)' => 'go/index/lottery/$1','help/(:any)' => 'go/article/show/$1','single/(:any)' => 'go/article/single/$1','link' => 'link/link/init/$1','s_tag/(:any)' => 'search/index/tag/$1','buyrecord' => 'go/databuyrecord/buyrecord','buyrecordbai' => 'go/databuyrecord/buyrecordbai','group_qq' => 'go/qq_qun','group' => 'group/group/init','group/show/(:any)' => 'group/group/show/$1','group/nei/(:any)' => 'group/group/nei/$1', ) ); ?> 这代码写的,唉,好吧,能用就凑合用吧,无法是一个进站规则问题。
1.不能连接外网 2.不能改变默认内网设置 这个问题真是影响深远。
织梦后台登陆地位竟然放了个广告,这么个严肃的地方能放广告吗?当然不能,怎么办?去了他。位置在admin下的login.php 代码如下位置,注释掉 /* if ($dopost=='showad') { include('templets/login_ad.htm'); exit; }*/ 世界清净了。
漏动来源:阿里云云盾 漏动名称:Didcuz memcache+ssrf GETSHELL漏洞 漏动描述:Discuz存在SSRF漏洞,在配置了memcache的情况下,攻击者可以利用ssrf通过memcache中转,向磁盘上写入WEBSHELL恶意代码,从而造成数据库泄漏; 漏动文件:bbs/src/source/function/function_core.php 修复方法: 查找位置: function output_replace($content) { //.... // 此处添加内容 if (preg_match("(/|#|\+|%).*(/|#|\+|%)e", $_G['setting']['output']['preg']['search']) !== FALSE) { die("request error"); } $content = preg_replace($_G['setting']['output']['preg']['search'], $_G['setting']['output']['preg']['replace'], $content); } //豆丁上看到的另一套方案 很多同学肯定都收到阿里云提示discuz memcache+ssrf GETSHELL漏洞的相关说明,但购买阿里云云盾安骑士最少需要支付100块钱,下面我就在站帮网给大家分享下如何来解决Discuz memcache+ssrf GETSHELL漏洞的问题。该漏洞描述:discuz存在SSRF漏洞,在配置了memcache的情况下,攻击者可以利用ssrf通过memcache中转,向磁盘上写入WEBSHELL恶意代码,从而造成数据库泄漏官方发布相关说明:及时更新Discuz! X3.2 正式版 2016-06-01补丁更新(含安全更新)阿里云的安全提示忽略也是可以。解决方法一:利用云盾安骑士修复/source/function/function_core.php该文件在1089行加入了一段代码,如下面的代码,可自行添加。if (preg_match("(/|#|\+|%).*(/|#|\+|%)e", $_G['setting']['output']['preg']['search']) !== FALSE) { die("request error"); } $content = preg_replace($_G['setting']['output']['preg']['search'], $_G['setting']['output']['preg']['replace'], $content);使用方法,下载文件解压后,上传覆盖/source/function/function_core.php,后台更新缓存即可。文件下载: gbk_function_core.rar (18.19 KB, 下载次数: 0) utf_8_function_core.rar (18.22 KB, 下载次数: 0)解决方法二: 停止使用或卸载memcacheWindows下的Memcache卸载方法:memcached -d stopmemcached -d removesc delete "Memcached Server"linux memcached 卸载方法:1、首先查找你的memcached所在目录,可用如下命令查找名为memcached的文件夹find / -name memcached2、结束memcached进程killall memcached3、删除memcached目录及文件rm -rf /www/wdlinux/memcachedrm -rf /www/wdlinux/init.d/memcached4、关闭memcached开机启动chkconfig memcached off5、把memcached移出开机启动chkconfig --del memcached
#故障及原因 1)迁移到新站会有机会报。 2)更新缓存报1146错误 Table ‘common_member_archive’ doesn’t exist,DXC 某版本采集器造成。 #处理过程: 出于安全考虑,Discuz! 后台默认情况下禁止SQL语句直接执行,所以修改让他支持,修改方法如下: A.把config/config_global.php当中的$_config[admincp][runquery]设置修改为1。 B。由于用户存档表在Discuz! X2.5安装的时候不会自动生成,需要用户手动执行用户表优化。后台——站长——数据库,升级,执行 DELETE FROM `pre_common_setting` WHERE `skey` = 'membersplit'; 确认以上操作无误后在工具--更新缓存。
其实可以叫是一个插件引起的血案,因为杀人的心都有了,事件起因是服务器http状态监控报警,说断网,一看还是真是占用资源极高,重启一下服务,当时可以,一会又不行,环境是phpstudy2014,到官网一看,有2016,一想可能就是这个版本的问题,然后升级,麻烦正式开始。 根据作者的说明,停止原来的服务,直接安装2016版,装完后,恢复数据库密码,导入站点列表,结果故障扩大,静态的不出来,动态的也不显示,然后就备份,找资料,折腾,备份,找资料,折腾,一直搞到两点,好像可以了,休息一下,大概三点左右睡着了。 六点直接醒了,然后继续,这回直接切回到IIS了,因为以前做网维时曾经吃过这个亏,当时用的是信佑,行家都知道,要发挥最大性能就要上Linux版,结果后来出故障,一直找不到,最终丢了这个客户,后来学乖了,改用Win平台的软件。同样的想法,就决定切回来。然后一直这么切到中午,以为搞定了,吃个中饭。睡一会,醒来,发现故障依旧,头都大了,想来想去,好像最近只装了一个采集DXC插件,而且还是那啥版本的,果断关闭,好了,天下安静,所有故障消失,顺带还降了一下mysql的内存占用问题。 MySQL 5.6内存占用过高解决方案 距离MySQL 5.6正式发布已经有比较长的时间了,目前Oracle官网上的最新GA版本MySQL server也为5.6。但reizhi在安装配置后却发现其内存占用居高不下,无论如何调整cache甚至禁用InnoDB都不能解决。由于VPS仅有1GB内存,在开启常用的Web服务之后,无力再为MySQL提供400MB以上的物理内存。 通过网络爬文,MySQL 5.6相比于前代GA版本性能提升显著,但默认缓存设置对于小型站点并不合理。通过修改my.ini文件中的performance_schema_max_table_instances参数,能够有效降低内存占用。 默认my.ini文件位置:C:\Documents and Settings\All Users\Application Data\MySQL\MySQL Server 5.6 修改参数: performance_schema_max_table_instances=400 table_definition_cache=400 table_open_cache=256 保存之后重新启动MySQL服务,其内存占用即可从400MB以上降低至40MB左右。
有人提出许多方案,百度经验的最不靠谱,然后官方给的方案, 在\source\function\function_discuzcode.php 将 if(defined('IN_MOBILE') && !defined('TPL_DEFAULT') && !defined('IN_MOBILE_API')) { 改成 if($_G[setting][mobile][mobilesimpletype]) { 参考: http://www.zhugao.net/today/2015/7318.html http://www.discuz.net/thread-3527087-1-1.html 模板巴士给出了一个不修改代码的方式,没测试 :