﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>IT博客-寻梦岁月-随笔分类-C/C++/VC</title><link>http://www.cnitblog.com/ffan/category/475.html</link><description>寻梦的岁月不言辛苦几许,不问收获几多…</description><language>zh-cn</language><lastBuildDate>Mon, 26 Sep 2011 21:22:04 GMT</lastBuildDate><pubDate>Mon, 26 Sep 2011 21:22:04 GMT</pubDate><ttl>60</ttl><item><title>VC中窗口ID，句柄，指针三者相互转换函数</title><link>http://www.cnitblog.com/ffan/archive/2007/11/29/37010.html</link><dc:creator>ffan</dc:creator><author>ffan</author><pubDate>Thu, 29 Nov 2007 00:47:00 GMT</pubDate><guid>http://www.cnitblog.com/ffan/archive/2007/11/29/37010.html</guid><wfw:comment>http://www.cnitblog.com/ffan/comments/37010.html</wfw:comment><comments>http://www.cnitblog.com/ffan/archive/2007/11/29/37010.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/ffan/comments/commentRss/37010.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/ffan/services/trackbacks/37010.html</trackback:ping><description><![CDATA[<p>ID--HANDLE--HWND三者之间的互相转换<br>id-&gt;句柄、、、、、hWnd = ::GetDlgItem(hParentWnd,id);<br>id-&gt;指针、、、、、CWnd::GetDlgItem();<br>句柄-&gt;id、、、、、id = GetWindowLong(hWnd,GWL_ID);<br>句柄-&gt;指针、、、、CWnd *pWnd=CWnd::FromHandle(hWnd);<br>指针-&gt;ID、、、、、id = GetWindowLong(pWnd-&gt;GetSafeHwnd,GWL_ID);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GetDlgCtrlID();<br>指针-&gt;句柄、、、、hWnd=cWnd.GetSafeHandle() or mywnd-&gt;m_hWnd; &nbsp;</p>
<img src ="http://www.cnitblog.com/ffan/aggbug/37010.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/ffan/" target="_blank">ffan</a> 2007-11-29 08:47 <a href="http://www.cnitblog.com/ffan/archive/2007/11/29/37010.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[C#][转]在C#中利用Excel做高级报表</title><link>http://www.cnitblog.com/ffan/archive/2007/07/20/30291.html</link><dc:creator>ffan</dc:creator><author>ffan</author><pubDate>Fri, 20 Jul 2007 04:58:00 GMT</pubDate><guid>http://www.cnitblog.com/ffan/archive/2007/07/20/30291.html</guid><wfw:comment>http://www.cnitblog.com/ffan/comments/30291.html</wfw:comment><comments>http://www.cnitblog.com/ffan/archive/2007/07/20/30291.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cnitblog.com/ffan/comments/commentRss/30291.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/ffan/services/trackbacks/30291.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Visual&nbsp;Studio.Net&nbsp;自2001年2月问世以来，受到越来越多人的喜爱，C#做为主力军，集VB、Delphi的简单和VC的简炼与强大于一体，更是让许多人爱不释手，纷纷倒向它的怀抱。通常的软件都要用到数据库，数据库中必然要用到报表，在Visual&nbsp;Studio.Net中自带了一个水晶报表，虽然功能十分强大，但市面上相关资料非常缺乏，网上全面介绍其使用的文章也...&nbsp;&nbsp;<a href='http://www.cnitblog.com/ffan/archive/2007/07/20/30291.html'>阅读全文</a><img src ="http://www.cnitblog.com/ffan/aggbug/30291.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/ffan/" target="_blank">ffan</a> 2007-07-20 12:58 <a href="http://www.cnitblog.com/ffan/archive/2007/07/20/30291.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[C#][转贴]日期格式化</title><link>http://www.cnitblog.com/ffan/archive/2007/07/16/29898.html</link><dc:creator>ffan</dc:creator><author>ffan</author><pubDate>Mon, 16 Jul 2007 03:04:00 GMT</pubDate><guid>http://www.cnitblog.com/ffan/archive/2007/07/16/29898.html</guid><wfw:comment>http://www.cnitblog.com/ffan/comments/29898.html</wfw:comment><comments>http://www.cnitblog.com/ffan/archive/2007/07/16/29898.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/ffan/comments/commentRss/29898.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/ffan/services/trackbacks/29898.html</trackback:ping><description><![CDATA[<font size=2>为了达到不同的显示效果有时，我们需要对时间进行转化，默认格式为：2007-01-03 14:33:34 ，<br>要转化为其他格式，要用到DateTime.ToString的方法(String, IFormatProvider)，如下所示：<br>using System;<br>using System.Globalization;<br>String format="D";<br>DateTime date=DataTime,Now;<br>Response.Write(date.ToString(format, DateTimeFormatInfo.InvariantInfo));<br>结果输出<br>Thursday, June 16, 2005<br>参数format格式详细用法：<br>格式字符 关联属性/说明<br>d ShortDatePattern<br>D LongDatePattern<br>f 完整日期和时间（长日期和短时间）<br>F FullDateTimePattern（长日期和长时间）<br>g 常规（短日期和短时间）<br>G 常规（短日期和长时间）<br>m、M MonthDayPattern<br>r、R RFC1123Pattern<br>s 使用当地时间的 SortableDateTimePattern（基于 ISO 8601）<br>t ShortTimePattern<br>T LongTimePattern<br>u UniversalSortableDateTimePattern 用于显示通用时间的格式<br>U 使用通用时间的完整日期和时间（长日期和长时间）<br>y、Y YearMonthPattern<br>下表列出了可被合并以构造自定义模式的模式。这些模式是区分大小写的；例如，识别&#8220;MM&#8221;，但不识别&#8220;mm&#8221;。<br>如果自定义模式包含空白字符或用单引号括起来的字符，则输出字符串页也将包含这些字符。<br>未定义为格式模式的一部分或未定义为格式字符的字符按其原义复制。<br>格式模式 说明<br>d 月中的某一天。一位数的日期没有前导零。<br>dd 月中的某一天。一位数的日期有一个前导零。<br>ddd 周中某天的缩写名称，在 AbbreviatedDayNames 中定义。<br>dddd 周中某天的完整名称，在 DayNames 中定义。<br>M 月份数字。一位数的月份没有前导零。<br>MM 月份数字。一位数的月份有一个前导零。<br>MMM 月份的缩写名称，在 AbbreviatedMonthNames 中定义。<br>MMMM 月份的完整名称，在 MonthNames 中定义。<br>y 不包含纪元的年份。如果不包含纪元的年份小于 10，则显示不具有前导零的年份。<br>yy 不包含纪元的年份。如果不包含纪元的年份小于 10，则显示具有前导零的年份。<br>yyyy 包括纪元的四位数的年份。<br>gg 时期或纪元。如果要设置格式的日期不具有关联的时期或纪元字符串，则忽略该模式。<br>h 12 小时制的小时。一位数的小时数没有前导零。<br>hh 12 小时制的小时。一位数的小时数有前导零。<br>H 24 小时制的小时。一位数的小时数没有前导零。<br>HH 24 小时制的小时。一位数的小时数有前导零。<br>m 分钟。一位数的分钟数没有前导零。<br>mm 分钟。一位数的分钟数有一个前导零。<br>s 秒。一位数的秒数没有前导零。<br>ss 秒。一位数的秒数有一个前导零。<br>f 秒的小数精度为一位。其余数字被截断。<br>ff 秒的小数精度为两位。其余数字被截断。<br>fff 秒的小数精度为三位。其余数字被截断。<br>ffff 秒的小数精度为四位。其余数字被截断。<br>fffff 秒的小数精度为五位。其余数字被截断。<br>ffffff 秒的小数精度为六位。其余数字被截断。<br>fffffff 秒的小数精度为七位。其余数字被截断。<br>t 在 AMDesignator 或 PMDesignator 中定义的 AM/PM 指示项的第一个字符（如果存在）。<br>tt 在 AMDesignator 或 PMDesignator 中定义的 AM/PM 指示项（如果存在）。<br>z 时区偏移量（&#8220;+&#8221;或&#8220;-&#8221;后面仅跟小时）。一位数的小时数没有前导零。例如，太平洋标准时间是&#8220;-8&#8221;。<br>zz 时区偏移量（&#8220;+&#8221;或&#8220;-&#8221;后面仅跟小时）。一位数的小时数有前导零。例如，太平洋标准时间是&#8220;-08&#8221;。<br>zzz 完整时区偏移量（&#8220;+&#8221;或&#8220;-&#8221;后面跟有小时和分钟）。一位数的小时数和分钟数有前导零。<br>例如，太平洋标准时间是&#8220;-08:00&#8221;。<br>: 在 TimeSeparator 中定义的默认时间分隔符。<br>/ 在 DateSeparator 中定义的默认日期分隔符。<br>% c 其中 c 是格式模式（如果单独使用）。如果格式模式与原义字符或其他格式模式合并，则可以省略&#8220;%&#8221;字符。<br>\ c 其中 c 是任意字符。照原义显示字符。若要显示反斜杠字符，请使用&#8220;\\&#8221;。<br>只有上面第二个表中列出的格式模式才能用于创建自定义模式；在第一个表中列出的标准格式字符不能用于创建自定义模式。<br>自定义模式的长度至少为两个字符；例如，<br>DateTime.ToString( "d") 返回 DateTime 值；&#8220;d&#8221;是标准短日期模式。<br>DateTime.ToString( "%d") 返回月中的某天；&#8220;%d&#8221;是自定义模式。<br>DateTime.ToString( "d ") 返回后面跟有一个空白字符的月中的某天；&#8220;d&#8221;是自定义模式。<br>比较方便的是,上面的参数可以随意组合,并且不会出错,多试试,肯定会找到你要的时间格式<br>如要得到2005年06月 这样格式的时间<br>可以这样写:<br>date.ToString("yyyy年MM月", DateTimeFormatInfo.InvariantInfo)<br><strong>日期转化二</strong><br>DateTime dt = DateTime.Now;<br>Label1.Text = dt.ToString();//2005-11-5 13:21:25<br>Label2.Text = dt.ToFileTime().ToString();//127756416859912816<br>Label3.Text = dt.ToFileTimeUtc().ToString();//127756704859912816<br>Label4.Text = dt.ToLocalTime().ToString();//2005-11-5 21:21:25<br>Label5.Text = dt.ToLongDateString().ToString();//2005年11月5日<br>Label6.Text = dt.ToLongTimeString().ToString();//13:21:25<br>Label7.Text = dt.ToOADate().ToString();//38661.5565508218<br>Label8.Text = dt.ToShortDateString().ToString();//2005-11-5<br>Label9.Text = dt.ToShortTimeString().ToString();//13:21<br>Label10.Text = dt.ToUniversalTime().ToString();//2005-11-5 5:21:25<br>Label1.Text = dt.Year.ToString();//2005<br>Label2.Text = dt.Date.ToString();//2005-11-5 0:00:00<br>Label3.Text = dt.DayOfWeek.ToString();//Saturday<br>Label4.Text = dt.DayOfYear.ToString();//309<br>Label5.Text = dt.Hour.ToString();//13<br>Label6.Text = dt.Millisecond.ToString();//441<br>Label7.Text = dt.Minute.ToString();//30<br>Label8.Text = dt.Month.ToString();//11<br>Label9.Text = dt.Second.ToString();//28<br>Label10.Text = dt.Ticks.ToString();//632667942284412864<br>Label11.Text = dt.TimeOfDay.ToString();//13:30:28.4412864<br>Label1.Text = dt.ToString();//2005-11-5 13:47:04<br>Label2.Text = dt.AddYears(1).ToString();//2006-11-5 13:47:04<br>Label3.Text = dt.AddDays(1.1).ToString();//2005-11-6 16:11:04<br>Label4.Text = dt.AddHours(1.1).ToString();//2005-11-5 14:53:04<br>Label5.Text = dt.AddMilliseconds(1.1).ToString();//2005-11-5 13:47:04<br>Label6.Text = dt.AddMonths(1).ToString();//2005-12-5 13:47:04<br>Label7.Text = dt.AddSeconds(1.1).ToString();//2005-11-5 13:47:05<br>Label8.Text = dt.AddMinutes(1.1).ToString();//2005-11-5 13:48:10<br>Label9.Text = dt.AddTicks(1000).ToString();//2005-11-5 13:47:04<br>Label10.Text = dt.CompareTo(dt).ToString();//0<br>Label11.Text = dt.Add(?).ToString();//问号为一个时间段<br>Label1.Text = dt.Equals("2005-11-6 16:11:04").ToString();//False<br>Label2.Text = dt.Equals(dt).ToString();//True<br>Label3.Text = dt.GetHashCode().ToString();//1474088234<br>Label4.Text = dt.GetType().ToString();//System.DateTime<br>Label5.Text = dt.GetTypeCode().ToString();//DateTime<br>Label1.Text = dt.GetDateTimeFormats('s')[0].ToString();//2005-11-05T14:06:25<br>Label2.Text = dt.GetDateTimeFormats('t')[0].ToString();//14:06<br>Label3.Text = dt.GetDateTimeFormats('y')[0].ToString();//2005年11月<br>Label4.Text = dt.GetDateTimeFormats('D')[0].ToString();//2005年11月5日<br>Label5.Text = dt.GetDateTimeFormats('D')[1].ToString();//2005 11 05<br>Label6.Text = dt.GetDateTimeFormats('D')[2].ToString();//星期六 2005 11 05<br>Label7.Text = dt.GetDateTimeFormats('D')[3].ToString();//星期六 2005年11月5日<br>Label8.Text = dt.GetDateTimeFormats('M')[0].ToString();//11月5日<br>Label9.Text = dt.GetDateTimeFormats('f')[0].ToString();//2005年11月5日 14:06<br>Label10.Text = dt.GetDateTimeFormats('g')[0].ToString();//2005-11-5 14:06<br>Label11.Text = dt.GetDateTimeFormats('r')[0].ToString();//Sat, 05 Nov 2005 14:06:25 GMT<br>Label1.Text = string.Format("{0:d}",dt);//2005-11-5<br>Label2.Text = string.Format("{0:D}",dt);//2005年11月5日<br>Label3.Text = string.Format("{0:f}",dt);//2005年11月5日 14:23<br>Label4.Text = string.Format("{0:F}",dt);//2005年11月5日 14:23:23<br>Label5.Text = string.Format("{0:g}",dt);//2005-11-5 14:23<br>Label6.Text = string.Format("{0:G}",dt);//2005-11-5 14:23:23<br>Label7.Text = string.Format("{0:M}",dt);//11月5日<br>Label8.Text = string.Format("{0:R}",dt);//Sat, 05 Nov 2005 14:23:23 GMT<br>Label9.Text = string.Format("{0:s}",dt);//2005-11-05T14:23:23<br>Label10.Text&nbsp;&nbsp; string.Format("{0:t}",dt);//14:23<br>Label11.Text = string.Format("{0:T}",dt);//14:23:23<br>Label12.Text = string.Format("{0:u}",dt);//2005-11-05 14:23:23Z<br>Label13.Text = string.Format("{0:U}",dt);//2005年11月5日 6:23:23<br>Label14.Text = string.Format("{0:Y}",dt);//2005年11月<br>Label15.Text = string.Format("{0}",dt);//2005-11-5 14:23:23<br>Label16.Text = string.Format("{0:yyyyMMddHHmmssffff}",dt);&nbsp;&nbsp;</font>
<pre>
<h1 class=ContentTitle><font color=#993366 size=2>C＃比较两时间大小</font></h1>
</pre>
<pre><font size=2>1、比较时间大小的实验<br>&nbsp;&nbsp;&nbsp; string st1="12:13";<br>&nbsp;&nbsp;&nbsp; string st2="14:14";<br>&nbsp;&nbsp;&nbsp; DateTime dt1=Convert.ToDateTime(st1);<br>&nbsp;&nbsp;&nbsp; DateTime dt2=Convert.ToDateTime(st2);<br>&nbsp;&nbsp;&nbsp; DateTime dt3=DateTime.Now;<br>&nbsp;&nbsp;&nbsp; if(DateTime.Compare(dt1,dt2)&gt;0)<br>&nbsp;&nbsp;&nbsp;&nbsp; msg.Text=st1+"&gt;"+st2;<br>&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp; msg.Text=st1+"&lt;"+st2;<br>&nbsp;&nbsp;&nbsp; msg.Text+="\r\n"+dt1.ToString();<br>&nbsp;&nbsp;&nbsp; if(DateTime.Compare(dt1,dt3)&gt;0)<br>&nbsp;&nbsp;&nbsp;&nbsp; msg.Text+="\r\n"+st1+"&gt;"+dt3.ToString();<br>&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp; msg.Text+="\r\n"+st1+"&lt;"+dt3.ToString();</font></pre>
<pre><font size=2>2、计算两个时间差值的函数，返回时间差的绝对值：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private string DateDiff(DateTime DateTime1,DateTime DateTime2)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string dateDiff=null;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TimeSpan ts1=new&nbsp;&nbsp; TimeSpan(DateTime1.Ticks);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TimeSpan ts2=new&nbsp;&nbsp; TimeSpan(DateTime2.Ticks);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TimeSpan ts=ts1.Subtract(ts2).Duration();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dateDiff=ts.Days.ToString()+"天"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; +ts.Hours.ToString()+"小时"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; +ts.Minutes.ToString()+"分钟"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; +ts.Seconds.ToString()+"秒";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</font></pre>
<pre><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return dateDiff;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</font></pre>
<pre><font size=2>3、实现计算DateTime1－36天＝DateTime2的功能</font></pre>
<pre><font size=2>&nbsp;&nbsp;&nbsp; TimeSpan ts=new TimeSpan(40,0,0,0);<br>&nbsp;&nbsp;&nbsp; DateTime dt2=DateTime.Now.Subtract(ts);<br>&nbsp;&nbsp;&nbsp; msg.Text=DateTime.Now.ToString()+"-"+ts.Days.ToString()+"天\r\n";<br>&nbsp;&nbsp;&nbsp; msg.Text+=dt2.ToString(); </font></pre>
<em></em>
<img src ="http://www.cnitblog.com/ffan/aggbug/29898.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/ffan/" target="_blank">ffan</a> 2007-07-16 11:04 <a href="http://www.cnitblog.com/ffan/archive/2007/07/16/29898.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在vc中创建目录（文件夹） </title><link>http://www.cnitblog.com/ffan/archive/2007/07/04/29475.html</link><dc:creator>ffan</dc:creator><author>ffan</author><pubDate>Wed, 04 Jul 2007 15:38:00 GMT</pubDate><guid>http://www.cnitblog.com/ffan/archive/2007/07/04/29475.html</guid><wfw:comment>http://www.cnitblog.com/ffan/comments/29475.html</wfw:comment><comments>http://www.cnitblog.com/ffan/archive/2007/07/04/29475.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cnitblog.com/ffan/comments/commentRss/29475.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/ffan/services/trackbacks/29475.html</trackback:ping><description><![CDATA[<h1><font size=2>最简单的方法是执行dos命令：system("md ...")但是这种方法会弹出来dos窗口。 另一种创建目录的方法：</font> </h1>
<h1>MakeSureDirectoryPathExists</h1>
<p>&#160;</p>
<p>The <strong>MakeSureDirectoryPathExists</strong> function creates all the directories in the specified <em>DirPath</em>, beginning with the root.</p>
<p>&#160;</p>
<pre class=syntax xml:space="preserve">				<strong>BOOL</strong>
<strong>MakeSureDirectoryPathExists(</strong>
<strong>PCSTR</strong>
<em>
<a class=synParam onclick=showTip(this) href=""><u><font color=#638f27>DirPath</font></u></a>
</em>
<strong>
</strong>
<strong>
</strong>
<strong>);</strong>
</pre>
<div class=reftip id=reftip style="VISIBILITY: hidden; OVERFLOW: visible; POSITION: absolute"></div>
<h4>Parameters</h4>
<dl>
<dt><em>DirPath</em>
<dd>[in] Pointer to a null-terminated string that specifies a valid path name. If the final component of the path is a directory, not a file name, the string must end with a backslash (\) character. </dd></dl>
<h4>Return Values</h4>
<p>If the function succeeds, the return value is TRUE.</p>
<p>If the function fails, the return value is FALSE. To retrieve extended error information, call <a href="ms-help://MS.MSDNQTR.2003FEB.1033/debug/base/getlasterror.htm"><strong><u><font color=#638f27>GetLastError</font></u></strong></a>.</p>
<h4>Remarks</h4>
<p>Each directory specified is created, if it does not already exist. If only some of the directories are created, the function will return FALSE.</p>
<p>All DbgHelp functions, such as this one, are single threaded. Therefore, calls from more than one thread to this function will likely result in unexpected behavior or memory corruption. To avoid this, you must synchronize all concurrent calls from more than one thread to this function.</p>
<h4><mshelp:link tabIndex=0 errorurl="../common/badjump.htm" keywords="psdk.requirements">Requirements</mshelp:link> </h4>
<p><strong>Client: </strong>Included in Windows&nbsp;XP and Windows&nbsp;2000 Professional.<br><strong>Server: </strong>Included in Windows Server&nbsp;2003 and Windows&nbsp;2000 Server.<br><strong>Redistributable: </strong>Requires DbgHelp.dll on Windows NT 4.0 and Windows Me/98/95.<br><strong>Header: </strong>Declared in Dbghelp.h.<br><strong>Library: </strong>Use Dbghelp.lib.<br><br>函数功能描述:该函数创建一个从根目录开始的完整的指定路径.<br><br>.函数原型:<br> BOOL MakeSureDirectoryPathExists(PCSTR DirPath);<br><br>.参数:<br> DirPath [in] : 指向一个以NULL结尾的包含正确的指定的路径名,如果路径名的结尾部分不是文件名而是文件夹,那么要以'\'为结束符.<br><br>.返回值:<br> 函数成功返回TRUE;<br> 函数失败返回FALSE;要获得具体错误信息用GetLastError();<br><br>.备注:<br> 每一级目录如果不存在就创建它,如果只有一些目录被创建了,那么函数返回FALSE.<br><br>.示例代码段:<br> 在用MakeSureDirectoryPathExists前,要在Project-&gt;Settings...-&gt;Link-&gt;/Object/library modules中加入imagehlp.lib.<br> {<br> BOOL bRet=MakeSureDirectoryPathExists("f:\\Directory1\\Directory2\\Directory3\\");<br> //创建目录,要注意结尾"\\".<br> ASSERT(bRet);<br> bRet=MakeSureDirectoryPathExists("f:\\Directory1\\Directory2\\Directory3");<br> //创建目录,但不创建Directory3,因为没有'\\'结尾.<br> ASSERT(bRet);<br> bRet=MakeSureDirectoryPathExists("f:\\Directory1\\Directory2\\Directory3\\test.txt");<br> //创建目录,但不创建文件,可以不用'\\'结尾.<br> ASSERT(bRet);<br> }<br><br>.使用条件:<br> Windows NT/2000: 要求是Windows NT 3.1或后续版本。<br> Windows 95/98: 要求是 Windows 95或后续版本。<br> 头文件: 在Dbghelp.h中定义.<br> 静态库: Dbghelp.lib. <br></p>
<img src ="http://www.cnitblog.com/ffan/aggbug/29475.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/ffan/" target="_blank">ffan</a> 2007-07-04 23:38 <a href="http://www.cnitblog.com/ffan/archive/2007/07/04/29475.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[VC]Debug版运行正常Release出错的原因和解决方法</title><link>http://www.cnitblog.com/ffan/archive/2007/06/21/28891.html</link><dc:creator>ffan</dc:creator><author>ffan</author><pubDate>Thu, 21 Jun 2007 03:05:00 GMT</pubDate><guid>http://www.cnitblog.com/ffan/archive/2007/06/21/28891.html</guid><wfw:comment>http://www.cnitblog.com/ffan/comments/28891.html</wfw:comment><comments>http://www.cnitblog.com/ffan/archive/2007/06/21/28891.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cnitblog.com/ffan/comments/commentRss/28891.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/ffan/services/trackbacks/28891.html</trackback:ping><description><![CDATA[今天遇到的问题，调试的时候运行正常，把Release里生成的程序发布出去后运行就崩溃了。在网上搜索找到了解决方法，总结如下：<br><br><span style="COLOR: #0000ff"><strong>原因：<br></strong></span>先看ON_MESSAGE定义：
<h4 class=dtH4>ON_MESSAGE(<a onclick=showTip(this) href=""><span>message</span></a><span>, </span><a onclick=showTip(this) href="">memberFxn</a> )</h4>
<p class=dtH4>这个宏需要两个参数， 如果消息响应函数并没有参数编译器在压栈出栈时就会出错。而Debug版运行时编译器会自动加一些测试代码，所以不会造成堆栈的破坏。<br><br><span style="COLOR: #0000ff"><strong>解决方法：</strong></span></p>
方法1：把ON_MESSAGE替换成ON_MESSAGE_VOID(头文件&lt;AFXPRIV.H&gt;）<br>方法2：修改消息响应函数：afx_msg void OnXXXX(WPARAM wParam, LPARAM lParam)<br><br>
<img src ="http://www.cnitblog.com/ffan/aggbug/28891.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/ffan/" target="_blank">ffan</a> 2007-06-21 11:05 <a href="http://www.cnitblog.com/ffan/archive/2007/06/21/28891.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>实现线程同步的几种方法</title><link>http://www.cnitblog.com/ffan/archive/2007/04/20/25926.html</link><dc:creator>ffan</dc:creator><author>ffan</author><pubDate>Fri, 20 Apr 2007 03:18:00 GMT</pubDate><guid>http://www.cnitblog.com/ffan/archive/2007/04/20/25926.html</guid><wfw:comment>http://www.cnitblog.com/ffan/comments/25926.html</wfw:comment><comments>http://www.cnitblog.com/ffan/archive/2007/04/20/25926.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/ffan/comments/commentRss/25926.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/ffan/services/trackbacks/25926.html</trackback:ping><description><![CDATA[<span style="COLOR: #0000ff"><strong>1. 利用互斥对象实现线程同步</strong></span><br>互斥对象（mutex)属于内核对象,主要函数：<br>
<p>The <a><strong>CreateMutex</strong></a> function creates or opens a named or unnamed mutex object.</p>
<pre class=syntax xml:space="preserve"><strong>HANDLE</strong> <strong>CreateMutex(</strong>
<strong>LPSECURITY_ATTRIBUTES</strong> <em><a class=synParam onclick=showTip(this) href=""><u><font color=#0000ff>lpMutexAttributes</font></u></a></em><strong></strong><strong>,
</strong>  <strong>BOOL</strong> <em><a class=synParam onclick=showTip(this) href=""><u><font color=#0000ff>bInitialOwner</font></u></a></em><strong></strong><strong>,
</strong>  <strong>LPCTSTR</strong> <em><a class=synParam onclick=showTip(this) href=""><u><font color=#0000ff>lpName</font></u></a></em>
<strong>);<br><br><br>
</strong>
<p><strong>The </strong><a><strong>ReleaseMutex</strong></a><strong> function releases ownership of the specified mutex object.</strong></p>
<p><strong>&nbsp;</strong></p>
<pre class=syntax xml:space="preserve"><strong>BOOL ReleaseMutex(
HANDLE <em><a class=synParam onclick=showTip(this) href="">hMutex</a></em>
);<br></strong></pre>
<pre class=syntax xml:space="preserve">
<p><strong>The </strong><a><strong>WaitForSingleObject</strong></a><strong> function returns when one of the following occurs:</strong></p>
<p><strong>&nbsp;</strong></p>
<ul>
    <strong>    </strong>
    <li><strong>The specified object is in the signaled state.
    </strong>
    <li><strong>The time-out interval elapses.
    </strong></li>
</ul>
<p><strong>To enter an alertable wait state, use the </strong><a href="ms-help://MS.MSDNQTR.2003FEB.1033/dllproc/base/waitforsingleobjectex.htm"><strong>WaitForSingleObjectEx</strong></a><strong> function. To wait for multiple objects, use the </strong><a href="ms-help://MS.MSDNQTR.2003FEB.1033/dllproc/base/waitformultipleobjects.htm"><strong>WaitForMultipleObjects</strong></a><strong>.</strong></p>
<p><strong>&nbsp;</strong></p>
<pre class=syntax xml:space="preserve"><strong>DWORD WaitForSingleObject(
HANDLE <em><a class=synParam onclick=showTip(this) href="">hHandle</a></em>,
DWORD <em><a class=synParam onclick=showTip(this) href="">dwMilliseconds</a></em>
);
</strong></pre>
<pre class=syntax xml:space="preserve"><strong><span style="COLOR: #0000ff">2. 利用事件对象实现线程同步<br></span>主要函数：</strong></pre>
<pre class=syntax xml:space="preserve">
<p><strong>The </strong><a><strong>CreateEvent</strong></a><strong> function creates or opens a named or unnamed event object.</strong></p>
<p><strong>&nbsp;</strong></p>
<pre class=syntax xml:space="preserve"><strong>HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES <em><a class=synParam onclick=showTip(this) href="">lpEventAttributes</a></em>,
BOOL <em><a class=synParam onclick=showTip(this) href="">bManualReset</a></em>,
BOOL <em><a class=synParam onclick=showTip(this) href="">bInitialState</a></em>,
LPCTSTR <em><a class=synParam onclick=showTip(this) href="">lpName</a></em>
);
</strong></pre>
<pre class=syntax xml:space="preserve">
<p><strong>The </strong><a><strong>SetEvent</strong></a><strong> function sets the specified event object to the signaled state.</strong></p>
<p><strong>&nbsp;</strong></p>
<pre class=syntax xml:space="preserve"><strong>BOOL SetEvent(
HANDLE <em><a class=synParam onclick=showTip(this) href="">hEvent</a></em>
);
</strong></pre>
<pre><strong>The </strong><a><strong>ResetEvent</strong></a><strong> function sets the specified event object to the nonsignaled state.</strong></pre>
<p><strong>&nbsp;</strong></p>
<pre class=syntax xml:space="preserve"><strong>BOOL ResetEvent(
HANDLE <em><a class=synParam onclick=showTip(this) href="">hEvent</a></em>
);
</strong></pre>
<pre class=syntax xml:space="preserve"><span style="COLOR: #0000ff"><strong>3. 关键代码</strong></span></pre>
<pre class=syntax xml:space="preserve"><strong>关键代码段， 也称为临界区，工作在用户方式下。它是指一个小代码段，在代码能够执行前，它必须独占对某些资源的访问权</strong></pre>
<pre class=syntax xml:space="preserve"><strong>&nbsp;</strong></pre>
<pre class=syntax xml:space="preserve"><strong>主要函数：</strong></pre>
<pre><strong>The </strong><a><strong>InitializeCriticalSection</strong></a><strong> function initializes a critical section object.</strong></pre>
<p><strong>&nbsp;</strong></p>
<pre class=syntax xml:space="preserve"><strong>void InitializeCriticalSection(
LPCRITICAL_SECTION <em><a class=synParam onclick=showTip(this) href="">lpCriticalSection</a></em>
);
</strong></pre>
<pre class=syntax xml:space="preserve">
<p><strong>The </strong><a><strong>EnterCriticalSection</strong></a><strong> function waits for ownership of the specified critical section object. The function returns when the calling thread is granted ownership.</strong></p>
<p><strong>&nbsp;</strong></p>
<pre class=syntax xml:space="preserve"><strong>void EnterCriticalSection(
LPCRITICAL_SECTION <em><a class=synParam onclick=showTip(this) href="">lpCriticalSection</a></em>
);
</strong></pre>
<pre><strong>The </strong><a><strong>LeaveCriticalSection</strong></a><strong> function releases ownership of the specified critical section object.</strong></pre>
<p><strong>&nbsp;</strong></p>
<pre class=syntax xml:space="preserve"><strong>void LeaveCriticalSection(
LPCRITICAL_SECTION <em><a class=synParam onclick=showTip(this) href="">lpCriticalSection</a></em>
);
</strong></pre>
<span style="COLOR: #008000">三种方法的区别在于：</span></pre>
<pre class=syntax xml:space="preserve"><span style="COLOR: #008000">互斥对象和事件都属于内核对象，利用内核对象进行线程同步时速度较慢，但利用这两种方法可以在多个进程的各个线程间进行同步</span></pre>
<pre class=syntax xml:space="preserve"><span style="COLOR: #008000">关键代码段工作方式下，同步速度较快， 但很容易产生死锁，因为在等待进入关键代码段时无法设定超时值</span></pre>
<pre class=syntax xml:space="preserve">
<pre class=syntax xml:space="preserve"><strong>&nbsp;</strong></pre>
</pre>
</pre>
</pre>
<br><br><br><br></pre>
</pre>
<pre class=syntax xml:space="preserve"><br><br></pre>
<img src ="http://www.cnitblog.com/ffan/aggbug/25926.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/ffan/" target="_blank">ffan</a> 2007-04-20 11:18 <a href="http://www.cnitblog.com/ffan/archive/2007/04/20/25926.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WINDOWS钩子函数[1]</title><link>http://www.cnitblog.com/ffan/archive/2005/08/23/2273.html</link><dc:creator>ffan</dc:creator><author>ffan</author><pubDate>Tue, 23 Aug 2005 08:07:00 GMT</pubDate><guid>http://www.cnitblog.com/ffan/archive/2005/08/23/2273.html</guid><wfw:comment>http://www.cnitblog.com/ffan/comments/2273.html</wfw:comment><comments>http://www.cnitblog.com/ffan/archive/2005/08/23/2273.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/ffan/comments/commentRss/2273.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/ffan/services/trackbacks/2273.html</trackback:ping><description><![CDATA[本课中我们将要<A class=Channel_KeyLink href="http://www.study888.com/">学习</A>WINDOWS钩子函数的使用方法。WINDOWS钩子函数的功能非常强大，有了它您可以探测其它进程并且改变其它进程的行为。 <BR>理论：<BR>WINDOWS的钩子函数可以认为是WINDOWS的主要特性之一。利用它们，您可以捕捉您自己进程或其它进程发生的事件。通过“钩挂”，您可以给WINDOWS一个处理或过滤事件的回调函数，该函数也叫做“钩子函数”，当每次发生您感兴趣的事件时，WINDOWS都将调用该函数。一共有两种类型的钩子：局部的和远程的。 <BR>局部钩子仅钩挂您自己进程的事件。 <BR>远程的钩子还可以将钩挂其它进程发生的事件。远程的钩子又有两种： <BR>基于线程的 它将捕获其它进程中某一特定线程的事件。简言之，就是可以用来观察其它进程中的某一特定线程将发生的事件。 <BR>系统范围的 将捕捉系统中所有进程将发生的事件消息。 <BR>安装钩子函数将会影响系统的性能。监测“系统范围事件”的系统钩子特别明显。因为系统在处理所有的相关事件时都将调用您的钩子函数，这样您的系统将会明显的减慢。所以应谨慎使用，用完后立即卸载。还有，由于您可以预先截获其它进程的消息，所以一旦您的钩子函数出了问题的话必将影响其它的进程。记住：功能强大也意味着使用时要负责任。<BR>在正确使用钩子函数前，我们先讲解钩子函数的工作原理。当您创建一个钩子时，WINDOWS会先在内存中创建一个数据结构，该数据结构包含了钩子的相关信息，然后把该结构体加到已经存在的钩子链表中去。新的钩子将加到老的前面。当一个事件发生时，如果您安装的是一个局部钩子，您进程中的钩子函数将被调用。如果是一个远程钩子，系统就必须把钩子函数插入到其它进程的地址空间，要做到这一点要求钩子函数必须在一个动态链接库中，所以如果您想要使用远程钩子，就必须把该钩子函数放到动态链接库中去。当然有两个例外：工作日志钩子和工作日志回放钩子。这两个钩子的钩子函数必须在安装钩子的线程中。原因是：这两个钩子是用来监控比较底层的硬件事件的，既然是记录和回放，所有的事件就当然都是有先后次序的。所以如果把回调函数放在DLL中，输入的事件被放在几个线程中记录，所以我们无法保证得到正确的次序。故解决的办法是：把钩子函数放到单个的线程中，譬如安装钩子的线程。<BR>钩子一共有14种，以下是它们被调用的时机： <BR>WH_CALLWNDPROC 当调用SendMessage时 <BR>WH_CALLWNDPROCRET 当SendMessage的调用返回时 <BR>WH_GETMESSAGE 当调用GetMessage 或 PeekMessage时 <BR>WH_KEYBOARD 当调用GetMessage 或 PeekMessage 来从消息队列中查询WM_KEYUP 或 WM_KEYDOWN 消息时 <BR>WH_MOUSE 当调用GetMessage 或 PeekMessage 来从消息队列中查询鼠标事件消息时 <BR>WH_HARDWARE 当调用GetMessage 或 PeekMessage 来从消息队列种查询非鼠标、键盘消息时 <BR>WH_MSGFILTER 当对话框、菜单或滚动条要处理一个消息时。该钩子是局部的。它时为那些有自己的消息处理过程的控件对象设计的。 <BR>WH_SYSMSGFILTER 和WH_MSGFILTER一样，只不过是系统范围的 <BR>WH_JOURNALRECORD 当WINDOWS从硬件队列中获得消息时 <BR>WH_JOURNALPLAYBACK 当一个事件从系统的硬件输入队列中被请求时 <BR>WH_SHELL 当关于WINDOWS外壳事件发生时，譬如任务条需要重画它的按钮. <BR>WH_CBT 当基于计算机的训练(CBT)事件发生时 <BR>WH_FOREGROUNDIDLE 由WINDOWS自己使用，一般的应用程序很少使用 <BR>WH_DEBUG 用来给钩子函数除错 <BR>现在我们知道了一些基本的理论，现在开始讲解如何安装/卸载一个钩子。 <BR>要安装一个钩子，您可以调用SetWindowHookEx函数。该函数的原型如下： <BR>SetWindowsHookEx proto HookType:DWORD, pHookProc:DWORD, hInstance:DWORD, ThreadID:DWORD <BR>HookType 是我们上面列出的值之一,譬如： WH_MOUSE, WH_KEYBOARD <BR>pHookProc 是钩子函数的地址。如果使用的是远程的钩子，就必须放在一个DLL中，否则放在本身代码中 <BR>hInstance 钩子函数所在DLL的实例句柄。如果是一个局部的钩子，该值为NULL <BR>ThreadID 是您安装该钩子函数后想监控的线程的ID号。该参数可以决定该钩子是局部的还是系统范围的。如果该值为NULL，那么该钩子将被解释成系统范围内的，那它就可以监控所有的进程及它们的线程。如果您指定了您自己进程中的某个线程ID 号，那该钩子是一个局部的钩子。如果该线程ID是另一个进程中某个线程的ID，那该钩子是一个全局的远程钩子。这里有两个特殊情况：WH_JOURNALRECORD 和 WH_JOURNALPLAYBACK总是代表局部的系统范围的钩子，之所以说是局部，是因为它们没有必要放到一个DLL中。WH_SYSMSGFILTER 总是一个系统范围内的远程钩子。其实它和WH_MSGFILTER钩子类似，如果把参数ThreadID设成0的话，它们就完全一样了。 <BR>如果该函数调用成功的话，将在eax中返回钩子的句柄，否则返回NULL。您必须保存该句柄，因为后面我们还要它来卸载钩子。<BR>要卸载一个钩子时调用UnhookWidowHookEx函数，该函数仅有一个参数，就是欲卸载的钩子的句柄。如果调用成功的话，在eax中返回非0值，否则返回NULL。<BR>现在您知道了如何安装和卸载一个钩子了，接下来我们将看看钩子函数。. <BR>只要您安装的钩子的消息事件类型发生，WINDOWS就将调用钩子函数。譬如您安装的钩子是WH_MOUSE类型，那么只要有一个鼠标事件发生时，该钩子函数就会被调用。不管您安装的时那一类型钩子，钩子函数的原型都时是一样的： <BR>HookProc proto nCode:DWORD, wParam:DWORD, lParam:DWORD <BR><BR>nCode 指定是否需要处理该消息 <BR>wParam 和 lParam 包含该消息的附加消息 <BR>HookProc 可以看作是一个函数名的占位符。只要函数的原型一致，您可以给该函数取任何名字。至于以上的几个参数及返回值的具体含义各种类型的钩子都不相同。譬如： <BR>WH_CALLWNDPROC <BR>nCode 只能是HC_ACTION，它代表有一个消息发送给了一个窗口 <BR>wParam 如果非0，代表正被发送的消息 <BR>lParam 指向CWPSTRUCT型结构体变量的指针 <BR>return value: 未使用，返回0 <BR>WH_MOUSE <BR>nCode 为HC_ACTION 或 HC_NOREMOVE <BR>wParam 包含鼠标的事件消息 <BR>lParam 指向MOUSEHOOKSTRUCT型结构体变量的指针 <BR>return value: 如果不处理返回0，否则返回非0值 <BR>所以您必须查询您的WIN32 API 指南来得到不同类型的钩子的参数的详细定义以及它们返回值的意义。这里还有一个问题需要注意：所有的钩子都串在一个链表上，最近加入的钩子放在链表的头部。当一个事件发生时，WINDOWS将按照从链表头到链表尾调用的顺序。所以您的钩子函数有责任把消息传到下一个链中的钩子函数。当然您可以不这样做，但是您最好明白这时这么做的原因。在大多数的情况下，最好把消息事件传递下去以便其它的钩子都有机会获得处理这一消息的机会。调用下一个钩子函数可以调用函数CallNextHookEx。该函数的原型如下： <BR>CallNextHookEx proto hHook:DWORD, nCode:DWORD, wParam:DWORD, lParam:DWORD <BR>hHook 时是您自己的钩子函数的句柄。利用该句柄可以遍历钩子链。 <BR>nCode, wParam and lParam 您只要把传入的参数简单传给CallNextHookEx即可。 <BR>请注意：对于远程钩子，钩子函数必须放到DLL中，它们将从DLL中映射到其它的进程空间中去。当WINDOWS映射DLL到其它的进程空间中去时，不会把数据段也进行映射。简言之，所有的进程仅共享DLL的代码，至于数据段，每一个进程都将有其单独的拷贝。这是一个很容易被忽视的问题。您可能想当然的以为，在DLL中保存的值可以在所有映射该DLL的进程之间共享。在通常情况下，由于每一个映射该DLL的进程都有自己的数据段，所以在大多数的情况下您的程序运行得都不错。但是钩子函数却不是如此。对于钩子函数来说，要求DLL的数据段对所有的进程也必须相同。这样您就必须把数据段设成共享的，这可以通过在链接开关中指定段的属性来实现。在MASM中您可以这么做： <BR>/SECTION:&lt;section name&gt;, S<BR>已初期化的段名是.data，未初始化的段名是.bss。`加入您想要写一个包含钩子函数的DLL，而且想使它的未初始化的数据段在所有进程间共享，您必须这么做： <BR>link /section:.bss,S /DLL /SUBSYSTEM:WINDOWS ..........<BR>S 代表该段是共享段。 <BR>例子：<BR>一共有两个模块：一个是GUI部分，另一个是安装和卸载钩子的DLL。 <BR>;--------------------------------------------- 主程序的源代码部分-------------------------------------- <BR>.386 <BR>.model flat,stdcall <BR>option casemap:none <BR>include \masm32\include\windows.inc <BR>include \masm32\include\user32.inc <BR>include \masm32\include\kernel32.inc <BR>include mousehook.inc <BR>includelib mousehook.lib <BR>includelib \masm32\lib\user32.lib <BR>includelib \masm32\lib\kernel32.lib 
<P>wsprintfA proto C :DWORD,:DWORD,:VARARG <BR>wsprintf TEXTEQU &lt;wsprintfA&gt; </P>
<P>.const <BR>IDD_MAINDLG equ 101 <BR>IDC_CLASSNAME equ 1000 <BR>IDC_HANDLE equ 1001 <BR>IDC_WNDPROC equ 1002 <BR>IDC_HOOK equ 1004 <BR>IDC_EXIT equ 1005 <BR>WM_MOUSEHOOK equ WM_USER+6 </P>
<P>DlgFunc PROTO :DWORD,:DWORD,:DWORD,:DWORD </P>
<P>.data <BR>HookFlag dd FALSE <BR>HookText db "&amp;Hook",0 <BR>UnhookText db "&amp;Unhook",0 <BR>template db "%lx",0 </P>
<P>.data? <BR>hInstance dd ? <BR>hHook dd ? <BR>.code <BR>start: <BR>invoke GetModuleHandle,NULL <BR>mov hInstance,eax <BR>invoke DialogBoxParam,hInstance,IDD_MAINDLG,NULL,addr DlgFunc,NULL <BR>invoke ExitProcess,NULL </P>
<P>DlgFunc proc hDlg:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD <BR>LOCAL hLib:DWORD <BR>LOCAL buffer[128]:byte <BR>LOCAL buffer1[128]:byte <BR>LOCAL rect:RECT <BR>.if uMsg==WM_CLOSE <BR>.if HookFlag==TRUE <BR>invoke UninstallHook <BR>.endif <BR>invoke EndDialog,hDlg,NULL <BR>.elseif uMsg==WM_INITDIALOG <BR>invoke GetWindowRect,hDlg,addr rect <BR>invoke SetWindowPos, hDlg, HWND_TOPMOST, rect.left, rect.top, rect.right, rect.bottom, SWP_SHOWWINDOW <BR>.elseif uMsg==WM_MOUSEHOOK <BR>invoke GetDlgItemText,hDlg,IDC_HANDLE,addr buffer1,128 <BR>invoke wsprintf,addr buffer,addr template,wParam <BR>invoke lstrcmpi,addr buffer,addr buffer1 <BR>.if eax!=0 <BR>invoke SetDlgItemText,hDlg,IDC_HANDLE,addr buffer <BR>.endif <BR>invoke GetDlgItemText,hDlg,IDC_CLASSNAME,addr buffer1,128 <BR>invoke GetClassName,wParam,addr buffer,128 <BR>invoke lstrcmpi,addr buffer,addr buffer1 <BR>.if eax!=0 <BR>invoke SetDlgItemText,hDlg,IDC_CLASSNAME,addr buffer <BR>.endif <BR>invoke GetDlgItemText,hDlg,IDC_WNDPROC,addr buffer1,128 <BR>invoke GetClassLong,wParam,GCL_WNDPROC <BR>invoke wsprintf,addr buffer,addr template,eax <BR>invoke lstrcmpi,addr buffer,addr buffer1 <BR>.if eax!=0 <BR>invoke SetDlgItemText,hDlg,IDC_WNDPROC,addr buffer <BR>.endif <BR>.elseif uMsg==WM_COMMAND <BR>.if lParam!=0 <BR>mov eax,wParam <BR>mov edx,eax <BR>shr edx,16 <BR>.if dx==BN_CLICKED <BR>.if ax==IDC_EXIT <BR>invoke SendMessage,hDlg,WM_CLOSE,0,0 <BR>.else <BR>.if HookFlag==FALSE <BR>invoke InstallHook,hDlg <BR>.if eax!=NULL <BR>mov HookFlag,TRUE <BR>invoke SetDlgItemText,hDlg,IDC_HOOK,addr UnhookText <BR>.endif <BR>.else <BR>invoke UninstallHook <BR>invoke SetDlgItemText,hDlg,IDC_HOOK,addr HookText <BR>mov HookFlag,FALSE <BR>invoke SetDlgItemText,hDlg,IDC_CLASSNAME,NULL <BR>invoke SetDlgItemText,hDlg,IDC_HANDLE,NULL <BR>invoke SetDlgItemText,hDlg,IDC_WNDPROC,NULL <BR>.endif <BR>.endif <BR>.endif <BR>.endif <BR>.else <BR>mov eax,FALSE <BR>ret <BR>.endif <BR>mov eax,TRUE <BR>ret <BR>DlgFunc endp </P>
<P>end start </P>
<P>;----------------------------------------------------- DLL的源代码部分 -------------------------------------- <BR>.386 <BR>.model flat,stdcall <BR>option casemap:none <BR>include \masm32\include\windows.inc <BR>include \masm32\include\kernel32.inc <BR>includelib \masm32\lib\kernel32.lib <BR>include \masm32\include\user32.inc <BR>includelib \masm32\lib\user32.lib </P>
<P>.const <BR>WM_MOUSEHOOK equ WM_USER+6 </P>
<P>.data <BR>hInstance dd 0 </P>
<P>.data? <BR>hHook dd ? <BR>hWnd dd ? </P>
<P>.code <BR>DllEntry proc hInst:HINSTANCE, reason:DWORD, reserved1:DWORD <BR>.if reason==DLL_PROCESS_ATTACH <BR>push hInst <BR>pop hInstance <BR>.endif <BR>mov eax,TRUE <BR>ret <BR>DllEntry Endp </P>
<P>MouseProc proc nCode:DWORD,wParam:DWORD,lParam:DWORD <BR>invoke CallNextHookEx,hHook,nCode,wParam,lParam <BR>mov edx,lParam <BR>assume edx:PTR MOUSEHOOKSTRUCT <BR>invoke WindowFromPoint,[edx].pt.x,[edx].pt.y <BR>invoke PostMessage,hWnd,WM_MOUSEHOOK,eax,0 <BR>assume edx:nothing <BR>xor eax,eax <BR>ret <BR>MouseProc endp </P>
<P>InstallHook proc hwnd:DWORD <BR>push hwnd <BR>pop hWnd <BR>invoke SetWindowsHookEx,WH_MOUSE,addr MouseProc,hInstance,NULL <BR>mov hHook,eax <BR>ret <BR>InstallHook endp </P>
<P>UninstallHook proc <BR>invoke UnhookWindowsHookEx,hHook <BR>ret <BR>UninstallHook endp </P>
<P>End DllEntry </P>
<P>;---------------------------------------------- DLL的Makefile文件 ---------------------------------------------- </P>
<P>NAME=mousehook <BR>$(NAME).dll: $(NAME).obj <BR>Link /SECTION:.bss,S /DLL /DEF:$(NAME).def /SUBSYSTEM:WINDOWS /LIBPATH:c:\masm\lib $(NAME).obj <BR>$(NAME).obj: $(NAME).asm <BR>ml /c /coff /Cp $(NAME).asm <BR></P>
<P>分析:<BR>该应用程序的主窗口中包括三个编辑控件，它们将分别显示当前鼠标光标所在位置的窗口类名、窗口句柄和窗口过程的地址。还有两个按钮：“Hook”和“Eixt”。当您按下Hook时，应用程序将钩挂鼠标输入的事件消息，该按钮的文本将变成“Unhook”。当您把鼠标关标滑过一个窗口时，该窗口的有关消息将显示在主窗口中。当您按下“Unhook”时，应用程序将卸载钩子。 主窗口使用一个对话框来作为它的主窗口。它自定义了一个消息WM_MOUSEHOOK，用来在主窗口和DLL之间传递消息。当主窗口接收到该消息时，wParam中包含了光标所在位置的窗口的句柄。当然这是我们做的安排。我这么做只是为了方便。您可以使用您自己的方法在主应用程序和DLL之间进行通讯。 <BR>.if HookFlag==FALSE <BR>invoke InstallHook,hDlg <BR>.if eax!=NULL <BR>mov HookFlag,TRUE <BR>invoke SetDlgItemText,hDlg,IDC_HOOK,addr UnhookText <BR>.endif </P>
<P>该应用程序有一个全局变量，HookFlag，它用来监视钩子的状态。如果安装来钩子它就是TRUE，否则是FALSE。 当用户按下Hook按钮时，应用程序检查钩子是否已经安装。如果还没有的话，它将调用DLL中引出的函数InstallHook来安装它。注意我们把主对话框的句柄传递给了DLL，这样这个钩子DLL就可以把WM_MOUSEHOOK消息传递给正确的窗口了。当应用程序加载时，钩子DLL也同时加载。时机上当主程序一旦加载到内存中后，DLL就立即加载。DLL的入口点函数载主程序的第一条语句执行前就前执行了。所以当主程序执行时，DLL已经初始化好了。我们载入口点处放入如下代码： </P>
<P>.if reason==DLL_PROCESS_ATTACH <BR>push hInst <BR>pop hInstance <BR>.endif </P>
<P>该段代码把DLL自己的实例句柄放到一个全局变量中保存。由于入口点函数是在所有函数调用前被执行的，所以hInstance总是有效的。我们把该变量放到.data中，使得每一个进程都有自己一个该变量的值。因为当鼠标光标停在一个窗口上时，钩子DLL被映射进进程的地址空间。加入在DLL缺省加载的地址处已经加载其它的DLL，那钩子DLL将要被映射到其他的地址。hInstance将被更新成其它的值。当用户按下Unhook再按下Hook时，SetWindowsHookEx将被再次调用。这一次，它将把新的地址作为实例句柄。而在例子中这是错误的，DLL装载的地址并没有变。这个钩子将变成一个局部的，您只能钩挂发生在您窗口中的鼠标事件，这是很难让人满意的 。 </P>
<P>InstallHook proc hwnd:DWORD <BR>push hwnd <BR>pop hWnd <BR>invoke SetWindowsHookEx,WH_MOUSE,addr MouseProc,hInstance,NULL <BR>mov hHook,eax <BR>ret <BR>InstallHook endp </P>
<P>InstallHook 函数非常简单。它把传递过来的窗口句柄保存在hWnd中以备后用。接着调用SetWindowsHookEx函数来安装一个鼠标钩子。该函数的返回值放在全局变量hHook中，将来在UnhookWindowsHookEx中还要使用。在调用SetWindowsHookEx后，鼠标钩子就开始工作了。无论什么时候发生了鼠标事件，MouseProc函数都将被调用： </P>
<P>MouseProc proc nCode:DWORD,wParam:DWORD,lParam:DWORD <BR>invoke CallNextHookEx,hHook,nCode,wParam,lParam <BR>mov edx,lParam <BR>assume edx:PTR MOUSEHOOKSTRUCT <BR>invoke WindowFromPoint,[edx].pt.x,[edx].pt.y <BR>invoke PostMessage,hWnd,WM_MOUSEHOOK,eax,0 <BR>assume edx:nothing <BR>xor eax,eax <BR>ret <BR>MouseProc endp </P>
<P>钩子函数首先调用CallNextHookEx函数让其它的钩子处理该鼠标事件。然后，调用WindowFromPoint函数来得到给定屏幕坐标位置处的窗口句柄。注意：我们用lParam指向的MOUSEHOOKSTRUCT型结构体变量中的POINT成员变量作为当前的鼠标位置。在我们调用PostMessage函数把WM_MOUSEHOOK消息发送到主程序。您必须记住的一件事是：在钩子函数中不要使用SendMessage函数，它会引起死锁。MOUSEHOOKSTRUCT的定义如下： </P>
<P>MOUSEHOOKSTRUCT STRUCT DWORD <BR>pt POINT &lt;&gt; <BR>hwnd DWORD ? <BR>wHitTestCode DWORD ? <BR>dwExtraInfo DWORD ? <BR>MOUSEHOOKSTRUCT ENDS <BR></P>
<P>pt 是当前鼠标所在的屏幕位置。 <BR>hwnd 是将接收鼠标消息的窗口的句柄。通常它是鼠标所在处的窗口，但是如果窗口调用了SetCapture，鼠标的输入将到向到这个窗口。因我们不用该成员变量而是用WindowFromPoint函数。 <BR>wHitTestCode 指定hit-test值，该值给出了更多的鼠标位置值。它指定了鼠标在窗口的那个部位。该值的完全列表，请参考WIN32 API 指南中的WM_NCHITTEST消息。 <BR>dwExtraInfo 该值包含了相关的信息。一般该值由mouse_event函数设定，可以调用GetMessageExtraInfo来获得。 <BR></P>
<P>当主窗口接收到WM_MOUSEHOOK 消息时，它用wParam参数中的窗口句柄来查询窗口的消息。</P>
<P>.elseif uMsg==WM_MOUSEHOOK <BR>invoke GetDlgItemText,hDlg,IDC_HANDLE,addr buffer1,128 <BR>invoke wsprintf,addr buffer,addr template,wParam <BR>invoke lstrcmpi,addr buffer,addr buffer1 <BR>.if eax!=0 <BR>invoke SetDlgItemText,hDlg,IDC_HANDLE,addr buffer <BR>.endif <BR>invoke GetDlgItemText,hDlg,IDC_CLASSNAME,addr buffer1,128 <BR>invoke GetClassName,wParam,addr buffer,128 <BR>invoke lstrcmpi,addr buffer,addr buffer1 <BR>.if eax!=0 <BR>invoke SetDlgItemText,hDlg,IDC_CLASSNAME,addr buffer <BR>.endif <BR>invoke GetDlgItemText,hDlg,IDC_WNDPROC,addr buffer1,128 <BR>invoke GetClassLong,wParam,GCL_WNDPROC <BR>invoke wsprintf,addr buffer,addr template,eax <BR>invoke lstrcmpi,addr buffer,addr buffer1 <BR>.if eax!=0 <BR>invoke SetDlgItemText,hDlg,IDC_WNDPROC,addr buffer <BR>.endif </P>
<P>为了避免重绘文本时的抖动，我们把已经在编辑空间中线时的文本和我们将要显示的对比。如果相同，就可以忽略掉。得到类名调用GetClassName，得到窗口过程调用GetClassLong并传入GCL_WNDPROC标志，然后把它们格式化成文本串并放到相关的编辑空间中去。 </P>
<P>invoke UninstallHook <BR>invoke SetDlgItemText,hDlg,IDC_HOOK,addr HookText <BR>mov HookFlag,FALSE <BR>invoke SetDlgItemText,hDlg,IDC_CLASSNAME,NULL <BR>invoke SetDlgItemText,hDlg,IDC_HANDLE,NULL <BR>invoke SetDlgItemText,hDlg,IDC_WNDPROC,NULL </P>
<P>当用户按下Unhook后，主程序调用DLL中的UninstallHook函数。该函数调用UnhookWindowsHookEx函数。然后，它把按钮的文本换回“Hook”，HookFlag的值设成FALSE再清除掉编辑控件中的文本。<BR>链接器的开关选项如下： </P>
<P>Link /SECTION:.bss,S /DLL /DEF:$(NAME).def /SUBSYSTEM:WINDOWS </P>
<P>它指定.bss段作为一个共享段以便所有映射该DLL的进程共享未初始化的数据段。如果不用该开关，您DLL中的钩子就不能正常工作了。</P><img src ="http://www.cnitblog.com/ffan/aggbug/2273.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/ffan/" target="_blank">ffan</a> 2005-08-23 16:07 <a href="http://www.cnitblog.com/ffan/archive/2005/08/23/2273.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>