﻿<?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>C++博客-一针见血-文章分类-VC++</title><link>http://www.cppblog.com/gtwdaizi/category/5210.html</link><description /><language>zh-cn</language><lastBuildDate>Thu, 30 Oct 2008 03:56:52 GMT</lastBuildDate><pubDate>Thu, 30 Oct 2008 03:56:52 GMT</pubDate><ttl>60</ttl><item><title>[Windows Moble/VC++]GetCurrentTime()/GetLocalTime()/GetTickCount()</title><link>http://www.cppblog.com/gtwdaizi/articles/49177.html</link><dc:creator>郭天文</dc:creator><author>郭天文</author><pubDate>Thu, 08 May 2008 01:34:00 GMT</pubDate><guid>http://www.cppblog.com/gtwdaizi/articles/49177.html</guid><wfw:comment>http://www.cppblog.com/gtwdaizi/comments/49177.html</wfw:comment><comments>http://www.cppblog.com/gtwdaizi/articles/49177.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/gtwdaizi/comments/commentRss/49177.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/gtwdaizi/services/trackbacks/49177.html</trackback:ping><description><![CDATA[1. GetCurrentTime()<br><br>&nbsp;&nbsp;&nbsp;&nbsp;GetCurrentTime()只和16位版本的windows兼容，在32位windows下最好用gettickcount();<br><br>2. GetLocalTime()<br><br>&nbsp;&nbsp;&nbsp;&nbsp;GetLocalTime()在不同的机器中会有不同的结果，这和你在控制面板中的时区设置有关. 该函数是获取的系统当前所属时区的时间, 比如说, 在北京时区, 那么获取的该时间的时间.<br><br>3. GetSystemTime()<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GetSystemTime()获取的格林尼治时间, 是全球标准时间.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SYSTEMTIME stUTC;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GetSystemTime(&amp;stUTC);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="FONT-SIZE: 10.5pt">TCHAR chBuf[nBufSize];</span><br><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;wsprintf(chBuf,_T("UTC: %u/%u/%u&nbsp;%u:%u:%u:%u %d\r\n"),&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><br><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stUTC.wYear, stUTC.wMonth, stUTC.wDay,</span><br><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stUTC.wHour, stUTC.wMinute, stUTC.wSecond,</span><br><span style="FONT-SIZE: 10.5pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stUTC.wMilliseconds,stUTC.wDayOfWeek);</span><br><br>4. GetTickCount()<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GetTickCount()获取的是从设备开机后的毫秒数. 不包括系统的挂起时间.<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;主要的应用:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dwOldTime = GetTickCount();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DoSomeThing();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dwTimeElapsed = GetTickCount() - dwOldTime;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;获取某段程序执行所需的时间.<br><br>5. 更好的办法：&#8220;now函数&#8221; <br>&nbsp;&nbsp;&nbsp;&nbsp;例如：formatdatetime('yyyy ''年'' m ''月'' d ''日''dddd '+'hh:mm:ssAM/PM',now); <br>&nbsp;&nbsp;&nbsp;&nbsp;输出结果： 2001年5月8日星期一19：35：40 PM 
<img src ="http://www.cppblog.com/gtwdaizi/aggbug/49177.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/gtwdaizi/" target="_blank">郭天文</a> 2008-05-08 09:34 <a href="http://www.cppblog.com/gtwdaizi/articles/49177.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>VC 中clw、ncb、aps文件的作用</title><link>http://www.cppblog.com/gtwdaizi/articles/43370.html</link><dc:creator>郭天文</dc:creator><author>郭天文</author><pubDate>Thu, 28 Feb 2008 01:13:00 GMT</pubDate><guid>http://www.cppblog.com/gtwdaizi/articles/43370.html</guid><wfw:comment>http://www.cppblog.com/gtwdaizi/comments/43370.html</wfw:comment><comments>http://www.cppblog.com/gtwdaizi/articles/43370.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/gtwdaizi/comments/commentRss/43370.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/gtwdaizi/services/trackbacks/43370.html</trackback:ping><description><![CDATA[<div class=tit>&nbsp;</div>
<table style="TABLE-LAYOUT: fixed">
    <tbody>
        <tr>
            <td>
            <div class=cnt>
            <p><br>.clw文件记录了类的信息，如果classView中某个类不见了，重新生成该文件就可以了，方法：删除此文件，点击&#8220;建立类向导&#8221;，根据提示输入工程名称就可以了；</p>
            <p><br>.ncb文件记录了类的提示信息，如果类的成员函数和变量的提示不见了，重新生成该文件即可，方法同上； <br><br>.aps文件记录了资源信息，要利用现成的资源，需要修改3个文件，.rc文件，Resource.h文件和.aps文件，.aps直接删除后，进入程序，VC会自动生成。</p>
            </div>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cppblog.com/gtwdaizi/aggbug/43370.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/gtwdaizi/" target="_blank">郭天文</a> 2008-02-28 09:13 <a href="http://www.cppblog.com/gtwdaizi/articles/43370.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>CString 操作指南</title><link>http://www.cppblog.com/gtwdaizi/articles/42953.html</link><dc:creator>郭天文</dc:creator><author>郭天文</author><pubDate>Tue, 19 Feb 2008 09:30:00 GMT</pubDate><guid>http://www.cppblog.com/gtwdaizi/articles/42953.html</guid><wfw:comment>http://www.cppblog.com/gtwdaizi/comments/42953.html</wfw:comment><comments>http://www.cppblog.com/gtwdaizi/articles/42953.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/gtwdaizi/comments/commentRss/42953.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/gtwdaizi/services/trackbacks/42953.html</trackback:ping><description><![CDATA[<p>原文出处：<a href="http://www.codeproject.com/string/cstringmgmt.asp" target=_blank><u><font color=#0000ff>codeproject：CString Management</font></u></a><br><br><br>通过阅读本文你可以学习如何有效地使用 CString。<br><br>　　CString 是一种很有用的数据类型。它们很大程度上简化了MFC中的许多操作，使得MFC在做字符串操作的时候方便了很多。不管怎样，使用CString有很多特殊的技巧，特别是对于纯C背景下走出来的程序员来说有点难以学习。这篇文章就来讨论这些技巧。<br>　　使用CString可以让你对字符串的操作更加直截了当。这篇文章不是CString的完全手册，但囊括了大部分常见基本问题。<br><br>这篇文章包括以下内容： </p>
<ol>
    <li>
    <p align=left><a href="http://blog.csdn.net/pinping1314/archive/2005/04/21/357260.aspx#CString 对象的连接"><u><font color=#800080>CString 对象的连接</font></u></a> </p>
    <li><a href="http://blog.csdn.net/pinping1314/archive/2005/04/21/357260.aspx#格式化字符串"><u><font color=#800080>格式化字符串（包括 int 型转化为 CString ）</font></u></a>
    <li><a href="http://blog.csdn.net/pinping1314/archive/2005/04/21/357260.aspx#CString 型转化成 int 型"><u><font color=#800080>CString 型转化成 int 型</font></u></a>
    <li><a href="http://blog.csdn.net/pinping1314/archive/2005/04/21/357260.aspx#CString 型和 char* 类型的相互转化"><u><font color=#800080>CString 型和 char* 类型的相互转化</font></u></a><br>
    <dir>
    <li><a href="http://blog.csdn.net/pinping1314/archive/2005/04/21/357260.aspx#char* 转化为 CString"><u><font color=#800080>char* 转化成 CString</font></u></a>
    <li><a href="http://blog.csdn.net/pinping1314/archive/2005/04/21/357260.aspx#CString转化成char* 之一"><u><font color=#800080>CString 转化成 char* 之一：使用LPCTSTR强制转化</font></u></a>
    <li><a href="http://blog.csdn.net/pinping1314/archive/2005/04/21/357260.aspx#CString转化成char* 之二"><u><font color=#800080>CString 转化成 char* 之二：使用CString对象的GetBuffer方法</font></u></a>
    <li><a href="http://blog.csdn.net/pinping1314/archive/2005/04/21/357260.aspx#CString to char * 之三"><font color=#800080><u>CString 转化成 char* 之三: 和控件的接口</u></font></a></li>
    </dir>
    <li><a href="http://blog.csdn.net/pinping1314/archive/2005/04/21/357260.aspx#CString 型转化成 BSTR 型"><u><font color=#800080>CString 型转化成 BSTR 型</font></u></a>；
    <li><a href="http://blog.csdn.net/pinping1314/archive/2005/04/21/357260.aspx#BSTR 型转化成 CString 型"><u><font color=#800080>BSTR 型转化成 CString 型</font></u></a>；
    <li><a href="http://blog.csdn.net/pinping1314/archive/2005/04/21/357260.aspx#VARIANT 型转化成 CString 型"><u><font color=#800080>VARIANT 型转化成 CString 型</font></u></a>；
    <li><a href="http://blog.csdn.net/pinping1314/archive/2005/04/21/357260.aspx#载入字符串表资源"><u><font color=#800080>载入字符串表资源</font></u></a>；
    <li><a href="http://blog.csdn.net/pinping1314/archive/2005/04/21/357260.aspx#CString 和临时对象"><u><font color=#800080>CString 和临时对象</font></u></a>；
    <li><a href="http://blog.csdn.net/pinping1314/archive/2005/04/21/357260.aspx#CString的效率"><u><font color=#800080>CString 的效率</font></u></a>；
    <li><a href="http://blog.csdn.net/pinping1314/archive/2005/04/21/357260.aspx#总结"><u><font color=#800080>总结</font></u></a> </li>
</ol>
<p>下面我分别讨论。<br><br><img alt="" src="http://www.vckbase.com/document/image/paragraph.gif"> 1、<strong><a name="CString 对象的连接">CString 对象的连接</a></strong><br><br>　　能体现出 CString 类型方便性特点的一个方面就字符串的连接，使用 CString 类型，你能很方便地连接两个字符串，正如下面的例子：</p>
<pre>CString gray("Gray");CString cat("Cat");CString graycat = gray + cat;</pre>
<p>要比用下面的方法好得多：</p>
<pre>char gray[] = "Gray";char cat[] = "Cat";char * graycat = malloc(strlen(gray) + strlen(cat) + 1);strcpy(graycat, gray);strcat(graycat, cat);</pre>
<p><img alt="" src="http://www.vckbase.com/document/image/paragraph.gif"> 2、<strong><a name=格式化字符串>格式化字符串</a></strong><br><br>　　与其用 sprintf() 函数或 wsprintf() 函数来格式化一个字符串，还不如用 CString 对象的Format()方法：</p>
<pre>CString s;s.Format(_T("The total is %d"), total);</pre>
<p>　　用这种方法的好处是你不用担心用来存放格式化后数据的缓冲区是否足够大，这些工作由CString类替你完成。<br>　　格式化是一种把其它不是字符串类型的数据转化为CString类型的最常用技巧，比如，把一个整数转化成CString类型，可用如下方法：</p>
<pre>CString s;s.Format(_T("%d"), total);</pre>
<p>　　我总是对我的字符串使用_T()宏，这是为了让我的代码至少有Unicode的意识，当然，关于Unicode的话题不在这篇文章的讨论范围。_T()宏在8位字符环境下是如下定义的：</p>
<pre>#define _T(x) x // 非Unicode版本（non-Unicode version）</pre>
<p>而在Unicode环境下是如下定义的：</p>
<pre>#define _T(x) L##x // Unicode版本（Unicode version）</pre>
<p>所以在Unicode环境下，它的效果就相当于：</p>
<pre>s.Format(L"%d", total);</pre>
<p>　　如果你认为你的程序可能在Unicode的环境下运行，那么开始在意用 Unicode 编码。比如说，不要用 sizeof() 操作符来获得字符串的长度，因为在Unicode环境下就会有2倍的误差。我们可以用一些方法来隐藏Unicode的一些细节，比如在我需要获得字符长度的时候，我会用一个叫做DIM的宏，这个宏是在我的dim.h文件中定义的，我会在我写的所有程序中都包含这个文件：</p>
<pre>#define DIM(x) ( sizeof((x)) / sizeof((x)[0]) )</pre>
　　这个宏不仅可以用来解决Unicode的字符串长度的问题，也可以用在编译时定义的表格上，它可以获得表格的项数，如下：<br>
<pre>class Whatever { ... };Whatever data[] = {   { ... },    ...   { ... },};for(int i = 0; i &lt; DIM(data); i++) // 扫描表格寻找匹配项。</pre>
　　这里要提醒你的就是一定要注意那些在参数中需要真实字节数的API函数调用，如果你传递字符个数给它，它将不能正常工作。如下：
<pre>TCHAR data[20];lstrcpyn(data, longstring, sizeof(data) - 1); // WRONG!lstrcpyn(data, longstring, DIM(data) - 1); // RIGHTWriteFile(f, data, DIM(data), &amp;bytesWritten, NULL); // WRONG!WriteFile(f, data, sizeof(data), &amp;bytesWritten, NULL); // RIGHT</pre>
<p>造成以上原因是因为lstrcpyn需要一个字符个数作为参数，但是WriteFile却需要字节数作为参数。<br>同样需要注意的是有时候需要写出数据的所有内容。如果你仅仅只想写出数据的真实长度，你可能会认为你应该这样做：</p>
<pre>WriteFile(f, data, lstrlen(data), &amp;bytesWritten, NULL); // WRONG</pre>
<p>但是在Unicode环境下，它不会正常工作。正确的做法应该是这样：</p>
<pre>WriteFile(f, data, lstrlen(data) * sizeof(TCHAR), &amp;bytesWritten, NULL); // RIGHT</pre>
<p>　　因为WriteFile需要的是一个以字节为单位的长度。（可能有些人会想&#8220;在非Unicode的环境下运行这行代码，就意味着总是在做一个多余的乘1操作，这样不会降低程序的效率吗？&#8221;这种想法是多余的，你必须要了解编译器实际上做了什么，没有哪一个C或C++编译器会把这种无聊的乘1操作留在代码中。在Unicode环境下运行的时候，你也不必担心那个乘2操作会降低程序的效率，记住，这只是一个左移一位的操作而已，编译器也很乐意为你做这种替换。）<br>　　使用_T宏并不是意味着你已经创建了一个Unicode的程序，你只是创建了一个有Unicode意识的程序而已。如果你在默认的8-bit模式下编译你的程序的话，得到的将是一个普通的8-bit的应用程序（这里的8-bit指的只是8位的字符编码，并不是指8位的计算机系统）；当你在Unicode环境下编译你的程序时，你才会得到一个Unicode的程序。记住，CString 在 Unicode 环境下，里面包含的可都是16位的字符哦。<br><br><img alt="" src="http://www.vckbase.com/document/image/paragraph.gif"> 3、<strong><a name="CString 型转化成 int 型">CString 型转化成 int 型</a></strong><br><br>　　把 CString 类型的数据转化成整数类型最简单的方法就是使用标准的字符串到整数转换例程。<br>　　虽然通常你怀疑使用_atoi()函数是一个好的选择，它也很少会是一个正确的选择。如果你准备使用 Unicode 字符，你应该用_ttoi()，它在 ANSI 编码系统中被编译成_atoi()，而在 Unicode 编码系统中编译成_wtoi()。你也可以考虑使用_tcstoul()或者_tcstol()，它们都能把字符串转化成任意进制的长整数（如二进制、八进制、十进制或十六进制），不同点在于前者转化后的数据是无符号的（unsigned），而后者相反。看下面的例子：</p>
<pre>CString hex = _T("FAB");CString decimal = _T("4011");ASSERT(_tcstoul(hex, 0, 16) == _ttoi(decimal));</pre>
<p><img alt="" src="http://www.vckbase.com/document/image/paragraph.gif"> 4、<strong><a name="CString 型和 char* 类型的相互转化">CString 型和 char* 类型的相互转化</a></strong><br><br>　　这是初学者使用 CString 时最常见的问题。有了 C++ 的帮助，很多问题你不需要深入的去考虑它，直接拿来用就行了，但是如果你不能深入了解它的运行机制，又会有很多问题让你迷惑，特别是有些看起来没有问题的代码，却偏偏不能正常工作。<br>比如，你会奇怪为什么不能写向下面这样的代码呢：</p>
<pre>CString graycat = "Gray" + "Cat";</pre>
<p>或者这样：</p>
<pre>CString graycat("Gray" + "Cat");</pre>
<p>　　事实上，编译器将抱怨上面的这些尝试。为什么呢？因为针对CString 和 LPCTSTR数据类型的各种各样的组合，&#8220; +&#8221; 运算符 被定义成一个重载操作符。而不是两个 LPCTSTR 数据类型，它是底层数据类型。你不能对基本数据（如 int、char 或者 char*）类型重载 C++ 的运算符。你可以象下面这样做：</p>
<pre>CString graycat = CString("Gray") + CString("Cat");</pre>
<p>或者这样：</p>
<pre>CString graycat = CString("Gray") + "Cat";</pre>
<p>研究一番就会发现：&#8220; +&#8221;总是使用在至少有一个 CString 对象和一个 LPCSTR 的场合。<br><br>注意，编写有 Unicode 意识的代码总是一件好事，比如：</p>
<pre>CString graycat = CString(_T("Gray")) + _T("Cat");</pre>
<p>这将使得你的代码可以直接移植。<br><br><em><strong><a name="char* 转化为 CString">char* 转化为 CString</a></strong></em><br><br>　　现在你有一个 char* 类型的数据，或者说一个字符串。怎么样创建 CString 对象呢？这里有一些例子：</p>
<pre>char * p = "This is a test";</pre>
<p>或者象下面这样更具有 Unicode 意识：</p>
<pre>TCHAR * p = _T("This is a test")</pre>
<p>或</p>
<pre>LPTSTR p = _T("This is a test");</pre>
<p>你可以使用下面任意一种写法：</p>
<pre>CString s = "This is a test"; // 8-bit onlyCString s = _T("This is a test"); // Unicode-awareCString s("This is a test"); // 8-bit onlyCString s(_T("This is a test")); // Unicode-awareCString s = p;CString s(p);</pre>
<p>　　用这些方法可以轻松将常量字符串或指针转换成 CString。需要注意的是，字符的赋值总是被拷贝到 CString 对象中去的，所以你可以象下面这样操作：</p>
<pre>TCHAR * p = _T("Gray");CString s(p);p = _T("Cat");s += p;</pre>
<p>结果字符串肯定是&#8220;GrayCat&#8221;。<br><br>CString 类还有几个其它的构造函数，但是这里我们不考虑它，如果你有兴趣可以自己查看相关文档。<br><br>事实上，CString 类的构造函数比我展示的要复杂，比如：</p>
<pre>CString s = "This is a test"; </pre>
<p>　　这是很草率的编码，但是实际上它在 Unicode 环境下能编译通过。它在运行时调用构造函数的 MultiByteToWideChar 操作将 8 位字符串转换成 16 位字符串。不管怎样，如果 char * 指针是网络上传输的 8 位数据，这种转换是很有用的。<br><br><em><strong><a name="CString转化成char* 之一">CString 转化成 char* 之一</a>：</strong></em>强制类型转换为 LPCTSTR；<br><br>　　这是一种略微硬性的转换，有关&#8220;正确&#8221;的做法，人们在认识上还存在许多混乱，正确的使用方法有很多，但错误的使用方法可能与正确的使用方法一样多。<br>　　我们首先要了解 CString 是一种很特殊的 C++ 对象，它里面包含了三个值：一个指向某个数据缓冲区的指针、一个是该缓冲中有效的字符记数以及一个缓冲区长度。 有效字符数的大小可以是从0到该缓冲最大长度值减1之间的任何数（因为字符串结尾有一个NULL字符）。字符记数和缓冲区长度被巧妙隐藏。<br>　　除非你做一些特殊的操作，否则你不可能知道给CString对象分配的缓冲区的长度。这样，即使你获得了该0缓冲的地址，你也无法更改其中的内容，不能截短字符串，也 绝对没有办法加长它的内容，否则第一时间就会看到溢出。<br>　　LPCTSTR 操作符（或者更明确地说就是 TCHAR * 操作符）在 CString 类中被重载了，该操作符的定义是返回缓冲区的地址，因此，如果你需要一个指向 CString 的 字符串指针的话，可以这样做：</p>
<p>&#160;</p>
<pre>CString s("GrayCat");LPCTSTR p = s;</pre>
<p>　　它可以正确地运行。这是由C语言的强制类型转化规则实现的。当需要强制类型转化时，C++规测容许这种选择。比如，你可以将（浮点数）定义为将某个复数 （有一对浮点数）进行强制类型转换后只返回该复数的第一个浮点数（也就是其实部）。可以象下面这样：</p>
<pre>Complex c(1.2f, 4.8f);float realpart = c;</pre>
<p>如果(float)操作符定义正确的话，那么实部的的值应该是1.2。<br>　　这种强制转化适合所有这种情况，例如，任何带有 LPCTSTR 类型参数的函数都会强制执行这种转换。 于是，你可能有这样一个函数（也许在某个你买来的DLL中）：</p>
<pre>BOOL DoSomethingCool(LPCTSTR s);</pre>
<p>你象下面这样调用它：</p>
<pre>CString file("c:\\myfiles\\coolstuff")BOOL result = DoSomethingCool(file);</pre>
<p>　　它能正确运行。因为 DoSomethingCool 函数已经说明了需要一个 LPCTSTR 类型的参数，因此 LPCTSTR 被应用于该参数，在 MFC 中就是返回的串地址。<br><br>如果你要格式化字符串怎么办呢？</p>
<pre>CString graycat("GrayCat");CString s;s.Format("Mew! I love %s", graycat);</pre>
<p>　　注意由于在可变参数列表中的值（在函数说明中是以&#8220;...&#8221;表示的）并没有隐含一个强制类型转换操作符。你会得到什么结果呢？<br>　　一个令人惊讶的结果，我们得到的实际结果串是：</p>
<pre>"Mew! I love GrayCat"。</pre>
<p>　　因为 MFC 的设计者们在设计 CString 数据类型时非常小心， CString 类型表达式求值后指向了字符串，所以这里看不到任何象 Format 或 sprintf 中的强制类型转换，你仍然可以得到正确的行为。描述 CString 的附加数据实际上在 CString 名义地址之后。<br>　　有一件事情你是不能做的，那就是修改字符串。比如，你可能会尝试用&#8220;,&#8221;代替&#8220;.&#8221;（不要做这样的，如果你在乎国际化问题，你应该使用十进制转换的 National Language Support 特性，），下面是个简单的例子：</p>
<pre>CString v("1.00"); // 货币金额，两位小数LPCTSTR p = v;p[lstrlen(p) - 3] = '','';</pre>
<p>　　这时编译器会报错，因为你赋值了一个常量串。如果你做如下尝试，编译器也会错：</p>
<pre>strcat(p, "each");</pre>
<p>　　因为 strcat 的第一个参数应该是 LPTSTR 类型的数据，而你却给了一个 LPCTSTR。<br><br>　　不要试图钻这个错误消息的牛角尖，这只会使你自己陷入麻烦！<br><br>　　原因是缓冲有一个计数，它是不可存取的（它位于 CString 地址之下的一个隐藏区域），如果你改变这个串，缓冲中的字符计数不会反映所做的修改。此外，如果字符串长度恰好是该字符串物理限制的长度（梢后还会讲到这个问题），那么扩展该字符串将改写缓冲以外的任何数据，那是你无权进行写操作的内存（不对吗？），你会毁换坏不属于你的内存。这是应用程序真正的死亡处方。</p>
<p><em><strong><a name="CString转化成char* 之二">CString转化成char* 之二</a>：</strong></em>使用 CString 对象的 GetBuffer 方法；<br><br>　　如果你需要修改 CString 中的内容，它有一个特殊的方法可以使用，那就是 GetBuffer，它的作用是返回一个可写的缓冲指针。 如果你只是打算修改字符或者截短字符串，你完全可以这样做：</p>
<pre>CString s(_T("File.ext"));LPTSTR p = s.GetBuffer();LPTSTR dot = strchr(p, ''.''); // OK, should have used s.Find...if(p != NULL)*p = _T(''\0'');s.ReleaseBuffer();</pre>
<p>　　这是 GetBuffer 的第一种用法，也是最简单的一种，不用给它传递参数，它使用默认值 0，意思是：&#8220;给我这个字符串的指针，我保证不加长它&#8221;。当你调用 ReleaseBuffer 时，字符串的实际长度会被重新计算，然后存入 CString 对象中。<br>　　必须强调一点，在 GetBuffer 和 ReleaseBuffer 之间这个范围，一定不能使用你要操作的这个缓冲的 CString 对象的任何方法。因为 ReleaseBuffer 被调用之前，该 CString 对象的完整性得不到保障。研究以下代码：</p>
<pre>CString s(...);LPTSTR p = s.GetBuffer();//... 这个指针 p 发生了很多事情int n = s.GetLength(); // 很糟D!!!!! 有可能给出错误的答案!!!s.TrimRight(); // 很糟!!!!! 不能保证能正常工作!!!!s.ReleaseBuffer(); // 现在应该 OKint m = s.GetLength(); // 这个结果可以保证是正确的。s.TrimRight(); // 将正常工作。</pre>
<p>　　假设你想增加字符串的长度，你首先要知道这个字符串可能会有多长，好比是声明字符串数组的时候用：</p>
<pre>char buffer[1024];</pre>
<p>表示 1024 个字符空间足以让你做任何想做得事情。在 CString 中与之意义相等的表示法：</p>
<pre>LPTSTR p = s.GetBuffer(1024);</pre>
<p>　　调用这个函数后，你不仅获得了字符串缓冲区的指针，而且同时还获得了长度至少为 1024 个字符的空间（注意，我说的是&#8220;字符&#8221;，而不是&#8220;字节&#8221;，因为 CString 是以隐含方式感知 Unicode 的）。<br>　　同时，还应该注意的是，如果你有一个常量串指针，这个串本身的值被存储在只读内存中，如果试图存储它，即使你已经调用了 GetBuffer ，并获得一个只读内存的指针，存入操作会失败，并报告存取错误。我没有在 CString 上证明这一点，但我看到过大把的 C 程序员经常犯这个错误。<br>　　C 程序员有一个通病是分配一个固定长度的缓冲，对它进行 sprintf 操作，然后将它赋值给一个 CString：</p>
<pre>char buffer[256];sprintf(buffer, "%......", args, ...); // ... 部分省略许多细节CString s = buffer;</pre>
<p>虽然更好的形式可以这么做：</p>
<pre>CString s;s.Format(_T("%...."), args, ...);</pre>
<p>如果你的字符串长度万一超过 256 个字符的时候，不会破坏堆栈。<br><br>　　另外一个常见的错误是：既然固定大小的内存不工作，那么就采用动态分配字节，这种做法弊端更大：</p>
<pre>int len = lstrlen(parm1) + 13  lstrlen(parm2) + 10 + 100;char * buffer = new char[len];sprintf(buffer, "%s is equal to %s, valid data", parm1, parm2);CString s = buffer;......delete [] buffer;</pre>
<p>它可以能被简单地写成：</p>
<pre>CString s;s.Format(_T("%s is equal to %s, valid data"), parm1, parm2);</pre>
<p>　　需要注意 sprintf 例子都不是 Unicode 就绪的，尽管你可以使用 tsprintf 以及用 _T() 来包围格式化字符串，但是基本 思路仍然是在走弯路，这这样很容易出错。<br><br><em><strong><a name="CString to char * 之三">CString to char * 之三</a>：</strong></em>和控件的接口；<br><br>　　我们经常需要把一个 CString 的值传递给一个控件，比如，CTreeCtrl。MFC为我们提供了很多便利来重载这个操作，但是 在大多数情况下，你使用&#8220;原始&#8221;形式的更新，因此需要将墨某个串指针存储到 TVINSERTITEMSTRUCT 结构的 TVITEM 成员中。如下：</p>
<pre>TVINSERTITEMSTRUCT tvi;CString s;// ... 为s赋一些值。tvi.item.pszText = s; // Compiler yells at you here// ... 填写tvi的其他域HTREEITEM ti = c_MyTree.InsertItem(&amp;tvi);</pre>
<p>　　为什么编译器会报错呢？明明看起来很完美的用法啊！但是事实上如果你看看 TVITEM 结构的定义你就会明白，在 TVITEM 结构中 pszText&nbsp;成员的声明如下：</p>
<pre>LPTSTR pszText;int cchTextMax;</pre>
<p>　　因此，赋值不是赋给一个 LPCTSTR 类型的变量，而且编译器无法知道如何将赋值语句右边强制转换成 LPCTSTR。好吧，你说，那我就改成这样：</p>
<pre>tvi.item.pszText = (LPCTSTR)s; //编译器依然会报错。</pre>
<p>　　编译器之所以依然报错是因为你试图把一个 LPCTSTR 类型的变量赋值给一个 LPTSTR 类型的变量，这种操作在C或C++中是被禁止的。你不能用这种方法 来滥用常量指针与非常量指针概念，否则，会扰乱编译器的优化机制，使之不知如何优化你的程序。比如，如果你这么做：</p>
<pre>const int i = ...;//... do lots of stuff... = a[i]; // usage 1// ... lots more stuff... = a[i]; // usage 2</pre>
<p>　　那么，编译器会以为既然 i 是 const ，所以 usage1和usage2的值是相同的，并且它甚至能事先计算好 usage1 处的 a[i] 的地址，然后保留着在后面的 usage2 处使用，而不是重新计算。如果你按如下方式写的话：</p>
<pre>const int i = ...;int * p = &amp;i;//... do lots of stuff... = a[i]; // usage 1// ... lots more stuff(*p)++; // mess over compiler''s assumption// ... and other stuff... = a[i]; // usage 2</pre>
<p>　　编译器将认为 i 是常量，从而 a[i] 的位置也是常量，这样间接地破坏了先前的假设。因此，你的程序将会在 debug 编译模式（没有优化）和 release 编译模式（完全优化）中反映出不同的行为，这种情况可不好，所以当你试图把指向 i 的指针赋值给一个 可修改的引用时，会被编译器诊断为这是一种伪造。这就是为什么（LPCTSTR）强制类型转化不起作用的原因。<br>　　为什么不把该成员声明成 LPCTSTR 类型呢？因为这个结构被用于读写控件。当你向控件写数据时，文本指针实际上被当成 LPCTSTR，而当你从控件读数据 时，你必须有一个可写的字符串。这个结构无法区分它是用来读还是用来写。<br><br>因此，你会常常在我的代码中看到如下的用法：</p>
<pre>tvi.item.pszText = (LPTSTR)(LPCTSTR)s;</pre>
<p>　　它把 CString 强制类型转化成 LPCTSTR，也就是说先获得改字符串的地址，然后再强制类型转化成 LPTSTR，以便可以对之进行赋值操作。 注意这只有在使用 Set 或 Insert 之类的方法才有效！如果你试图获取数据，则不能这么做。<br>　　如果你打算获取存储在控件中的数据，则方法稍有不同，例如，对某个 CTreeCtrl 使用 GetItem 方法，我想获取项目的文本。我知道这些 文本的长度不会超过 MY_LIMIT，因此我可以这样写：</p>
<pre>TVITEM tvi;// ... assorted initialization of other fields of tvitvi.pszText = s.GetBuffer(MY_LIMIT);tvi.cchTextMax = MY_LIMIT;c_MyTree.GetItem(&amp;tvi);s.ReleaseBuffer();</pre>
<p>　　可以看出来，其实上面的代码对所有类型的 Set 方法都适用，但是并不需要这么做，因为所有的类 Set 方法（包括 Insert方法）不会改变字符串的内容。但是当你需要写 CString 对象时，必须保证缓冲是可写的，这正是 GetBuffer 所做的事情。再次强调： 一旦做了一次 GetBuffer 调用，那么在调用 ReleaseBuffer 之前不要对这个 CString 对象做任何操作。<br><br><img alt="" src="http://www.vckbase.com/document/image/paragraph.gif"> 5、<strong><a name="CString 型转化成 BSTR 型">CString 型转化成 BSTR 型</a></strong><br><br>　　当我们使用 ActiveX 控件编程时，经常需要用到将某个值表示成 BSTR 类型。BSTR 是一种记数字符串，Intel平台上的宽字符串（Unicode），并且 可以包含嵌入的 NULL 字符。<br><br>你可以调用 CString 对象的 AllocSysString 方法将 CString 转化成 BSTR：</p>
<pre>CString s;s = ... ; // whateverBSTR b = s.AllocSysString();</pre>
<p>　　现在指针 b 指向的就是一个新分配的 BSTR 对象，该对象是 CString 的一个拷贝，包含终结 NULL字符。现在你可以将它传递给任何需要 BSTR 的接口。通常，BSTR 由接收它的组件来释放，如果你需要自己释放 BSTR 的话，可以这么做：</p>
<pre>::SysFreeString(b);</pre>
<p>　　对于如何表示传递给 ActiveX 控件的字符串，在微软内部曾一度争论不休，最后 Visual Basic 的人占了上风，BSTR（&#8220;Basic String&#8221;的首字母缩写）就是这场争论的结果。<br><br><img alt="" src="http://www.vckbase.com/document/image/paragraph.gif"> 6、<strong><a name="BSTR 型转化成 CString 型">BSTR 型转化成 CString 型</a></strong><br><br>　　由于 BSTR 是记数 Unicode 字符串，你可以用标准转换方法来创建 8 位的 CString。实际上，这是 CString 内建的功能。在 CString 中 有特殊的构造函数可以把 ANSI 转化成 Unicode，也可以把Unicode 转化成 ANSI。你同样可以从 VARIANT 类型的变量中获得 BSTR 类型的字符串，VARIANT 类型是 由各种 COM 和 Automation (自动化)调用返回的类型。<br><br>例如，在一个ANSI程序中：</p>
<pre>BSTR b;b = ...; // whateverCString s(b == NULL ? L"" : b)</pre>
<p>　　对于单个的 BSTR 串来说，这种用法可以工作得很好，这是因为 CString 有一个特殊的构造函数以LPCWSTR（BSTR正是这种类型） 为参数，并将它转化成 ANSI 类型。专门检查是必须的，因为 BSTR 可能为空值，而 CString 的构造函数对于 NULL 值情况考虑的不是很周到，（感谢 Brian Ross 指出这一点!）。这种用法也只能处理包含 NUL 终结字符的单字符串；如果要转化含有多个 NULL 字符 串，你得额外做一些工作才行。在 CString 中内嵌的 NULL 字符通常表现不尽如人意，应该尽量避免。<br>　　根据 C/C++ 规则，如果你有一个 LPWSTR，那么它别无选择，只能和 LPCWSTR 参数匹配。<br><br>在 Unicode 模式下，它的构造函数是：</p>
<pre>CString::CString(LPCTSTR);</pre>
<p>正如上面所表示的，在 ANSI 模式下，它有一个特殊的构造函数：</p>
<pre>CString::CString(LPCWSTR); </pre>
<p>　　它会调用一个内部的函数将 Unicode 字符串转换成 ANSI 字符串。（在Unicode模式下，有一个专门的构造函数，该函数有一个参数是LPCSTR类型——一个8位 ANSI 字符串 指针，该函数将它加宽为 Unicode 的字符串！）再次强调：一定要检查 BSTR 的值是否为 NULL。<br>　　另外还有一个问题，正如上文提到的：BSTRs可以含有多个内嵌的NULL字符，但是 CString 的构造函数只能处理某个串中单个 NULL 字符。 也就是说，如果串中含有嵌入的 NUL字节，CString 将会计算出错误的串长度。你必须自己处理它。如果你看看 strcore.cpp 中的构造函数，你会发现 它们都调用了lstrlen，也就是计算字符串的长度。<br>　　注意从 Unicode 到 ANSI 的转换使用带专门参数的 ::WideCharToMultiByte，如果你不想使用这种默认的转换方式，则必须编写自己的转化代码。<br>　　如果你在 UNICODE 模式下编译代码，你可以简单地写成：<br></p>
<pre>CString convert(BSTR b){    if(b == NULL)        return CString(_T(""));    CString s(b); // in UNICODE mode    return s;}</pre>
　　如果是 ANSI 模式，则需要更复杂的过程来转换。注意这个代码使用与 ::WideCharToMultiByte 相同的参数值。所以你 只能在想要改变这些参数进行转换时使用该技术。例如，指定不同的默认字符，不同的标志集等。
<pre>CString convert(BSTR b){    CString s;    if(b == NULL)       return s; // empty for NULL BSTR#ifdef UNICODE    s = b;#else    LPSTR p = s.GetBuffer(SysStringLen(b) + 1);     ::WideCharToMultiByte(CP_ACP,            // ANSI Code Page                          0,                 // no flags                          b,                 // source widechar string                          -1,                // assume NUL-terminated                          p,                 // target buffer                          SysStringLen(b)+1, // target buffer length                          NULL,              // use system default char                          NULL);             // don''t care if default used    s.ReleaseBuffer();#endif    return s;}</pre>
　　我并不担心如果 BSTR 包含没有映射到 8 位字符集的 Unicode 字符时会发生什么，因为我指定了::WideCharToMultiByte 的最后两个参数为 NULL。这就是你可能需要改变的地方。 <br><br><img alt="" src="http://www.vckbase.com/document/image/paragraph.gif"> 7、<strong><a name="VARIANT 型转化成 CString 型">VARIANT 型转化成 CString 型</a></strong><br><br>　　事实上，我从来没有这么做过，因为我没有用 COM/OLE/ActiveX 编写过程序。但是我在microsoft.public.vc.mfc 新闻组上看到了 Robert Quirk 的一篇帖子谈到了这种转化，我觉得把他的文章包含在我的文章里是不太好的做法，所以在这里多做一些解释和演示。如果和他的文章有相孛的地方可能是我的疏忽。<br>　　VARIANT 类型经常用来给 COM 对象传递参数，或者接收从 COM 对象返回的值。你也能自己编写返回 VARIANT 类型的方法，函数返回什么类型 依赖可能（并且常常）方法的输入参数（比如，在自动化操作中，依赖与你调用哪个方法。IDispatch::Invoke 可能返回（通过其一个参数）一个 包含有BYTE、WORD、float、double、date、BSTR 等等 VARIANT 类型的结果，（详见 MSDN 上的 VARIANT 结构的定义）。在下面的例子中，假设 类型是一个BSTR的变体，也就是说在串中的值是通过 bsrtVal 来引用，其优点是在 ANSI 应用中，有一个构造函数会把 LPCWCHAR 引用的值转换为一个 CString（见 BSTR-to-CString 部分）。在 Unicode 模式中，将成为标准的 CString 构造函数，参见对缺省::WideCharToMultiByte 转换的告诫，以及你觉得是否可以接受（大多数情况下，你会满意的）。
<pre>VARIANT vaData;vaData = m_com.YourMethodHere();ASSERT(vaData.vt == VT_BSTR);CString strData(vaData.bstrVal);</pre>
<p>你还可以根据 vt 域的不同来建立更通用的转换例程。为此你可能会考虑：<br></p>
<pre>CString VariantToString(VARIANT * va){    CString s;    switch(va-&gt;vt)      { /* vt */       case VT_BSTR:          return CString(vaData-&gt;bstrVal);       case VT_BSTR | VT_BYREF:          return CString(*vaData-&gt;pbstrVal);       case VT_I4:          s.Format(_T("%d"), va-&gt;lVal);          return s;       case VT_I4 | VT_BYREF:          s.Format(_T("%d"), *va-&gt;plVal);       case VT_R8:          s.Format(_T("%f"), va-&gt;dblVal);          return s;       ... 剩下的类型转换由读者自己完成       default:          ASSERT(FALSE); // unknown VARIANT type (this ASSERT is optional)          return CString("");      } /* vt */}</pre>
<img alt="" src="http://www.vckbase.com/document/image/paragraph.gif"> 8、<strong><a name=载入字符串表资源>载入字符串表资源</a></strong><br><br>　　如果你想创建一个容易进行语言版本移植的应用程序，你就不能在你的源代码中直接包含本土语言字符串 （下面这些例子我用的语言都是英语，因为我的本土语是英语），比如下面这种写法就很糟：
<pre>CString s = "There is an error";</pre>
<p>　　你应该把你所有特定语言的字符串单独摆放（调试信息、在发布版本中不出现的信息除外）。这意味着向下面这样写比较好：</p>
<pre>s.Format(_T("%d - %s"), code, text);</pre>
<p>　　在你的程序中，文字字符串不是语言敏感的。不管怎样，你必须很小心，不要使用下面这样的串：</p>
<pre>// fmt is "Error in %s file %s"// readorwrite is "reading" or "writing"s.Format(fmt, readorwrite, filename); </pre>
<p>　　这是我的切身体会。在我的第一个国际化的应用程序中我犯了这个错误，尽管我懂德语，知道在德语的语法中动词放在句子的最后面，我们的德国方面的发行人还是苦苦的抱怨他们不得不提取那些不可思议的德语错误提示信息然后重新格式化以让它们能正常工作。比较好的办法（也是我现在使用的办法）是使用两个字符串，一个用 于读，一个用于写，在使用时加载合适的版本，使得它们对字符串参数是非敏感的。也就是说加载整个格式，而不是加载串&#8220;reading&#8221;，&#8220;writing&#8221;：</p>
<pre>// fmt is "Error in reading file %s"// "Error in writing file %s"s.Format(fmt, filename);</pre>
<p>　　一定要注意，如果你有好几个地方需要替换，你一定要保证替换后句子的结构不会出现问题，比如在英语中，可以是主语-宾语，主语-谓语，动词-宾语的结构等等。<br>　　在这里，我们并不讨论 FormatMessage，其实它比 sprintf/Format 还要有优势，但是不太容易和CString 结合使用。解决这种问题的办法就是我们按照参数出现在参数表中的位置给参数取名字，这样在你输出的时候就不会把他们的位置排错了。<br>　　接下来我们讨论我们这些独立的字符串放在什么地方。我们可以把字符串的值放入资源文件中的一个称为 STRINGTABLE 的段中。过程如下：首先使用 Visual Studio 的资源编辑器创建一个字符串，然后给每一个字符串取一个ID，一般我们给它取名字都以 IDS_开头。所以如果你有一个信息，你可以创建一个字符串资源然后取名为 IDS_READING_FILE，另外一个就取名为 IDS_WRITING_FILE。它们以下面的形式出现在你的 .rc 文件中：</p>
<pre>STRINGTABLEIDS_READING_FILE "Reading file %s"IDS_WRITING_FILE "Writing file %s"END</pre>
<p><strong>注意：</strong>这些资源都以 Unicode 的格式保存，不管你是在什么环境下编译。他们在Win9x系统上也是以Unicode 的形式存在，虽然 Win9x 不能真正处理 Unicode。<br>然后你可以这样使用这些资源：<br>// 在使用资源串表之前，程序是这样写的：<br></p>
<pre>   CString fmt;      if(...)        fmt = "Reading file %s";     else       fmt = "Writing file %s";  ...    // much later  CString s;  s.Format(fmt, filename); </pre>
// 使用资源串表之后，程序这样写：
<pre>    CString fmt;        if(...)           fmt.LoadString(IDS_READING_FILE);        else           fmt.LoadString(DS_WRITING_FILE);    ...      // much later    CString s;    s.Format(fmt, filename);</pre>
　　现在，你的代码可以移植到任何语言中去。LoadString 方法需要一个字符串资源的 ID 作为参数，然后它从 STRINGTABLE 中取出它对应的字符串，赋值给 CString 对象。 CString 对象的构造函数还有一个更加聪明的特征可以简化 STRINGTABLE 的使用。这个用法在 CString::CString 的文档中没有指出，但是在 构造函数的示例程序中使用了。（为什么这个特性没有成为正式文档的一部分，而是放在了一个例子中，我记不得了！）——【<strong>译者注</strong>：从这句话看，作者可能是CString的设计者。其实前面还有一句类似的话。说他没有对使用GetBuffer(0)获得的指针指向的地址是否可读做有效性检查 】。这个特征就是：如果你将一个字符串资源的ID强制类型转换为 LPCTSTR，将会隐含调用 LoadString。因此，下面两个构造字符串的例子具有相同的效果，而且其 ASSERT 在debug模式下不会被触发：
<pre>CString s;s.LoadString(IDS_WHATEVER);CString t( (LPCTSTR)IDS_WHATEVER );ASSERT(s == t);//不会被触发，说明s和t是相同的。</pre>
<p>　　现在，你可能会想：这怎么可能工作呢？我们怎么能把 STRINGTABLE ID 转化成一个指针呢？很简单：所有的字符串 ID 都在1~65535这个范围内，也就是说，它所有的高位都是0，而我们在程序中所使用的指针是不可能小于65535的，因为程序的低 64K 内存永远也不可能存在的，如果你试图访问0x00000000到0x0000FFFF之间的内存，将会引发一个内存越界错误。所以说1~65535的值不可能是一个内存地址，所以我们可以用这些值来作为字符串资源的ID。<br>　　我倾向于使用 MAKEINTRESOURCE 宏显式地做这种转换。我认为这样可以让代码更加易于阅读。这是个只适合在 MFC 中使用的标准宏。你要记住，大多数的方法即可以接受一个 UINT 型的参数，也可以接受一个 LPCTSTR 型的参数，这是依赖 C++ 的重载功能做到的。C++重载函数带来的 弊端就是造成所有的强制类型转化都需要显示声明。同样，你也可以给很多种结构只传递一个资源名。</p>
<pre>CString s;s.LoadString(IDS_WHATEVER);CString t( MAKEINTRESOURCE(IDS_WHATEVER));ASSERT(s == t);</pre>
<p>　　告诉你吧：我不仅只是在这里鼓吹，事实上我也是这么做的。在我的代码中，你几乎不可能找到一个字符串，当然，那些只是偶然在调试中出现的或者和语言无关的字符串除外。<br><br><img alt="" src="http://www.vckbase.com/document/image/paragraph.gif"> 9、<strong><a name="CString 和临时对象">CString 和临时对象</a></strong><br><br>　　这是出现在 microsoft.public.vc.mfc 新闻组中的一个小问题，我简单的提一下，这个问题是有个程序员需要往注册表中写入一个字符串，他写道：<br>　　我试着用 RegSetValueEx() 设置一个注册表键的值，但是它的结果总是令我困惑。当我用char[]声明一个变量时它能正常工作，但是当我用 CString 的时候，总是得到一些垃圾："&amp;Yacute;&amp;Yacute;&amp;Yacute;&amp;Yacute;...&amp;Yacute;&amp;Yacute;&amp;Yacute;&amp;Yacute;&amp;Yacute;&amp;Yacute;"为了确认是不是我的 CString 数据出了问题，我试着用 GetBuffer，然后强制转化成 char*，LPCSTR。GetBuffer 返回的值是正确的，但是当我把它赋值给 char* 时，它就变成垃圾了。以下是我的程序段：</p>
<pre>char* szName = GetName().GetBuffer(20);RegSetValueEx(hKey, "Name", 0, REG_SZ,              (CONST BYTE *) szName,             strlen (szName + 1));</pre>
<p>这个 Name 字符串的长度小于 20，所以我不认为是 GetBuffer 的参数的问题。<br><br>真让人困惑，请帮帮我。<br><br>亲爱的 Frustrated，<br><br>你犯了一个相当微妙的错误，聪明反被聪明误，正确的代码应该象下面这样：<br></p>
<pre>CString Name = GetName();RegSetValueEx(hKey, _T("Name"), 0, REG_SZ,                     (CONST BYTE *) (LPCTSTR)Name,                    (Name.GetLength() + 1) * sizeof(TCHAR));</pre>
　　为什么我写的代码能行而你写的就有问题呢？主要是因为当你调用 GetName 时返回的 CString 对象是一个临时对象。参见：《C++ Reference manual》&#167;12.2<br>　　在一些环境中，编译器有必要创建一个临时对象，这样引入临时对象是依赖于实现的。如果编译器引入的这个临时对象所属的类有构造函数的话，编译器要确保这个类的构造函数被调用。同样的，如果这个类声明有析构函数的话，也要保证这个临时对象的析构函数被调用。<br>　　编译器必须保证这个临时对象被销毁了。被销毁的确切地点依赖于实现.....这个析构函数必须在退出创建该临时对象的范围之前被调用。<br>　　大部分的编译器是这样设计的：在临时对象被创建的代码的下一个执行步骤处隐含调用这个临时对象的析构函数，实现起来，一般都是在下一个分号处。因此，这个 CString 对象在 GetBuffer 调用之后就被析构了（顺便提一句，你没有理由给 GetBuffer 函数传递一个参数，而且没有使用ReleaseBuffer 也是不对的）。所以 GetBuffer 本来返回的是指向这个临时对象中字符串的地址的指针，但是当这个临时对象被析构后，这块内存就被释放了。然后 MFC 的调试内存分配器会重新为这块内存全部填上 0xDD，显示出来刚好就是&#8220;&amp;Yacute;&#8221;符号。在这个时候你向注册表中写数据，字符串的内容当然全被破坏了。<br>　　我们不应该立即把这个临时对象转化成 char* 类型，应该先把它保存到一个 CString 对象中，这意味着把临时对象复制了一份，所以当临时的 CString 对象被析构了之后，这个 CString 对象中的值依然保存着。这个时候再向注册表中写数据就没有问题了。<br>　　此外，我的代码是具有 Unicode 意识的。那个操作注册表的函数需要一个字节大小，使用lstrlen(Name+1) 得到的实际结果对于 Unicode 字符来说比 ANSI 字符要小一半，而且它也不能从这个字符串的第二个字符起开始计算，也许你的本意是 lstrlen(Name) + 1（OK，我承认，我也犯了同样的错误！）。不论如何，在 Unicode 模式下，所有的字符都是2个字节大小，我们需要处理这个问题。微软的文档令人惊讶地对此保持缄默：REG_SZ 的值究竟是以字节计算还是以字符计算呢？我们假设它指的是以字节为单位计算，你需要对你的代码做一些修改来计算这个字符串所含有的字节大小。<br><br><img alt="" src="http://www.vckbase.com/document/image/paragraph.gif"> 10、<strong><a name=CString的效率>CString 的效率</a></strong><br><br>　　CString 的一个问题是它确实掩藏了一些低效率的东西。从另外一个方面讲，它也确实可以被实现得更加高效，你可能会说下面的代码：
<pre>CString s = SomeCString1;s += SomeCString2;s += SomeCString3;s += ",";s += SomeCString4;</pre>
<p>比起下面的代码来，效率要低多了：</p>
<pre>char s[1024];lstrcpy(s, SomeString1);lstrcat(s, SomeString2);lstrcat(s, SomeString 3);lstrcat(s, ",");lstrcat(s, SomeString4);</pre>
<p>　　总之，你可能会想，首先，它为 SomeCString1 分配一块内存，然后把 SomeCString1 复制到里面，然后发现它要做一个连接，则重新分配一块新的足够大的内存，大到能够放下当前的字符串加上SomeCString2，把内容复制到这块内存 ，然后把 SomeCString2 连接到后面，然后释放第一块内存，并把指针重新指向新内存。然后为每个字符串重复这个过程。把这 4 个字符串连接起来效率多低啊。事实上，在很多情况下根本就不需要复制源字符串（在 += 操作符左边的字符串）。<br>　　在 VC++6.0 中，Release 模式下，所有的 CString 中的缓存都是按预定义量子分配的。所谓量子，即确定为 64、128、256 或者 512 字节。这意味着除非字符串非常长，连接字符串的操作实际上就是 strcat 经过优化后的版本（因为它知道本地的字符串应该在什么地方结束，所以不需要寻找字符串的结尾；只需要把内存中的数据拷贝到指定的地方即可）加上重新计算字符串的长度。所以它的执行效率和纯 C 的代码是一样的，但是它更容易写、更容易维护和更容易理解。<br>　　如果你还是不能确定究竟发生了怎样的过程，请看看 CString 的源代码，strcore.cpp，在你 vc98的安装目录的 mfc\src 子目录中。看看 ConcatInPlace 方法，它被在所有的 += 操作符中调用。<br><br>啊哈！难道 CString 真的这么"高效"吗？比如，如果我创建</p>
<pre>CString cat("Mew!");</pre>
<p>　　然后我并不是得到了一个高效的、精简的5个字节大小的缓冲区（4个字符加一个结束字符），系统将给我分配64个字节，而其中59个字节都被浪费了。<br>　　如果你也是这么想的话，那么就请准备好接受再教育吧。可能在某个地方某个人给你讲过尽量使用少的空间是件好事情。不错，这种说法的确正确，但是他忽略了事实中一个很重要的方面。<br>　　如果你编写的是运行在16K EPROMs下的嵌入式程序的话，你有理由尽量少使用空间，在这种环境下，它能使你的程序更健壮。但是在 500MHz, 256MB的机器上写 Windows 程序，如果你还是这么做，它只会比你认为的&#8220;低效&#8221;的代码运行得更糟。<br>　　举例来说。字符串的大小被认为是影响效率的首要因素，使字符串尽可能小可以提高效率，反之则降低效率，这是大家一贯的想法。但是这种想法是不对的，精确的内存分配的后果要在程序运行了好几个小时后才能体现得出来，那时，程序的堆中将充满小片的内存，它们太小以至于不能用来做任何事，但是他们增加了你程序的内存用量，增加了内存页面交换的次数，当页面交换的次数增加到系统能够忍受的上限，系统则会为你的程序分配更多的页面，直到你的程序占用了所有的可用内存。由此可见，虽然内存碎片是决定效率的次要因素，但正是这些因素实际控制了系统的行为，最终，它损害了系统的可靠性，这是令人无法接受的。<br>　　记住，在 debug 模式下，内存往往是精确分配的，这是为了更好的排错。<br>　　假设你的应用程序通常需要连续工作好几个月。比如，我常打开 VC++，Word，PowerPoint，Frontpage，Outlook Express，Fort&#233; Agent，Internet Explorer和其它的一些程序，而且通常不关闭它们。我曾经夜以继日地连续用 PowerPoint 工作了好几天（反之，如果你不幸不得不使用像 Adobe FrameMaker 这样的程序的话，你将会体会到可靠性的重要；这个程序机会每天都要崩溃4~6次，每次都是因为用完了所有的空间并填满我所有的交换页面）。所以精确内存分配是不可取的，它会危及到系统的可靠性，并引起应用程序崩溃。<br>　　按量子的倍数为字符串分配内存，内存分配器就可以回收用过的内存块，通常这些回收的内存块马上就可以被其它的 CString 对象重新用到，这样就可以保证碎片最少。分配器的功能加强了，应用程序用到的内存就能尽可能保持最小，这样的程序就可以运行几个星期或几个月而不出现问题。<br>　　题外话：很多年以前，我们在 CMU 写一个交互式系统的时候，一些对内存分配器的研究显示出它往往产生很多内存碎片。Jim Mitchell，现在他在 Sun Microsystems 工作，那时侯他创造了一种内存分配器，它保留了一个内存分配状况的运行时统计表，这种技术和当时的主流分配器所用的技术都不同，且较为领先。当一个内存块需要被分割得比某一个值小的话，他并不分割它，因此可以避免产生太多小到什么事都干不了的内存碎片。事实上他在内存分配器中使用了一个浮动指针，他认为：与其让指令做长时间的存取内存操作，还不如简单的忽略那些太小的内存块而只做一些浮动指针的操作。（His observation was that the long-term saving in instructions by not having to ignore unusable small storage chunks far and away exceeded the additional cost of doing a few floating point operations on an allocation operation.）他是对的。<br>　　永远不要认为所谓的&#8220;最优化&#8221;是建立在每一行代码都高速且节省内存的基础上的，事实上，高速且节省内存应该是在一个应用程序的整体水平上考虑的。在软件的整体水平上，只使用最小内存的字符串分配策略可能是最糟糕的一种方法。<br>　　如果你认为优化是你在每一行代码上做的那些努力的话，你应该想一想：在每一行代码中做的优化很少能真正起作用。你可以看我的另一篇关于优化问题的文章《Your Worst Enemy for some thought-provoking ideas》。<br>　　记住，+= 运算符只是一种特例，如果你写成下面这样：</p>
<pre>CString s = SomeCString1 + SomeCString2 + SomeCString3 + "," + SomeCString4;</pre>
<p>则每一个 + 的应用会造成一个新的字符串被创建和一次复制操作。<br><br><img alt="" src="http://www.vckbase.com/document/image/paragraph.gif"> <strong><a name=总结>总结</a></strong><br><br>　　以上是使用 CString 的一些技巧。我每天写程序的时候都会用到这些。CString 并不是一种很难使用的类，但是 MFC 没有很明显的指出这些特征，需要你自己去探索、去发现。<br></p>
<img src ="http://www.cppblog.com/gtwdaizi/aggbug/42953.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/gtwdaizi/" target="_blank">郭天文</a> 2008-02-19 17:30 <a href="http://www.cppblog.com/gtwdaizi/articles/42953.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>音频编解码标准</title><link>http://www.cppblog.com/gtwdaizi/articles/41884.html</link><dc:creator>郭天文</dc:creator><author>郭天文</author><pubDate>Fri, 25 Jan 2008 05:29:00 GMT</pubDate><guid>http://www.cppblog.com/gtwdaizi/articles/41884.html</guid><wfw:comment>http://www.cppblog.com/gtwdaizi/comments/41884.html</wfw:comment><comments>http://www.cppblog.com/gtwdaizi/articles/41884.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/gtwdaizi/comments/commentRss/41884.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/gtwdaizi/services/trackbacks/41884.html</trackback:ping><description><![CDATA[<dd>
<div><br><span style="COLOR: red">PCMU(G.711U)<br></span>类型：Audio<br>制定者：ITU-T<br>所需频宽：64Kbps(90.4)<br>特性：PCMU和PCMA都能提供较好的语音质量，但是它们占用的带宽较高，需要64kbps。<br>优点：语音质量优<br>缺点：占用的带宽较高<br>应用领域：voip<br>版税方式：Free<br>备注：PCMU and PCMA都能够达到CD音质，但是它们消耗的带宽也最多(64kbps)。如果网络带宽比较低，可以选用低比特速率的编码方法，如G.723或G.729，这两种编码的方法也能达到传统长途电话的音质，但是需要很少的带宽（G723需要5.3/6.3kbps，G729需要8kbps）。如果带宽足够并且需要更好的语音质量，就使用PCMU 和 PCMA，甚至可以使用宽带的编码方法G722(64kbps)，这可以提供有高保真度的音质。<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; &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; <br><br><span style="COLOR: red">PCMA(G.711A)<br></span>类型：Audio<br>制定者：ITU-T<br>所需频宽：64Kbps(90.4)<br>特性：PCMU和PCMA都能提供较好的语音质量，但是它们占用的带宽较高，需要64kbps。<br>优点：语音质量优<br>缺点：占用的带宽较高<br>应用领域：voip<br>版税方式：Free<br>备注：PCMU and PCMA都能够达到CD音质，但是它们消耗的带宽也最多(64kbps)。如果网络带宽比较低，可以选用低比特速率的编码方法，如G.723或G.729，这两种编码的方法也能达到传统长途电话的音质，但是需要很少的带宽（G723需要5.3/6.3kbps，G729需要8kbps）。如果带宽足够并且需要更好的语音质量，就使用PCMU 和 PCMA，甚至可以使用宽带的编码方法G722(64kbps)，这可以提供有高保真度的音质。</div>
<dt>&nbsp;
<dd>
<div><br><span style="COLOR: red">ADPCM(自适应差分PCM)<br></span>类型：Audio<br>制定者：ITU-T<br>所需频宽：32Kbps<br>特性：ADPCM(adaptive difference pulse code modulation)综合了APCM的自适应特性和DPCM系统的差分特性，是一种性能比较好的波形编码。它的核心想法是：<br>&nbsp; &nbsp;&nbsp; &nbsp; ①利用自适应的思想改变量化阶的大小，即使用小的量化阶(step-size)去编码小的差值，使用大的量化阶去编码大的差值；<br>&nbsp; &nbsp;&nbsp; &nbsp; ②使用过去的样本值估算下一个输入样本的预测值，使实际样本值和预测值之间的差值总是最小。<br>优点：算法复杂度低，压缩比小（CD音质&gt;400kbps），编解码延时最短（相对其它技术）<br>缺点：声音质量一般<br>应用领域：voip<br>版税方式：Free<br>备注：ADPCM (ADPCM Adaptive Differential Pulse Code Modulation), 是一种针对 16bit (或者更高?) 声音波形数据的一种有损压缩算法, 它将声音流中每次采样的 16bit 数据以 4bit 存储, 所以压缩比 1:4. 而压缩/解压缩算法非常的简单, 所以是一种低空间消耗,高质量声音获得的好途径。<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; &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;<br><br><span style="COLOR: red">LPC(Linear Predictive Coding，线性预测编码)<br></span>类型：Audio<br>制定者： <br>所需频宽：2Kbps-4.8Kbps<br>特性：压缩比大，计算量大，音质不高，廉价<br>优点：压缩比大,廉价<br>缺点：计算量大，语音质量不是很好，自然度较低<br>应用领域：voip<br>版税方式：Free<br>备注：参数编码又称为声源编码，是将信源信号在频率域或其它正交变换域提取特征参数，并将其变换成数字代码进行传输。译码为其反过程，将收到的数字序列经变换恢复特征参量，再根据特征参量重建语音信号。具体说，参数编码是通过对语音信号特征参数的提取和编码，力图使重建语音信号具有尽可能高的准确性，但重建信号的波形同原语音信号的波形可能会有相当大的差别。如：线性预测编码（LPC）及其它各种改进型都属于参数编码。该编码比特率可压缩到2Kbit/s-4.8Kbit/s，甚至更低，但语音质量只能达到中等，特别是自然度较低。</div>
<dt>&nbsp;
<dd>
<div><br><span style="COLOR: red">CELP(Code Excited Linear Prediction，码激励线性预测编码)<br></span>类型：Audio<br>制定者：欧洲通信标准协会（ETSI）<br>所需频宽：4～16Kbps的速率<br>特性：改善语音的质量：<br>&nbsp; &nbsp;&nbsp; &nbsp; ① 对误差信号进行感觉加权，利用人类听觉的掩蔽特性来提高语音的主观质量；<br>&nbsp; &nbsp;&nbsp; &nbsp; ②用分数延迟改进基音预测，使浊音的表达更为准确，尤其改善了女性语音的质量；<br>&nbsp; &nbsp;&nbsp; &nbsp; ③ 使用修正的MSPE准则来寻找 &#8220;最佳&#8221;的延迟，使得基音周期延迟的外形更为平滑；<br>&nbsp; &nbsp;&nbsp; &nbsp; ④根据长时预测的效率，调整随机激励矢量的大小，提高语音的主观质量；&nbsp; &nbsp;&nbsp; &nbsp; ⑤ 使用基于信道错误率估计的自适应平滑器，在信道误码率较高的情况下也能合成自然度较高的语音。<br>&nbsp; &nbsp;&nbsp; &nbsp; 结论：<br>&nbsp; &nbsp;&nbsp; &nbsp; ① CELP算法在低速率编码环境下可以得到令人满意的压缩效果；<br>&nbsp; &nbsp;&nbsp; &nbsp; ②使用快速算法，可以有效地降低CELP算法的复杂度，使它完全可以实时地实现；<br>&nbsp; &nbsp;&nbsp; &nbsp; ③CELP可以成功地对各种不同类型的语音信号进行编码，这种适应性对于真实环境，尤其是背景噪声存在时更为重要。<br>优点：用很低的带宽提供了较清晰的语音<br>缺点：<br>应用领域：voip<br>版税方式：Free<br>备注：1999年欧洲通信标准协会（ETSI）推出了基于码激励线性预测编码（CELP）的第三代移动通信语音编码标准自适应多速率语音编码器（AMR），其中最低速率为4.75kb/s，达到通信质量。CELP 码激励线性预测编码是Code Excited Linear Prediction的缩写。CELP是近10年来最成功的语音编码算法。<br>&nbsp; &nbsp;&nbsp; &nbsp; CELP语音编码算法用线性预测提取声道参数，用一个包含许多典型的激励矢量的码本作为激励参数，每次编码时都在这个码本中搜索一个最佳的激励矢量，这个激励矢量的编码值就是这个序列的码本中的序号。<br>&nbsp; &nbsp;&nbsp; &nbsp; CELP已经被许多语音编码标准所采用，美国联邦标准FS1016就是采用CELP的编码方法，主要用于高质量的窄带语音保密通信。CELP (Code-Excited Linear Prediction) 这是一个简化的 LPC 算法，以其低比特率著称 (4800-9600Kbps)，具有很清晰的语音品质和很高的背景噪音免疫性。CELP是一种在中低速率上广泛使用的语音压缩编码方案。<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; &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; <br><br><span style="COLOR: red">G.711<br></span>类型：Audio<br>制定者：ITU-T<br>所需频宽：64Kbps<br>特性：算法复杂度小，音质一般<br>优点：算法复杂度低，压缩比小（CD音质&gt;400kbps），编解码延时最短（相对其它技术）<br>缺点：占用的带宽较高<br>应用领域：voip<br>版税方式：Free<br>备注：70年代CCITT公布的G.711 64kb/s脉冲编码调制PCM。<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; &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;<br><br><span style="COLOR: red">G.721</span><br>类型：Audio<br>制定者：ITU-T<br>所需频宽：32Kbps<br>特性：相对于PCMA和PCMU，其压缩比较高，可以提供2：1的压缩比。<br>优点：压缩比大<br>缺点：声音质量一般<br>应用领域：voip<br>版税方式：Free<br>备注：子带ADPCM（SB-ADPCM）技术。G.721标准是一个代码转换系统。它使用ADPCM转换技术，实现64 kb/s A律或&#956;律PCM速率和32 kb/s速率之间的相互转换。</div>
<dt>&nbsp;
<dd>
<div><br><span style="COLOR: red">G.722</span><br>类型：Audio<br>制定者：ITU-T<br>所需频宽：64Kbps<br>特性：G722能提供高保真的语音质量<br>优点：音质好<br>缺点：带宽要求高<br>应用领域：voip<br>版税方式：Free<br>备注：子带ADPCM（SB-ADPCM）技术<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; &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;<br><br><span style="COLOR: red">G.723(低码率语音编码算法)<br></span>类型：Audio<br>制定者：ITU-T<br>所需频宽：5.3Kbps/6.3Kbps<br>特性：语音质量接近良，带宽要求低，高效实现，便于多路扩展，可利用C5402片内16kRAM实现53coder。达到ITU-TG723要求的语音质量，性能稳定。可用于IP电话语音信源编码或高效语音压缩存储。<br>优点：码率低，带宽要求较小。并达到ITU-TG723要求的语音质量，性能稳定。<br>缺点：声音质量一般<br>应用领域：voip<br>版税方式：Free<br>备注：G.723语音编码器是一种用于多媒体通信，编码速率为5.3kbits/s和6.3kbit/s的双码率编码方案。G.723标准是国际电信联盟（ITU）制定的多媒体通信标准中的一个组成部分，可以应用于IP电话等系统中。其中，5.3kbits/s码率编码器采用多脉冲最大似然量化技术（MP－MLQ），6.3kbits/s码率编码器采用代数码激励线性预测技术。<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; &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; <br><br><span style="COLOR: red">G.723.1(双速率语音编码算法)<br></span>类型：Audio<br>制定者：ITU-T<br>所需频宽：5.3Kbps(22.9)<br>特性：能够对音乐和其他音频信号进行压缩和解压缩，但它对语音信号来说是最优的。G.723.1采用了执行不连续传输的静音压缩，这就意味着在静音期间的比特流中加入了人为的噪声。除了预留带宽之外，这种技术使发信机的调制解调器保持连续工作，并且避免了载波信号的时通时断。<br>优点：码率低，带宽要求较小。并达到ITU-TG723要求的语音质量，性能稳定,避免了载波信号的时通时断。<br>缺点：语音质量一般<br>应用领域：voip<br>版税方式：Free<br>备注：G.723.1算法是 ITU-T建议的应用于低速率多媒体服务中语音或其它音频信号的压缩算法，其目标应用系统包括H.323、H.324等多媒体通信系统 。目前该算法已成为IP电话系统中的必选算法之一。<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; &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; <br><br><span style="COLOR: red">G.728<br></span>类型：Audio<br>制定者：ITU-T<br>所需频宽：16Kbps/8Kbps<br>特性：用于IP电话、卫星通信、语音存储等多个领域。G.728是一种低时延编码器，但它比其它的编码器都复杂，这是因为在编码器中必须重复做50阶LPC分析。G.728还采用了自适应后置滤波器来提高其性能。<br>优点：后向自适应，采用自适应后置滤波器来提高其性能<br>缺点：比其它的编码器都复杂<br>应用领域：voip<br>版税方式：Free<br>备注：G.728 16kb/s短延时码本激励线性预测编码（LD-CELP）。1996年ITU公布了G.728 8kb/s的CS－ACELP算法，可以用于IP电话、卫星通信、语音存储等多个领域。16 kbps G.728低时延码激励线性预测。 <br>&nbsp; &nbsp;&nbsp; &nbsp; G.728是低比特线性预测合成分析编码器（G.729和G.723.1）和后向ADPCM编码器的混合体。G.728是LD-CELP编码器，它一次只处理5个样点。对于低速率（56~128 kbps）的综合业务数字网（ISDN）可视电话，G.728是一种建议采用的语音编码器。由于其后向自适应特性，因此G.728是一种低时延编码器，但它比其它的编码器都复杂，这是因为在编码器中必须重复做50阶LPC分析。G.728还采用了自适应后置滤波器来提高其性能。</div>
<dt>&nbsp;
<dd>
<div><br><span style="COLOR: red">G.729<br></span>类型：Audio<br>制定者：ITU-T<br>所需频宽：8Kbps<br>特性：在良好的信道条件下要达到长话质量，在有随机比特误码、发生帧丢失和多次转接等情况下要有很好的稳健性等。这种语音压缩算法可以应用在很广泛的领域中，包括ＩＰ电话、无线通信、数字卫星系统和数字专用线路。<br>&nbsp; &nbsp;&nbsp; &nbsp; G.729算法采用&#8220;共轭结构代数码本激励线性预测编码方案&#8221;（CS-ACELP）算法。这种算法综合了波形编码和参数编码的优点，以自适应预测编码技术为基础，采用了矢量量化、合成分析和感觉加权等技术。<br>&nbsp; &nbsp;&nbsp; &nbsp; G.729编码器是为低时延应用设计的，它的帧长只有10ms，处理时延也是10ms，再加上5ms的前视，这就使得G.729产生的点到点的时延为25ms，比特率为8 kbps。<br>优点：语音质量良，应用领域很广泛，采用了矢量量化、合成分析和感觉加权，提供了对帧丢失和分组丢失的隐藏处理机制<br>缺点：在处理随机比特错误方面性能不好。<br>应用领域：voip<br>版税方式：Free<br>备注：国际电信联盟（ITU-T）于1995年11月正式通过了G.729。 ITU-T建议G.729也被称作&#8220;共轭结构代数码本激励线性预测编码方案&#8221;(CS-ACELP)，它是当前较新的一种语音压缩标准。G.729是由美国、法国、日本和加拿大的几家著名国际电信实体联合开发的。<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; &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;<br><br><span style="COLOR: red">G.729A<br></span>类型：Audio<br>制定者：ITU-T<br>所需频宽：8Kbps(34.4)<br>特性：复杂性较G.729低，性能较G.729差。<br>优点：语音质量良，降低了计算的复杂度以便于实时实现，提供了对帧丢失和分组丢失的隐藏处理机制<br>缺点：性能较G.729差<br>应用领域：voip<br>版税方式：Free<br>备注：96年ITU-T又制定了G.729的简化方案G.729A，主要降低了计算的复杂度以便于实时实现，因此目前使用的都是G.729A。<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; &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; <br><br><span style="COLOR: red">GIPS<br></span>类型：Audio<br>制定者：瑞典Global IP Sound公司<br>所需频宽：<br>特性：GIPS技术可根据带宽状况自动调节编码码率，提供低码率高质量的音频。GIPS的核心技术（网络自适应算法，丢包补偿算法和回声消除算法）可很好地解决语音延迟与回声问题，带来完美音质，提供比电话还清晰的语音通话效果。<br>优点：很好地解决语音延迟与回声问题，带来完美音质，提供比电话还清晰的语音通话效果<br>缺点： 不是Free<br>应用领域：voip<br>版税方式：每年支付一笔使用权费用<br>备注：GIPS音频技术是由来自瑞典的全球顶尖的语音处理高科技公司--"GLOBAL IP SOUND"提供的专用于互联网的语音压缩引擎系统。GIPS技术可根据带宽状况自动调节编码码率，提供低码率高质量的音频。GIPS的核心技术（网络自适应算法，丢包补偿算法和回声消除算法）可很好地解决语音延迟与回声问题，带来完美音质，提供比电话还清晰的语音通话效果。<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; &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; <br><br><span style="COLOR: red">Apt-X</span><br>类型：Audio<br>制定者：Audio Processing Technology 公司<br>所需频宽：10Hz to 22.5 kHz，56kbit/s to 576 kbit/s(16 bit 7.5 kHz mono to 24-bit, 22.5kHz stereo)<br>特性：主要用于专业音频领域，提供高品质的音频。其特点是：<br>&nbsp; &nbsp;&nbsp; &nbsp; ①采用4:1:4的压缩与放大方案；<br>&nbsp; &nbsp;&nbsp; &nbsp; ②硬件低复杂度；<br>&nbsp; &nbsp;&nbsp; &nbsp; ③极低的编码延迟；<br>&nbsp; &nbsp;&nbsp; &nbsp; ④由单芯片实现；<br>&nbsp; &nbsp;&nbsp; &nbsp; ⑤单声道或立体声编解码；<br>&nbsp; &nbsp;&nbsp; &nbsp; ⑥只需单设备即可实现22.5kHz的双通道立体声；<br>&nbsp; &nbsp;&nbsp; &nbsp; ⑦高达48kHz的采样频率；<br>&nbsp; &nbsp;&nbsp; &nbsp; ⑧容错性好；<br>&nbsp; &nbsp;&nbsp; &nbsp; ⑨完整的AUTOSYNC&#8482;编解码同步方案；<br>&nbsp; &nbsp;&nbsp; &nbsp; ⑩低功率消耗<br>优点：高品质的音频，硬件复杂度低，设备要求低<br>缺点：不是Free<br>应用领域：voip<br>版税方式：一次性付费<br>备注：子带ADPCM（SB-ADPCM）技术</div>
<dt>&nbsp;
<dd>
<div><br><span style="COLOR: red">NICAM(Near Instantaneous Companded Audio Multiplex 准瞬时压扩音频复用)<br></span>类型：Audio<br>制定者：英国BBC广播公司<br>所需频宽：728Kbps<br>特性：应用范围及其广泛，可用它进行立体声或双语广播<br>优点：应用范围及其广泛，信噪比高，动态范围宽、音质同CD相媲美，故名丽音，因此NICAM又称为丽音<br>缺点：不是Free，频宽要求高<br>应用领域：voip<br>版税方式：一次性付费<br>备注：NICAM也称丽音，它是英文Near-Instantaneously Companded Audio Multiplex的缩写，其含义为准瞬时压扩音频复用，是由英国BBC广播公司开发研究成功的。<br>&nbsp; &nbsp;&nbsp; &nbsp; 通俗地说NICAM技术实际上就是双声道数字声技术，其应用范围及其广泛，最典型的应用便是电视广播附加双声道数字声技术，利用它进行立体声或双语广播，以充分利用电视频道的频谱资源。这是在常规电视广播的基础上无需增加许多投资就可以实现的。在进行立体声广播时，它提高了音频的信号质量，使其接近CD的质量。而且还可以利用NICAM技术进行高速数据广播及其他数据传输的增殖服务，这在当今的信息化社会中似乎就显得尤为重要了！<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; &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; <br><br><span style="COLOR: red">MPEG-1 audio layer 1</span><br>类型：Audio<br>制定者：MPEG<br>所需频宽：384kbps（压缩4倍）<br>特性：编码简单，用于数字盒式录音磁带，2声道，VCD中使用的音频压缩方案就是MPEG-1层Ⅰ。<br>优点：压缩方式相对时域压缩技术而言要复杂得多，同时编码效率、声音质量也大幅提高，编码延时相应增加。可以达到&#8220;完全透明&#8221;的声音质量（EBU音质标准）<br>缺点：频宽要求较高<br>应用领域：voip<br>版税方式：Free<br>备注：MPEG-1声音压缩编码是国际上第一个高保真声音数据压缩的国际标准，它分为三个层次：<br>--层1(Layer 1)：编码简单，用于数字盒式录音磁带<br>--层2(Layer 2)：算法复杂度中等，用于数字音频广播(DAB)和VCD等<br>--层3(Layer 3)：编码复杂，用于互联网上的高质量声音的传输，如MP3音乐压缩10倍<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; &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; <br><br><span style="COLOR: red">MUSICAM(MPEG-1 audio layer 2,即MP2)</span><br>类型：Audio<br>制定者：MPEG<br>所需频宽：256～192kbps（压缩6～8倍）<br>特性：算法复杂度中等，用于数字音频广播(DAB)和VCD等，2声道，而MUSICAM由于其适当的复杂程度和优秀的声音质量，在数字演播室、DAB、DVB等数字节目的制作、交换、存储、传送中得到广泛应用。<br>优点：压缩方式相对时域压缩技术而言要复杂得多，同时编码效率、声音质量也大幅提高，编码延时相应增加。可以达到&#8220;完全透明&#8221;的声音质量（EBU音质标准）<br>缺点：<br>应用领域：voip<br>版税方式：Free<br>备注：同MPEG-1 audio layer 1<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; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br><br><span style="COLOR: red">MP3(MPEG-1 audio layer 3)<br></span>类型：Audio<br>制定者：MPEG<br>所需频宽：128～112kbps（压缩10～12倍）<br>特性：编码复杂，用于互联网上的高质量声音的传输，如MP3音乐压缩10倍，2声道。MP3是在综合MUSICAM和ASPEC的优点的基础上提出的混合压缩技术，在当时的技术条件下，MP3的复杂度显得相对较高，编码不利于实时，但由于MP3在低码率条件下高水准的声音质量，使得它成为软解压及网络广播的宠儿。<br>优点：压缩比高，适合用于互联网上的传播<br>缺点：MP3在128KBitrate及以下时，会出现明显的高频丢失<br>应用领域：voip<br>版税方式：Free<br>备注：同MPEG-1 audio layer 1<br><br><span style="COLOR: red">MPEG-2 audio layer</span><br>类型：Audio<br>制定者：MPEG<br>所需频宽：与MPEG-1层1，层2，层3相同<br>特性：MPEG-2的声音压缩编码采用与MPEG-1声音相同的编译码器，层1, 层2和层3的结构也相同，但它能支持5.1声道和7.1声道的环绕立体声。<br>优点：支持5.1声道和7.1声道的环绕立体声<br>缺点：<br>应用领域：voip<br>版税方式：按个收取<br>备注：MPEG-2的声音压缩编码采用与MPEG-1声音相同的编译码器，层1, 层2和层3的结构也相同，但它能支持5.1声道和7.1声道的环绕立体声。</div>
<dt>&nbsp;
<dd>
<div><br><span style="COLOR: red">AAC(Advanced Audio Coding，先进音频编码)<br></span>类型：Audio<br>制定者：MPEG<br>所需频宽：96-128 kbps<br>特性：AAC可以支持1到48路之间任意数目的音频声道组合、包括15路低频效果声道、配音/多语音声道，以及15路数据。它可同时传送16套节目，每套节目的音频及数据结构可任意规定。<br>&nbsp; &nbsp;&nbsp; &nbsp; AAC主要可能的应用范围集中在因特网网络传播、数字音频广播，包括卫星直播和数字AM、以及数字电视及影院系统等方面。AAC使用了一种非常灵活的熵编码核心去传输编码频谱数据。具有48 个主要音频通道，16 个低频增强通道，16 个集成数据流, 16 个配音，16 种编排。<br>优点：支持多种音频声道组合，提供优质的音质<br>缺点： <br>应用领域：voip<br>版税方式：一次性收费<br>备注：AAC于1997年形成国际标准ISO 13818-7。先进音频编码（Advanced Audio Coding--AAC）开发成功，成为继MPEG-2音频标准（ISO/IEC13818-3）之后的新一代音频压缩标准。<br>&nbsp; &nbsp;&nbsp; &nbsp; 在MPEG-2制订的早期，本来是想将其音频编码部分保持与MPEG-1兼容的。但后来为了适应演播电视的要求而将其定义成为一个可以获得更高质量的多声道音频标准。理所当然地，这个标准是不兼容MPEG-1的，因此被称为MPEG-2 AAC。换句话说，从表面上看，要制作和播放AAC，都需要使用与MP3完全不同的工具。<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; &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;<br><br><span style="COLOR: red">Dolby AC-3<br></span>类型：Audio<br>制定者：美国杜比公司<br>所需频宽：64kbps<br>特性：提供的环绕立体声系统由5个全频带声道加一个超低音声道组成，6个声道的信息在制作和还原过程中全部数字化，信息损失很少，细节丰富，具有真正的立体声效果，在数字电视、DVD和家庭影院中广泛使用。<br>优点：环绕立体声，信息损失很少，细节丰富，具有真正的立体声效果<br>缺点：<br>应用领域：voip<br>版税方式：按个收取<br>备注：杜比数字AC-3（Dolby Digital AC-3）：美国杜比公司开发的多声道全频带声音编码系统，它提供的环绕立体声系统由5个全频带声道加一个超低音声道组成，6个声道的信息在制作和还原过程中全部数字化，信息损失很少，细节丰富，具有真正的立体声效果，在数字电视、DVD和家庭影院中广泛使用。<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; &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;<br><br><span style="COLOR: red">ASPEC（Audio Spectral Perceptual Entropy Coding）<br></span>类型：Audio<br>制定者：AT&amp;T<br>所需频宽：64kps<br>特性：音频质量获得显著改善，不过计算复杂度也大大提高，而且在回响、低码率时声音质量严重下降。<br>优点：音频质量获得显著改善<br>缺点：计算复杂度的提高。块边界影响、预计算复杂度的提高。回响、低码率时声音质量严重下降<br>应用领域：voip<br>版税方式：按个收取<br>备注：变换压缩技术<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; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<br><br><span style="COLOR: red">PAC（Perceptual Audio Coder）<br></span>类型：Audio<br>制定者：AT&amp;T<br>所需频宽：64kps<br>特性：音频质量获得显著改善，不过在回响、低码率时声音质量严重下降。<br>优点：音频质量获得显著改善<br>缺点：块边界影响、预回响、低码率时声音质量严重下降<br>应用领域：voip<br>版税方式：按个收取<br>备注：变换压缩技术<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; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br><br><span style="COLOR: red">HR</span><br>类型：Audio<br>制定者： 飞利浦<br>所需频宽：8Kbps<br>特性：以增加GSM网络容量为目的,但是会损害语音质量;由于现在网络频率紧缺,一些大的运营商已经在大城市密集地带开通此方式以增加容量。<br>优点：系统容量大<br>缺点：语音质量差<br>应用领域：GSM<br>版税方式：按个收费<br>备注：HF半速率,是一种GSM语音编码方式。</div>
<dt>&nbsp;
<dd>
<div><br><span style="COLOR: red">FR<br></span>类型：Audio<br>制定者：飞利浦<br>所需频宽：13Kbps<br>特性：是一般的GSM手机的通信编码方式,可以获得达到4.1左右Qos的语音通信质量(国际电联规定语音通信质量Qos满分为5)<br>优点：语音质量得到了提高<br>缺点：系统容量降低<br>应用领域：GSM<br>版税方式：按个收费<br>备注：FR全速率，是一种GSM语音编码方式。<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; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; <br><br><span style="COLOR: red">EFR<br></span>类型：Audio<br>制定者：飞利浦<br>所需频宽：13Kbps<br>特性：用于GSM手机基于全速率13Kbps的语音编码和发送,可以获得更好更清晰的语音质量(接近Qos4.7),需要网络服务商开通此项网络功能，手机才能配合实现。<br>优点：音质好<br>缺点：需要网络服务商开通此项网络功能，且系统容量降低<br>应用领域：GSM<br>版税方式：按个收费<br>备注：EFR增强型全速率,一种GSM网络语音的编码方式。</div>
<dt>&nbsp;
<dd>
<div><br><span style="COLOR: red">GSM-AMR(Adaptive Multi-Rate)<br></span>类型：Audio<br>制定者：飞利浦<br>所需频宽：8Kbps(4.75 Kbps~12.2 Kbps)<br>特性： 可以对语音进行替换和消音，平滑噪音，支持间断式传输，对语音进行动态侦查。能在各种网络条件下提供优质的语音效果。<br>优点：音质出色<br>缺点：<br>应用领域：GSM<br>版税方式：按个收费<br>备注：GSM-ASM是一种广泛使用在GPRS和W-CDMA网络上的音频标准。在规范ETSI GSM06.90中对GSM-AMR进行了定义。AMR语音编码是GSM 2+和WCDMA的默认编码标准，是第三代无线通讯系统的语音编码标准。GSM-AMR标准基于ACELP（代数激励线性预测）编码。它能在广泛的传输条件下提供高品质的语音效果。<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; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<br><br><span style="COLOR: red">EVRC(Enhanced Variable Rate Coder，增强型可变速率编码器)<br></span>类型：Audio<br>制定者：美国Qualcomm通信公司(即高通)<br>所需频宽：8Kbps或13Kbps<br>特性：支持三种码率（9.6 Kbps, 4.8 Kbps 和 1.2 Kbps），噪声抑制，邮件过滤。能在各种网络条件下提供优质的语音效果。<br>优点：音质出色<br>缺点：<br>应用领域：CDMA<br>版税方式：按个收费<br>备注：EVRC编码广泛使用于CDMA网络。EVRC标准遵循规范TIA IS-127的内容。EVRC编码基于RCELP（松弛码激励线性预测）标准。该编码可以以Rate 1（171bits/packet），Rate 1/2（80bits/packet）或是Rate 1/8（16bits/packet）的容量进行操作。在要求下，它也能产生空包（0bits/packet）。</div>
<dt>&nbsp;
<dd>
<div><br><span style="COLOR: red">QCELP(QualComm Code Excited Linear Predictive，受激线性预测编码)<br></span>类型：Audio<br>制定者：美国Qualcomm通信公司(即高通)<br>所需频宽：8k的语音编码算法(可工作于4/4.8/8/9.6Kbps等固定速率上，而且可变速率地工作于800Kbps～9600Kbps之间)<br>特性：使用适当的门限值来决定所需速率。QCELP是一种8k的语音编码算法(可以在8k的速率下提供接近13k的话音压缩质量)。这是一种可变速率话音编码，根据人的说话特性（大家应该能够体会我们日常的沟通和交流时并不是一直保持某种恒定的方式讲话，有间断、有不同的声音频率等都是人的自然表达）而采取的一种优化技术。<br>优点：话音清晰、背景噪声小，系统容量大<br>缺点： 不是Free<br>应用领域：CDMA<br>版税方式：每年支付一笔使用权费用<br>备注：QCELP，即QualComm Code Excited Linear Predictive（QualComm受激线性预测编码）。美国Qualcomm通信公司的专利语音编码算法，是北美第二代数字移动电话（CDMA）的语音编码标准（IS95）。这种算法不仅可工作于4/4.8/8/9.6kbit／s等固定速率上，而且可变速率地工作于800bit／s～9600bit／s之间。QCELP算法被认为是到目前为止效率效率最高的一种算法，它的主要特点之一，是使用适当的门限值来决定所需速率。I&#8216;1限值懈景噪声电平变化而变化，这样就抑制了背景噪声，使得即使在喧闹的环境中，也能得到良好的话音质量， CDMA8Kbit/s的话音近似GSM 13Mbit/s的话音。CDMA采用QCELP编码等一系列技术，具有话音清晰、背景噪声小等优势，其性能明显 优于其他无线移动通信系统，语音质量可以与有线电话媲美。 无线辐射低。</div>
</dd>
<img src ="http://www.cppblog.com/gtwdaizi/aggbug/41884.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/gtwdaizi/" target="_blank">郭天文</a> 2008-01-25 13:29 <a href="http://www.cppblog.com/gtwdaizi/articles/41884.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++字符串完全指引之一 —— Win32 字符编码</title><link>http://www.cppblog.com/gtwdaizi/articles/39809.html</link><dc:creator>郭天文</dc:creator><author>郭天文</author><pubDate>Fri, 28 Dec 2007 01:54:00 GMT</pubDate><guid>http://www.cppblog.com/gtwdaizi/articles/39809.html</guid><wfw:comment>http://www.cppblog.com/gtwdaizi/comments/39809.html</wfw:comment><comments>http://www.cppblog.com/gtwdaizi/articles/39809.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/gtwdaizi/comments/commentRss/39809.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/gtwdaizi/services/trackbacks/39809.html</trackback:ping><description><![CDATA[<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td>
            <p align=center><strong>C++字符串完全指引之一 —— Win32 字符编码<br><br></strong><br>原著：Michael Dunn<br><br>翻译：<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#99;&#106;&#115;&#117;&#110;&#64;&#105;&#110;&#115;&#117;&#110;&#46;&#104;&#105;&#116;&#46;&#101;&#100;&#117;&#46;&#99;&#110;"><u><font color=#0000ff>Chengjie Sun</font></u></a></p>
            <p><br><br>原文出处：<font color=#0000ff size=2><a href="http://www.codeproject.com/string/cppstringguide1.asp" target=_blank><u>CodeProject：The Complete Guide to C++ Strings, Part I</u></a></font><br><br><u><font color=#0000ff><img src="http://www.vckbase.com/document/image/paragraph.gif"></font></u> <strong>引言</strong><br><br>　　毫无疑问，我们都看到过像 TCHAR, std::string, BSTR 等各种各样的字符串类型，还有那些以 _tcs 开头的奇怪的宏。你也许正在盯着显示器发愁。本指引将总结引进各种字符类型的目的，展示一些简单的用法，并告诉您在必要时，如何实现各种字符串类型之间的转换。<br>　　在第一部分，我们将介绍3种字符编码类型。了解各种编码模式的工作方式是很重要的事情。即使你已经知道一个字符串是一个字符数组，你也应该阅读本部分。一旦你了解了这些，你将对各种字符串类型之间的关系有一个清楚地了解。<br>　　在第二部分，我们将单独讲述string类，怎样使用它及实现他们相互之间的转换。<br><br><img src="http://www.vckbase.com/document/image/paragraph.gif"> <strong>字符基础 -- ASCII, DBCS, Unicode</strong><br><br>　　所有的 string 类都是以C-style字符串为基础的。C-style 字符串是字符数组。所以我们先介绍字符类型。这里有3种编码模式对应3种字符类型。第一种编码类型是单子节字符集（single-byte character set or SBCS）。在这种编码模式下，所有的字符都只用一个字节表示。ASCII是SBCS。一个字节表示的0用来标志SBCS字符串的结束。<br>　　第二种编码模式是多字节字符集（multi-byte character set or MBCS）。一个MBCS编码包含一些一个字节长的字符，而另一些字符大于一个字节的长度。用在Windows里的MBCS包含两种字符类型，单字节字符（single-byte characters）和双字节字符（double-byte characters）。由于Windows里使用的多字节字符绝大部分是两个字节长，所以MBCS常被用DBCS代替。<br>　　在DBCS编码模式中，一些特定的值被保留用来表明他们是双字节字符的一部分。例如，在Shift-JIS编码中（一个常用的日文编码模式），0x81-0x9f之间和 0xe0-oxfc之间的值表示"这是一个双字节字符，下一个子节是这个字符的一部分。"这样的值被称作"leading bytes",他们都大于0x7f。跟随在一个leading byte子节后面的字节被称作"trail byte"。在DBCS中，trail byte可以是任意非0值。像SBCS一样，DBCS字符串的结束标志也是一个单字节表示的0。<br>　　第三种编码模式是Unicode。Unicode是一种所有的字符都使用两个字节编码的编码模式。Unicode字符有时也被称作宽字符，因为它比单子节字符宽（使用了更多的存储空间）。注意，Unicode不能被看作MBCS。MBCS的独特之处在于它的字符使用不同长度的字节编码。Unicode字符串使用两个字节表示的0作为它的结束标志。<br>　　单字节字符包含拉丁文字母表，accented characters及ASCII标准和DOS操作系统定义的图形字符。双字节字符被用来表示东亚及中东的语言。Unicode被用在COM及Windows NT操作系统内部。<br>　　你一定已经很熟悉单字节字符。当你使用char时，你处理的是单字节字符。双字节字符也用char类型来进行操作（这是我们将会看到的关于双子节字符的很多奇怪的地方之一）。Unicode字符用wchar_t来表示。Unicode字符和字符串常量用前缀L来表示。例如：</p>
            <pre>wchar_t wch = L''1''; // 2 bytes, 0x0031
            wchar_t* wsz = L"Hello"; // 12 bytes, 6 wide characters</pre>
            <p><img src="http://www.vckbase.com/document/image/paragraph.gif"> <strong>字符在内存中是怎样存储的</strong><br><br>　　单字节字符串：每个字符占一个字节按顺序依次存储，最后以单字节表示的0结束。例如。"Bob"的存贮形式如下：</p>
            <table id=AutoNumber1 style="BORDER-COLLAPSE: collapse" borderColor=#111111 cellSpacing=0 cellPadding=0 width="26%" border=1>
                <tbody>
                    <tr>
                        <td align=middle width="25%">42</td>
                        <td align=middle width="25%"><font color=#990000>6F</font></td>
                        <td align=middle width="25%">62</td>
                        <td align=middle width="25%">00</td>
                    </tr>
                    <tr>
                        <td align=middle width="25%"><font color=#990000>B</font></td>
                        <td align=middle width="25%"><font color=#990000>o</font></td>
                        <td align=middle width="25%"><font color=#990000>b</font></td>
                        <td align=middle width="25%"><font color=#990000>BOS</font></td>
                    </tr>
                </tbody>
            </table>
            <p>Unicode的存储形式，L"Bob"</p>
            <table id=AutoNumber2 style="BORDER-COLLAPSE: collapse" borderColor=#111111 cellSpacing=0 cellPadding=0 width="42%" border=1>
                <tbody>
                    <tr>
                        <td align=middle width="25%">42 00 </td>
                        <td align=middle width="25%"><font color=#990000>6F</font> 00</td>
                        <td align=middle width="25%">62 00</td>
                        <td align=middle width="25%">00 00</td>
                    </tr>
                    <tr>
                        <td align=middle width="25%"><font color=#990000>B</font></td>
                        <td align=middle width="25%"><font color=#990000>o</font></td>
                        <td align=middle width="25%"><font color=#990000>b</font></td>
                        <td align=middle width="25%"><font color=#990000>BOS</font></td>
                    </tr>
                </tbody>
            </table>
            <p>使用两个字节表示的0来做结束标志。<br><br>　　一眼看上去，DBCS 字符串很像 SBCS 字符串，但是我们一会儿将看到 DBCS 字符串的微妙之处，它使得使用字符串操作函数和永字符指针遍历一个字符串时会产生预料之外的结果。字符串" " ("nihongo")在内存中的存储形式如下（LB和TB分别用来表示 leading byte 和 trail byte）</p>
            <table id=AutoNumber3 style="BORDER-COLLAPSE: collapse" borderColor=#111111 height=52 cellSpacing=0 cellPadding=0 width="62%" border=1>
                <tbody>
                    <tr>
                        <td align=middle width="25%" height=20>93 <font color=#990000>FA</font></td>
                        <td align=middle width="25%" height=20>96 <font color=#990000>7B</font></td>
                        <td align=middle width="25%" height=20><font color=#990000>8C EA</font></td>
                        <td align=middle width="25%" height=20>00</td>
                    </tr>
                    <tr>
                        <td align=middle width="25%" height=20><font color=#990000>LB TB</font></td>
                        <td align=middle width="25%" height=20><font color=#990000>LB TB</font></td>
                        <td align=middle width="25%" height=20><font color=#990000>LB TB</font></td>
                        <td align=middle width="25%" height=20><font color=#990000>EOS</font></td>
                    </tr>
                    <tr>
                        <td align=middle width="25%" height=20><img height=13 src="http://www.vckbase.com/document/journal/vckbase30/images/ri.gif" width=10 border=0></td>
                        <td align=middle width="25%" height=20><img height=13 src="http://www.vckbase.com/document/journal/vckbase30/images/ben.gif" width=13 border=0></td>
                        <td align=middle width="25%" height=20><img height=13 src="http://www.vckbase.com/document/journal/vckbase30/images/yu.gif" width=13 border=0></td>
                        <td align=middle width="25%" height=20><font color=#990000>EOS</font></td>
                    </tr>
                </tbody>
            </table>
            <p>值得注意的是，"ni"的值不能被解释成WORD型值0xfa93，而应该看作两个值93和fa以这种顺序被作为"ni"的编码。<br><br><img src="http://www.vckbase.com/document/image/paragraph.gif"> <strong>使用字符串处理函数</strong><br><br>　　我们都已经见过C语言中的字符串函数，strcpy(), sprintf(), atoll()等。这些字符串只应该用来处理单字节字符字符串。标准库也提供了仅适用于Unicode类型字符串的函数，比如wcscpy(), swprintf(), wtol()等。<br>　　微软还在它的CRT(C runtime library)中增加了操作DBCS字符串的版本。Str***()函数都有对应名字的DBCS版本_mbs***()。如果你料到可能会遇到DBCS字符串（如果你的软件会被安装在使用DBCS编码的国家，如中国，日本等，你就可能会），你应该使用_mbs***()函数，因为他们也可以处理SBCS字符串。（一个DBCS字符串也可能含有单字节字符，这就是为什么_mbs***()函数也能处理SBCS字符串的原因）<br>　　让我们来看一个典型的字符串来阐明为什么需要不同版本的字符串处理函数。我们还是使用前面的Unicode字符串 L"Bob"：</p>
            <table id=AutoNumber4 style="BORDER-COLLAPSE: collapse" borderColor=#111111 cellSpacing=0 cellPadding=0 width="42%" border=1>
                <tbody>
                    <tr>
                        <td align=middle width="25%">42 00 </td>
                        <td align=middle width="25%"><font color=#990000>6F</font> 00</td>
                        <td align=middle width="25%">62 00</td>
                        <td align=middle width="25%">00 00</td>
                    </tr>
                    <tr>
                        <td align=middle width="25%"><font color=#990000>B</font></td>
                        <td align=middle width="25%"><font color=#990000>o</font></td>
                        <td align=middle width="25%"><font color=#990000>b</font></td>
                        <td align=middle width="25%"><font color=#990000>BOS</font></td>
                    </tr>
                </tbody>
            </table>
            <p>　　因为x86CPU是little-endian，值0x0042在内存中的存储形式是42 00。你能看出如果这个字符串被传给strlen()函数会出现什么问题吗？它将先看到第一个字节42，然后是00，而00是字符串结束的标志，于是strlen()将会返回1。如果把"Bob"传给wcslen()，将会得出更坏的结果。wcslen()将会先看到0x6f42，然后是0x0062，然后一直读到你的缓冲区的末尾，直到发现00 00结束标志或者引起了GPF。<br>　　到目前为止，我们已经讨论了str***()和wcs***()的用法及它们之间的区别。Str***()和_mbs**()之间的有区别区别呢？明白他们之间的区别，对于采用正确的方法来遍历DBCS字符串是很重要的。下面，我们将先介绍字符串的遍历，然后回到str***()与_mbs***()之间的区别这个问题上来。<br><br><img src="http://www.vckbase.com/document/image/paragraph.gif"> <strong>正确的遍历和索引字符串</strong><br><br>　　因为我们中大多数人都是用着SBCS字符串成长的，所以我们在遍历字符串时，常常使用指针的++-和-操作。我们也使用数组下标的表示形式来操作字符串中的字符。这两种方式是用于SBCS和Unicode字符串，因为它们中的字符有着相同的宽度，编译器能正确的返回我们需要的字符。<br>　　然而，当碰到DBCS字符串时，我们必须抛弃这些习惯。这里有使用指针遍历DBCS字符串时的两条规则。违背了这两条规则，你的程序就会存在DBCS有关的bugs。</p>
            <dir>
            <li>1．在前向遍历时，不要使用++操作，除非你每次都检查lead byte；
            <li>2．永远不要使用-操作进行后向遍历。 </li>
            </dir>
            <p>　　我们先来阐述规则2，因为找到一个违背它的真实的实例代码是很容易的。假设你有一个程序在你自己的目录里保存了一个设置文件，你把安装目录保存在注册表中。在运行时，你从注册表中读取安装目录，然后合成配置文件名，接着读取该文件。假设，你的安装目录是C:\Program Files\MyCoolApp，那么你合成的文件名应该是C:\Program Files\MyCoolApp\config.bin。当你进行测试时，你发现程序运行正常。<br>　　现在，想象你合成文件名的代码可能是这样的：</p>
            <pre>bool GetConfigFileName ( char* pszName, size_t nBuffSize )
            {
            char szConfigFilename[MAX_PATH];
            // Read install dir from registry... we''ll assume it succeeds.
            // Add on a backslash if it wasn''t present in the registry value.
            // First, get a pointer to the terminating zero.
            char* pLastChar = strchr ( szConfigFilename, ''\0'' );
            // Now move it back one character.
            pLastChar--;
            if ( *pLastChar != ''\\'' )
            strcat ( szConfigFilename, "\\" );
            // Add on the name of the config file.
            strcat ( szConfigFilename, "config.bin" );
            // If the caller''s buffer is big enough, return the filename.
            if ( strlen ( szConfigFilename ) &gt;= nBuffSize )
            return false;
            else
            {
            strcpy ( pszName, szConfigFilename );
            return true;
            }
            }      </pre>
            　　这是一段很健壮的代码，然而在遇到 DBCS 字符时它将会出错。让我们来看看为什么。假设一个日本用户使用了你的程序，把它安装在 C:\<img height=13 src="http://www.vckbase.com/document/journal/vckbase30/images/youkoso.gif" width=35 border=0>。下面是这个名字在内存中的存储形式：<br>　
            <table id=AutoNumber5 style="BORDER-COLLAPSE: collapse" borderColor=#111111 cellSpacing=0 cellPadding=0 width="55%" border=1>
                <tbody>
                    <tr>
                        <td align=middle width="12%">43</td>
                        <td align=middle width="12%"><font color=#990000>3A</font></td>
                        <td align=middle width="12%"><font color=#0000ff>5C</font></td>
                        <td align=middle width="12%">83 88</td>
                        <td align=middle width="13%">83 45</td>
                        <td align=middle width="13%">83 52</td>
                        <td align=middle width="13%">83 <font color=#0000ff>5C</font></td>
                        <td align=middle width="13%">00</td>
                    </tr>
                    <tr>
                        <td align=middle width="12%">　</td>
                        <td align=middle width="12%">　</td>
                        <td align=middle width="12%">　</td>
                        <td align=middle width="12%"><font color=#990000>LB TB </font></td>
                        <td align=middle width="13%"><font color=#990000>LB TB </font></td>
                        <td align=middle width="13%"><font color=#990000>LB TB </font></td>
                        <td align=middle width="13%"><font color=#990000>LB TB </font></td>
                        <td align=middle width="13%">　</td>
                    </tr>
                    <tr>
                        <td align=middle width="12%">C</td>
                        <td align=middle width="12%">:</td>
                        <td align=middle width="12%">\</td>
                        <td align=middle width="12%"><img height=11 src="http://www.vckbase.com/document/journal/vckbase30/images/yo.gif" width=9 border=0></td>
                        <td align=middle width="13%"><img height=13 src="http://www.vckbase.com/document/journal/vckbase30/images/u.gif" width=10 border=0></td>
                        <td align=middle width="13%"><img height=11 src="http://www.vckbase.com/document/journal/vckbase30/images/ko.gif" width=9 border=0></td>
                        <td align=middle width="13%"><img height=12 src="http://www.vckbase.com/document/journal/vckbase30/images/so.gif" width=10 border=0></td>
                        <td align=middle width="13%"><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: #990000; FONT-FAMILY: Courier New">EOS</span></td>
                    </tr>
                </tbody>
            </table>
            <p>　　当使用 GetConfigFileName() 检查尾部的''\\''时，它寻找安装目录名中最后的非0字节，看它是等于''\\''的，所以没有重新增加一个''\\''。结果是代码返回了错误的文件名。<br>　　哪里出错了呢？看看上面两个被用蓝色高量显示的字节。斜杠''\\''的值是0x5c。'' ''的值是83 5c。上面的代码错误的读取了一个 trail byte，把它当作了一个字符。<br>　　正确的后向遍历方法是使用能够识别DBCS字符的函数，使指针移动正确的字节数。下面是正确的代码。（指针移动的地方用红色标明） </p>
            <pre>bool FixedGetConfigFileName ( char* pszName, size_t nBuffSize )
            {
            char szConfigFilename[MAX_PATH];
            // Read install dir from registry... we''ll assume it succeeds.
            // Add on a backslash if it wasn''t present in the registry value.
            // First, get a pointer to the terminating zero.
            char* pLastChar = _mbschr ( szConfigFilename, ''\0'' );
            // Now move it back one double-byte character.
            <font color=#ff0000>  pLastChar = CharPrev ( szConfigFilename, pLastChar );</font>
            if ( *pLastChar != ''\\'' )
            _mbscat ( szConfigFilename, "\\" );
            // Add on the name of the config file.
            _mbscat ( szConfigFilename, "config.bin" );
            // If the caller''s buffer is big enough, return the filename.
            if ( _mbslen ( szInstallDir ) &gt;= nBuffSize )
            return false;
            else
            {
            _mbscpy ( pszName, szConfigFilename );
            return true;
            }
            }
            </pre>
            　　上面的函数使用CharPrev() API使pLastChar向后移动一个字符，这个字符可能是两个字节长。在这个版本里，if条件正常工作，因为lead byte永远不会等于0x5c。<br>　　让我们来想象一个违背规则1的场合。例如，你可能要检测一个用户输入的文件名是否多次出现了'':''。如果，你使用++操作来遍历字符串，而不是使用CharNext()，你可能会发出不正确的错误警告如果恰巧有一个trail byte它的值的等于'':''的值。<br>与规则2相关的关于字符串索引的规则：
            <pre>2a. 永远不要使用减法去得到一个字符串的索引。</pre>
            <p>违背这条规则的代码和违背规则2的代码很相似。例如，</p>
            <pre>char* pLastChar = &amp;szConfigFilename [strlen(szConfigFilename) - 1];</pre>
            <p>这和向后移动一个指针是同样的效果。<br><br><img src="http://www.vckbase.com/document/image/paragraph.gif"><strong> 回到关于str***()和_mbs***()的区别</strong><br><br>　　现在，我们应该很清楚为什么_mbs***()函数是必需的。Str***()函数根本不考虑DBCS字符，而_mbs***()考虑。如果，你调用strrchr("C:\\ ", ''\\'')，返回结果可能是错误的，然而_mbsrchr()将会认出最后的双字节字符，返回一个指向真的''\\''的指针。<br>　　关于字符串函数的最后一点：str***()和_mbs***()函数认为字符串的长度都是以char来计算的。所以，如果一个字符串包含3个双字节字符，_mbslen()将会返回6。Unicode函数返回的长度是按wchar_t来计算的。例如，wcslen(L"Bob")返回3。<br><br><img src="http://www.vckbase.com/document/image/paragraph.gif"><strong> Win32 API中的MBCS和Unicode</strong><br><br>两组 APIs： <br>　　尽管你也许从来没有注意过，Win32中的每个与字符串相关的API和message都有两个版本。一个版本接受MBCS字符串，另一个接受Unicode字符串。例如，根本没有SetWindowText()这个API，相反，有SetWindowTextA()和SetWindowTextW()。后缀A表明这是MBCS函数，后缀W表示这是Unicode版本的函数。<br>　　当你 build 一个 Windows 程序，你可以选择是用 MBCS 或者 Unicode APIs。如果，你曾经用过VC向导并且没有改过预处理的设置，那表明你用的是MBCS版本。那么，既然没有 SetWindowText() API，我们为什么可以使用它呢？winuser.h头文件包含了一些宏，例如： </p>
            <pre>BOOL WINAPI SetWindowTextA ( HWND hWnd, LPCSTR lpString );
            BOOL WINAPI SetWindowTextW ( HWND hWnd, LPCWSTR lpString );
            #ifdef UNICODE
            #define SetWindowText  SetWindowTextW
            #else
            #define SetWindowText  SetWindowTextA
            #endif      </pre>
            当使用MBCS APIs来build程序时，UNICODE没有被定义，所以预处理器看到：
            <pre>#define SetWindowText SetWindowTextA</pre>
            <p>　　这个宏定义把所有对SetWindowText的调用都转换成真正的API函数SetWindowTextA。（当然，你可以直接调用SetWindowTextA() 或者 SetWindowTextW()，虽然你不必那么做。）<br>　　所以，如果你想把默认使用的API函数变成Unicode版的，你可以在预处理器设置中，把_MBCS从预定义的宏列表中删除，然后添加UNICODE和_UNICODE。(你需要两个都定义，因为不同的头文件可能使用不同的宏。) 然而，如果你用char来定义你的字符串，你将会陷入一个尴尬的境地。考虑下面的代码：</p>
            <pre>HWND hwnd = GetSomeWindowHandle();
            char szNewText[] = "we love Bob!";
            SetWindowText ( hwnd, szNewText );</pre>
            <p>在预处理器把SetWindowText用SetWindowTextW来替换后，代码变成：</p>
            <pre>HWND hwnd = GetSomeWindowHandle();
            char szNewText[] = "we love Bob!";
            SetWindowTextW ( hwnd, szNewText );</pre>
            <p>　　看到问题了吗？我们把单字节字符串传给了一个以Unicode字符串做参数的函数。解决这个问题的第一个方案是使用 #ifdef 来包含字符串变量的定义：</p>
            <pre>HWND hwnd = GetSomeWindowHandle();
            #ifdef UNICODE
            wchar_t szNewText[] = L"we love Bob!";
            #else
            char szNewText[] = "we love Bob!";
            #endif
            SetWindowText ( hwnd, szNewText );</pre>
            <p>你可能已经感受到了这样做将会使你多么的头疼。完美的解决方案是使用TCHAR.<br><br><img src="http://www.vckbase.com/document/image/paragraph.gif"><strong> 使用TCHAR</strong><br><br>　　TCHAR是一种字符串类型，它让你在以MBCS和UNNICODE来build程序时可以使用同样的代码，不需要使用繁琐的宏定义来包含你的代码。TCHAR的定义如下：</p>
            <pre>#ifdef UNICODE
            typedef wchar_t TCHAR;
            #else
            typedef char TCHAR;
            #endif</pre>
            <p>所以用MBCS来build时，TCHAR是char，使用UNICODE时，TCHAR是wchar_t。还有一个宏来处理定义Unicode字符串常量时所需的L前缀。</p>
            <pre>#ifdef UNICODE
            #define _T(x) L##x
            #else
            #define _T(x) x
            #endif</pre>
            <p>　　##是一个预处理操作符，它可以把两个参数连在一起。如果你的代码中需要字符串常量，在它前面加上_T宏。如果你使用Unicode来build，它会在字符串常量前加上L前缀。</p>
            <pre>TCHAR szNewText[] = _T("we love Bob!");</pre>
            <p>　　像是用宏来隐藏SetWindowTextA/W的细节一样，还有很多可以供你使用的宏来实现str***()和_mbs***()等字符串函数。例如，你可以使用_tcsrchr宏来替换strrchr()、_mbsrchr()和wcsrchr()。_tcsrchr根据你预定义的宏是_MBCS还是UNICODE来扩展成正确的函数，就像SetWindowText所作的一样。<br>　　不仅str***()函数有TCHAR宏。其他的函数如， _stprintf（代替sprinft()和swprintf()）,_tfopen（代替fopen()和_wfopen()）。 MSDN中"Generic-Text Routine Mappings."标题下有完整的宏列表。<br><br><img src="http://www.vckbase.com/document/image/paragraph.gif"><strong> 字符串和TCHAR typedefs</strong><br><br>　　由于Win32 API文档的函数列表使用函数的常用名字（例如，"SetWindowText"），所有的字符串都是用TCHAR来定义的。（除了XP中引入的只适用于Unicode的API）。下面列出一些常用的typedefs，你可以在msdn中看到他们。</p>
            <table id=AutoNumber6 style="BORDER-COLLAPSE: collapse" borderColor=#111111 cellSpacing=4 cellPadding=4 width="98%" border=1>
                <tbody>
                    <tr>
                        <td align=middle width="16%"><strong>type </strong></td>
                        <td align=middle width="42%"><strong>Meaning in MBCS builds </strong></td>
                        <td align=middle width="42%"><strong>Meaning in Unicode builds</strong></td>
                    </tr>
                    <tr>
                        <td width="16%">WCHAR</td>
                        <td width="42%">wchar_t</td>
                        <td width="42%">wchar_t</td>
                    </tr>
                    <tr>
                        <td width="16%">LPSTR </td>
                        <td width="42%">zero-terminated string of char (char*)</td>
                        <td width="42%">zero-terminated string of char (char*)</td>
                    </tr>
                    <tr>
                        <td width="16%">LPCSTR </td>
                        <td width="42%">constant zero-terminated string of char (const char*)</td>
                        <td width="42%">constant zero-terminated string of char (const char*)</td>
                    </tr>
                    <tr>
                        <td width="16%">LPWSTR</td>
                        <td width="42%">zero-terminated Unicode string (wchar_t*) </td>
                        <td width="42%">zero-terminated Unicode string (wchar_t*)</td>
                    </tr>
                    <tr>
                        <td width="16%">LPCWSTR</td>
                        <td width="42%">constant zero-terminated Unicode string (const wchar_t*)</td>
                        <td width="42%">constant zero-terminated Unicode string (const wchar_t*) </td>
                    </tr>
                    <tr>
                        <td width="16%"><xxxxime xime="7">TCHAR</xxxxime></td>
                        <td width="42%"><xxxxime xime="7">char</xxxxime></td>
                        <td width="42%"><xxxxime xime="7">wchar_t</xxxxime></td>
                    </tr>
                    <tr>
                        <td width="16%">LPTSTR</td>
                        <td width="42%">zero-terminated string of TCHAR (TCHAR*) </td>
                        <td width="42%">zero-terminated string of TCHAR (TCHAR*)</td>
                    </tr>
                    <tr>
                        <td width="16%">LPCTSTR </td>
                        <td width="42%">constant zero-terminated string of TCHAR (const TCHAR*)</td>
                        <td width="42%">constant zero-terminated string of TCHAR (const TCHAR*)</td>
                    </tr>
                </tbody>
            </table>
            <p><img src="http://www.vckbase.com/document/image/paragraph.gif"><strong> 何时使用 TCHAR 和 Unicode</strong><br><br>　　到现在，你可能会问，我们为什么要使用Unicode。我已经用了很多年的char。下列3种情况下，使用Unicode将会使你受益：</p>
            <dir>
            <li>1．你的程序只运行在Windows NT系统中。
            <li>2． 你的程序需要处理超过MAX_PATH个字符长的文件名。
            <li>3． 你的程序需要使用XP中引入的只有Unicode版本的API. </li>
            </dir>
            <p>　　Windows 9x 中大多数的 API 没有实现 Unicode 版本。所以，如果你的程序要在windows 9x中运行，你必须使用MBCS APIs。然而，由于NT系统内部都使用Unicode，所以使用Unicode APIs将会加快你的程序的运行速度。每次，你传递一个字符串调用MBCS API，操作系统会把这个字符串转换成Unicode字符串，然后调用对应的Unicode API。如果一个字符串被返回，操作系统还要把它转变回去。尽管这个转换过程被高度优化了，但它对速度造成的损失是无法避免的。<br>　　只要你使用Unicode API，NT系统允许使用非常长的文件名（突破了MAX_PATH的限制，MAX_PATH=260）。使用Unicode API的另一个优点是你的程序会自动处理用户输入的各种语言。所以一个用户可以输入英文，中文或者日文，而你不需要额外编写代码去处理它们。<br>　　最后，随着windows 9x产品的淡出，微软似乎正在抛弃MBCS APIs。例如，包含两个字符串参数的SetWindowTheme() API只有Unicode版本的。使用Unicode来build你的程序将会简化字符串的处理，你不必在MBCS和Unicdoe之间相互转换。<br>　　即使你现在不使用Unicode来build你的程序，你也应该使用TCHAR及其相关的宏。这样做不仅可以的代码可以很好地处理DBCS，而且如果将来你想用Unicode来build你的程序，你只需要改变一下预处理器中的设置就可以实现了。<br><br></p>
            <p>&#160;</p>
            <p>&#160;</p>
            <p>&#160;</p>
            </td>
        </tr>
        <tr>
            <td><img src="http://www.vckbase.com/document/image/paragraph.gif"><strong> 作者简介</strong><br>　　Michael Dunn：居住在阳光城市洛杉矶。他是如此的喜欢这里的天气以致于想一生都住在这里。他在4年级时开始编程，那时用的电脑是Apple //e。1995年，在 UCLA 获得数学学士学位，随后在Symantec 公司做 QA 工程师，在 Norton AntiVirus 组工作。他自学了 Windows 和 MFC 编程。1999-2000年，他设计并实现了 Norton AntiVirus 的新界面。　<br>　　Michael 现在在 Napster（一个提供在线订阅音乐服务的公司）做开发工作，他还开发了UltraBar，一个IE工具栏插件，它可以使网络搜索更加容易，给了 googlebar 以沉重打击；他还开发了 CodeProject SearchBar；与人共同创建了 Zabersoft 公司，该公司在洛杉矶和丹麦的 Odense 都设有办事处。<br>　　他喜欢玩游戏。爱玩的游戏有 pinball, bike riding，偶尔还玩 PS, Dreamcasth 和 MAME 游戏。他因忘了自己曾经学过的语言：法语、汉语、日语而感到悲哀。</td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cppblog.com/gtwdaizi/aggbug/39809.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/gtwdaizi/" target="_blank">郭天文</a> 2007-12-28 09:54 <a href="http://www.cppblog.com/gtwdaizi/articles/39809.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Windows Mobile中如何建立GPRS连接以便Socket能正常通信</title><link>http://www.cppblog.com/gtwdaizi/articles/39578.html</link><dc:creator>郭天文</dc:creator><author>郭天文</author><pubDate>Tue, 25 Dec 2007 06:51:00 GMT</pubDate><guid>http://www.cppblog.com/gtwdaizi/articles/39578.html</guid><wfw:comment>http://www.cppblog.com/gtwdaizi/comments/39578.html</wfw:comment><comments>http://www.cppblog.com/gtwdaizi/articles/39578.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/gtwdaizi/comments/commentRss/39578.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/gtwdaizi/services/trackbacks/39578.html</trackback:ping><description><![CDATA[<span>&nbsp;
<p><span>【版<span><span>&nbsp;&nbsp; </span></span>本】</span></p>
<p><st1:chsdate year="1899" month="12" day="30" islunardate="False" isrocdate="False" w:st="on"><span>1.0.0</span></st1:chsdate></p>
<p><span>【操作系统】</span></p>
<p><span>Windows Mobile 5.0</span></p>
<p><span>【作<span><span>&nbsp;&nbsp; </span></span>者】</span></p>
<p><span>谢红伟&#183;<span>chrys </span>&#183;<span><a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#99;&#104;&#114;&#121;&#115;&#64;&#49;&#54;&#51;&#46;&#99;&#111;&#109;">chrys@163.com</a> </span>&#183;<span><a href="http://www.howa.com.cn/">http://www.howa.com.cn</a></span></span></p>
<p><span>【开发日期】</span></p>
<p><span>2007-12-11 01:23:56</span></p>
<p>&#160;</p>
<p><font size=3><span>最近编写一个医疗项目的程序，需要用</span><span><font face="Times New Roman"> Windows Mobile </font></span><span>来做通信处理，需要将手机端的数据通过</span><span><font face="Times New Roman">GPRS</font></span><span>传送至公网上的一个服务器上。数据传输我采用的是</span><span><font face="Times New Roman">socket</font></span><span>，用数据线</span><span><font face="Times New Roman">+ActiveSync</font></span><span>调试通过，数据传输正常，在准备将软件提交给质检部门的时候，用真正的</span><span><font face="Times New Roman">GPRS</font></span><span>来做通信测试时，问题出来了，连接始终建立不了，但用手机的</span><span><font face="Times New Roman">IE</font></span><span>浏览器却能正常打开网页，而且奇怪的是只要用</span><span><font face="Times New Roman">IE</font></span><span>浏览器成功访问过一次网页，我的</span><span><font face="Times New Roman"> socket </font></span><span>就能正常进行数据通信，看来传说中的</span><span><font face="Times New Roman">GPRS</font></span><span>常连接被我误解了。</span></font></p>
<p><font size=3><span>手机开通</span><span><font face="Times New Roman">GPRS</font></span><span>以后，我们的</span><span><font face="Times New Roman">socket </font></span><span>程序还不能直接建立网络连接，需要用连接管理器来获取当前可用连接，并自动选择一个最佳的连接途径，然后启用这个连接，在连接启动成功以后再用</span><span><font face="Times New Roman">socket </font></span><span>进行网络连接方可正常进行。大概</span><span><font face="Times New Roman">GPRS</font></span><span>拨号和连接过程就是在这里自动进行的吧。源代码中封装了一个连接管理的类和测试代码，可以清楚地看到</span><span><font face="Times New Roman">Windows Mobile </font></span><span>在</span><span><font face="Times New Roman">socket </font></span><span>编程之前到底需要做什么样的操作。</span></font></p>
<p><span><font face="Times New Roman" size=3>&nbsp;</font></span></p>
<p><span><font size=3>首先需要枚举当前可用的连接</font></span></p>
<p><span><font size=3><font face="Times New Roman">void CConnectManager::EnumNetIdentifier ( OUT CStringArray &amp;StrAry )</font></font></span></p>
<p><span><font size=3><font face="Times New Roman">{</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>CONNMGR_DESTINATION_INFO networkDestInfo = {0};</font></font></span></p>
<p><span><font face="Times New Roman" size=3>&nbsp;</font></span></p>
<p><font size=3><span><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// </font></span><span>得到网络列表</span></font></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>for ( DWORD dwEnumIndex=0; ; dwEnumIndex++ )</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>memset ( &amp;networkDestInfo, 0, sizeof(CONNMGR_DESTINATION_INFO) );</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if ( ConnMgrEnumDestinations ( dwEnumIndex, &amp;networkDestInfo ) == E_FAIL )</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>break;</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>StrAry.Add ( networkDestInfo.szDescription );</font></font></span></p>
<p><span><font face="Times New Roman"><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</font></font></span></p>
<p><span><font face="Times New Roman"><font size=3>}</font></font></span></p>
<p><span><font face="Times New Roman" size=3>&nbsp;</font></span></p>
<p><font size=3><span>接下来找到&#8220;</span><span><font face="Times New Roman">Internet</font></span><span>&#8221;这个连接，可用远程</span><span><font face="Times New Roman">URL</font></span><span>映射的方式来完成，这样可以让系统自动选取一个最好的连接。</span></font></p>
<p><span><font size=3><font face="Times New Roman">int CConnectManager::MapURLAndGUID ( LPCTSTR lpszURL, OUT GUID &amp;guidNetworkObject, OUT CString *pcsDesc/*=NULL*/ )</font></font></span></p>
<p><span><font size=3><font face="Times New Roman">{</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if ( !lpszURL || lstrlen(lpszURL) &lt; 1 )</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return FALSE;</font></font></span></p>
<p><span><font face="Times New Roman" size=3>&nbsp;</font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>memset ( &amp;guidNetworkObject, 0, sizeof(GUID) );</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int nIndex = 0;</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>HRESULT hResult = ConnMgrMapURL ( lpszURL, &amp;guidNetworkObject, (DWORD*)&amp;nIndex );</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if ( FAILED(hResult) )</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>nIndex = -1;</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>DWORD dwLastError = GetLastError ();</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>AfxMessageBox ( _T("Could not map a request to a network identifier") );</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>else</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if ( pcsDesc )</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>CONNMGR_DESTINATION_INFO DestInfo = {0};</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if ( SUCCEEDED(ConnMgrEnumDestinations(nIndex, &amp;DestInfo)) )</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&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; </span>*pcsDesc = DestInfo.szDescription;</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</font></font></span></p>
<p><span><font face="Times New Roman" size=3>&nbsp;</font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return nIndex;</font></font></span></p>
<p><span><font size=3><font face="Times New Roman">}</font></font></span></p>
<p><span><font face="Times New Roman" size=3>&nbsp;</font></span></p>
<p><span><font size=3>以下代码是用来启用指定编号的连接</font></span></p>
<p><span><font size=3><font face="Times New Roman">BOOL CConnectManager::EstablishConnection ( DWORD dwIndex )</font></font></span></p>
<p><span><font size=3><font face="Times New Roman">{</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ReleaseConnection ();</font></font></span></p>
<p><span><font face="Times New Roman" size=3>&nbsp;</font></span></p>
<p><font size=3><span><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// </font></span><span>得到正确的连接信息</span></font></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>CONNMGR_DESTINATION_INFO DestInfo = {0};</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>HRESULT hResult = ConnMgrEnumDestinations(dwIndex, &amp;DestInfo);</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>BOOL bRet = FALSE;</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if(SUCCEEDED(hResult))</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</font></font></span></p>
<p><font size=3><span><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// </font></span><span>初始化连接结构</span></font></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>CONNMGR_CONNECTIONINFO ConnInfo;</font></font></span></p>
<p><span><font face="Times New Roman" size=3>&nbsp;</font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ZeroMemory(&amp;ConnInfo, sizeof(ConnInfo));</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ConnInfo.cbSize = sizeof(ConnInfo);</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ConnInfo.dwParams = CONNMGR_PARAM_GUIDDESTNET;</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ConnInfo.dwFlags = CONNMGR_FLAG_PROXY_HTTP | CONNMGR_FLAG_PROXY_WAP | CONNMGR_FLAG_PROXY_SOCKS4 | CONNMGR_FLAG_PROXY_SOCKS5;</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ConnInfo.dwPriority = CONNMGR_PRIORITY_USERINTERACTIVE;</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ConnInfo.guidDestNet = DestInfo.guid;</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ConnInfo.bExclusive<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>= FALSE; </font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ConnInfo.bDisabled = FALSE;</font></font></span></p>
<p><span><font face="Times New Roman" size=3>&nbsp;</font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>DWORD dwStatus = 0;</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>hResult = ConnMgrEstablishConnectionSync(&amp;ConnInfo, &amp;m_hConnection, 10*1000, &amp;dwStatus );</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if(FAILED(hResult))</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>m_hConnection = NULL;</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>else bRet = TRUE;</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</font></font></span></p>
<p><span><font face="Times New Roman" size=3>&nbsp;</font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return bRet;</font></font></span></p>
<p><span><font size=3><font face="Times New Roman">}</font></font></span></p>
<p><span><font face="Times New Roman" size=3>&nbsp;</font></span></p>
<p><span><font size=3>为了确保连接是否真正可用，需要检测连接状态，在规定的时间内如果未取得&#8220;连接成功&#8221;的状态，则认为连接未能正常启用，可能需要配置手机的连接管理器界面</font></span></p>
<p><span><font size=3><font face="Times New Roman">BOOL CConnectManager::WaitForConnected ( int nTimeoutSec, DWORD *pdwStatus/*=NULL*/ )</font></font></span></p>
<p><span><font size=3><font face="Times New Roman">{</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>DWORD dwStartTime = GetTickCount ();</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>BOOL bRet = FALSE;</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>while ( GetTickCount ()-dwStartTime &lt; (DWORD)nTimeoutSec * 1000 )</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if ( m_hConnection )</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>DWORD dwStatus = 0;</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>HRESULT hr = ConnMgrConnectionStatus ( m_hConnection, &amp;dwStatus );</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if ( pdwStatus ) *pdwStatus = dwStatus;</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if ( SUCCEEDED(hr) )</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&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; </span>if ( dwStatus == CONNMGR_STATUS_CONNECTED )</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&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; </span>{</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&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; </span>bRet = TRUE;</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&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; </span>break;</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&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; </span>}</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Sleep ( 100 );</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</font></font></span></p>
<p><span><font face="Times New Roman" size=3>&nbsp;</font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return bRet;</font></font></span></p>
<p><span><font size=3><font face="Times New Roman">}</font></font></span></p>
<p><span><font face="Times New Roman" size=3>&nbsp;</font></span></p>
<p><font size=3><span>至此，我们的连接启用工作已经做完了，我们可以用我们熟悉的</span><span><font face="Times New Roman"> socket </font></span><span>来编写网络通信程序了。下面是一个测试</span><span><font face="Times New Roman"> socket </font></span><span>测试网络连接是否能正常建立的例子：</span></font></p>
<p><span><font size=3><font face="Times New Roman">SetWaitCursor ();</font></font></span></p>
<p><span><font size=3><font face="Times New Roman">CSocket sock;</font></font></span></p>
<p><span><font size=3><font face="Times New Roman">sock.Create ();</font></font></span></p>
<p><span><font size=3><font face="Times New Roman">if ( sock.Connect ( _T("www.baidu.com"), 80 ) )</font></font></span></p>
<p><span><font size=3><font face="Times New Roman">{</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>RestoreCursor ();</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>AfxMessageBox ( _T("Connect to www.baidu.com successfully"), MB_ICONINFORMATION );</font></font></span></p>
<p><span><font size=3><font face="Times New Roman">}</font></font></span></p>
<p><span><font size=3><font face="Times New Roman">else</font></font></span></p>
<p><span><font size=3><font face="Times New Roman">{</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>RestoreCursor ();</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>AfxMessageBox ( _T("Connect to www.baidu.com failed") );</font></font></span></p>
<p><span><font size=3><font face="Times New Roman">}</font></font></span></p>
<p><span><font face="Times New Roman" size=3>&nbsp;</font></span></p>
<p><font size=3><span><font face="Times New Roman">GPRSDemo.exe </font></span><span>的使用</span></font></p>
<p><span><font size=3>程序启动以后出现如下界面：</font></span></p>
<p align=center><span><font size=3></font></span></p>
<p><span><font face="Times New Roman" size=3>&nbsp;<img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/chrys2000/屏幕截图_2007-12-11%2002_30_10.jpg"></font></span></p>
<p><font size=3><span>连接可用性</span><font face="Times New Roman"> <span>&#8211; </span></font><span>检测连接管理器是否可用</span></font></p>
<p><font size=3><span>映射</span><span><font face="Times New Roman">URL &#8211; </font></span><span>是让系统自动寻找一个最好的连接</span></font></p>
<p><font size=3><span>枚举网络标识符</span><font face="Times New Roman"> <span>&#8211; </span></font><span>将当前系统中所有可用的连接都会被枚举出来</span></font></p>
<p><font size=3><span>连接网络</span><font face="Times New Roman"> <span>&#8211; </span></font><span>将枚举出来的连接选中的那个连接进行连接启用操作。</span></font></p>
<p><font size=3><span>连接状态</span><font face="Times New Roman"> <span>&#8211; </span></font><span>表示可以获取到当前连接的状态；</span></font></p>
<p><font size=3><span>连接到公网测试</span><font face="Times New Roman"> </font><span><font face="Times New Roman">&#8211; </font></span><span>利用</span><font face="Times New Roman"> <span><a href="http://www.baidu.com/">www.baidu.com</a>&nbsp;</span></font><span>来测试连接是否已经正常启动。</span></font></p>
<p><span><font face="Times New Roman" size=3>&nbsp;</font></span></p>
<p><span><font size=3>操作步骤：</font></span></p>
<p><font size=3><span>可以直接按&#8220;枚举网络标识符&#8221;，程序将所有当前在用的网络枚举出来并添加到</span><span><font face="Times New Roman"> ListBox </font></span><span>控件中；</span></font></p>
<p><font size=3><span>连接网络。选择一个连接（例如：</span><span><font face="Times New Roman">Internet</font></span><span>），按&#8220;连接网络&#8221;按钮，当提示</span><font face="Times New Roman"> <span>Connection net successfully </span></font><span>表示连接已经正常启用了。</span></font></p>
<p><font size=3><span>按&#8220;连接到公网测试&#8221;按钮，软件自动和</span><font face="Times New Roman"> <span><a href="http://www.baidu.com/">www.baidu.com</a> </span></font><span>进行连接测试。</span></font></p>
<p><font size=3></font>&nbsp;</p>
<p><font size=3><span><a href="http://www.pudn.com/downloads94/sourcecode/embed/detail373151.html" target=_blank>源代码下载地址：http://www.pudn.com/downloads94/sourcecode/embed/detail373151.html</a></span></font></p>
</span>
<img src ="http://www.cppblog.com/gtwdaizi/aggbug/39578.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/gtwdaizi/" target="_blank">郭天文</a> 2007-12-25 14:51 <a href="http://www.cppblog.com/gtwdaizi/articles/39578.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WaitForMultipleObjects用法探索</title><link>http://www.cppblog.com/gtwdaizi/articles/39556.html</link><dc:creator>郭天文</dc:creator><author>郭天文</author><pubDate>Tue, 25 Dec 2007 03:09:00 GMT</pubDate><guid>http://www.cppblog.com/gtwdaizi/articles/39556.html</guid><wfw:comment>http://www.cppblog.com/gtwdaizi/comments/39556.html</wfw:comment><comments>http://www.cppblog.com/gtwdaizi/articles/39556.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/gtwdaizi/comments/commentRss/39556.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/gtwdaizi/services/trackbacks/39556.html</trackback:ping><description><![CDATA[<p><font size=3><span><font face="Times New Roman">WaitForMultipleObjects</font></span><span>是</span><span><font face="Times New Roman">Windows</font></span><span>中的一个功能非常强大的函数，几乎可以等待</span><span><font face="Times New Roman">Windows</font></span><span>中的所有的内核对象（关于该函数的描述和例子见</span><span><font face="Times New Roman">MSDN</font></span><span>，）。但同时该函数在用法上却需要一定的技巧。</span></font></p>
<pre><font size=3><font color=#660000><strong><span>原型</span></strong><span>：</span></font></font><strong><span>DWORD</span></strong><span> <strong>WaitForMultipleObjects(</strong></span></pre>
<pre><span>&nbsp;<strong>DWORD</strong> <em><span><span><span><u>nCount</u></span></span></span></em><strong>,</strong></span></pre>
<pre><span>&nbsp;<strong>const HANDLE*</strong> <em><span><span><span><u>lpHandles</u></span></span></span></em><strong>,</strong></span></pre>
<pre><span>&nbsp;<strong>BOOL</strong> <em><span><span><span><u>bWaitAll</u></span></span></span></em><strong>,</strong></span></pre>
<pre><span>&nbsp;<strong>DWORD</strong> <u><em><span><span><span>dwMilliseconds</span></span></span></em><strong></strong></u></span></pre>
<pre><strong><span>);</span></strong></pre>
<p><span><font face="Times New Roman" size=3>&nbsp;</font></span></p>
<p><font size=3><span>当</span><span><font face="Times New Roman">WaitForMultipleObjects</font></span><span>等到多个内核对象的时候，如果它的</span></font><em><span>bWaitAll </span></em><font size=3><span>参数设置为</span><span><font face="Times New Roman">false</font></span><span>。其返回值减去</span><span><font face="Times New Roman">WAIT_OBJECT_0 </font></span><span>就是参数</span></font><em><span>lpHandles</span></em><span><font size=3>数组的序号。如果同时有多个内核对象被出发，这个函数返回的只是其中序号最小的那个。</font></span></p>
<p><font size=3><span>问题就在这里，我们如何可以获取所有被同时触发的内核对象。举个例子：我们需要在一个线程中处理从完成端口、数据库、和可等待定时器来的数据。一个典型的实现方法就是：用</span><span><font face="Times New Roman">WaitForMultipleObjects</font></span><span>等待所有的这些事件。如果完成端口，数据库发过来的数据量非常大，可等待定时器时间也只有几十毫秒。那么这些事件同时触发的几率可以说非常大，我们不希望丢弃任何一个被触发的事件。那么如何能高效地实现这一处理呢？</span></font></p>
<p><font size=3><font face=宋体><span>MSDN</span>中有一句非常重要的描述，它可以说是<span>WaitForMultipleObjects</span>用法的精髓：</font></font><span>The function modifies the state of some types of synchronization objects. Modification occurs only for the object or objects whose signaled state caused the function to return. For example, the count of a semaphore object is decreased by one. When <em>bWaitAll</em> is FALSE, and multiple objects are in the signaled state, the function chooses one of the objects to satisfy the wait; the states of the objects not selected are unaffected</span><span>.</span></p>
<p><font size=3><span>多个内核对象被触发时，</span><span><font face="Times New Roman">WaitForMultipleObjects</font></span><span>选择其中序号最小的返回。而</span><span><font face="Times New Roman">WaitForMultipleObjects</font></span><span>它只会改变使它返回的那个内核对象的状态。</span></font></p>
<p><span><font size=3>这儿又会产生一个问题，如果序号最小的那个对象频繁被触发，那么序号比它大的内核对象将得不到被处理的机会。</font></span></p>
<p><font size=3><span>为了解决这一问题，可以采用双</span><span><font face="Times New Roman">WaitForMultipleObjects</font></span><span>检测机制来实现。见下面的例子：</span></font></p>
<p>
<table cellSpacing=0 cellPadding=0 width=612 border=0>
    <tbody>
        <tr>
            <td vAlign=top width=612>
            <p align=left><span>DWORD WINAPI ThreadProc(LPVOID lpParameter)</span></p>
            <p align=left><span>{</span></p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp; </span>DWORD dwRet = 0;</span></p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp; </span><span>int</span><span>&nbsp;&nbsp; </span>nIndex = 0;</span></p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp; </span><span>while</span>(1)</span></p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp; </span>{</span></p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>dwRet = WaitForMultipleObjects(nCount,pHandles,<span>false</span>,INFINITE);</span></p>
            <p align=left>&nbsp;</p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>switch</span>(dwRet)</span></p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</span></p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>case</span> WAIT_TIMEOUT:</span></p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>break</span>;</span></p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>case</span> WAIT_FAILED:</span></p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>return</span> 1;</span></p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>default</span>:</span></p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</span></p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>nIndex = dwRet - WAIT_OBJECT_0;</span></p>
            <p align=left>&nbsp;</p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ProcessHanlde(nIndex++);</span></p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>//</span></span><span>同时检测其他的事件</span></p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>while</span>(nIndex &lt; nCount)</span></p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</span></p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>dwRet = WaitForMultipleObjects(nCount - nIndex,&amp;pHandles[nIndex],<span>false</span>,0);</span></p>
            <p align=left>&nbsp;</p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>switch</span>(dwRet)</span></p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</span></p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>case</span> WAIT_TIMEOUT:</span></p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>nIndex = nCount; <span>//</span></span><span>退出检测<span>,</span>因为没有被触发的对象了<span>.</span></span></p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>break</span>;</span></p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>case</span> WAIT_FAILED:</span></p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>return</span> 1;</span></p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>default</span>:</span></p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</span></p>
            <p align=left><span><span>&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; </span>nIndex = dwRet - WAIT_OBJECT_0;</span></p>
            <p align=left><span><span>&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; </span>ProcessHanlde(nIndex++);</span></p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</span></p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>break</span></span></p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</span></p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</span></p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</span></p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>break</span>;</span></p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</span></p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp; </span>}</span></p>
            <p align=left><span><span>&nbsp;&nbsp;&nbsp; </span><span>return</span> 0;</span></p>
            <p><span>}</span></p>
            </td>
        </tr>
    </tbody>
</table>
<strong><font color=#000066 size=5></font></strong></p>
<img src ="http://www.cppblog.com/gtwdaizi/aggbug/39556.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/gtwdaizi/" target="_blank">郭天文</a> 2007-12-25 11:09 <a href="http://www.cppblog.com/gtwdaizi/articles/39556.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Using WinInet HTTP functions in Full Asynchronous Mode</title><link>http://www.cppblog.com/gtwdaizi/articles/39107.html</link><dc:creator>郭天文</dc:creator><author>郭天文</author><pubDate>Thu, 20 Dec 2007 05:49:00 GMT</pubDate><guid>http://www.cppblog.com/gtwdaizi/articles/39107.html</guid><wfw:comment>http://www.cppblog.com/gtwdaizi/comments/39107.html</wfw:comment><comments>http://www.cppblog.com/gtwdaizi/articles/39107.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/gtwdaizi/comments/commentRss/39107.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/gtwdaizi/services/trackbacks/39107.html</trackback:ping><description><![CDATA[<h2>Introduction</h2>
<p>If you have ever dug into the MSDN for WinInet API, you noticed that it can be used asynchronously and that it is the recommended way to use it.</p>
<p>If then you decide to use it, you won&#8217;t find any explanation of how to use it asynchronously. And no samples are available anywhere on Internet. After a long research and lots of testing, I finally managed to reconstruct a big part of the (voluntary?) missing documentation.</p>
<p>Why asynchronous is better? Because it can handle timeouts correctly. Just what&#8217;s missing in WinInet under IE5.5.</p>
<p>If you try to use <code>TerminateThread</code> or <code>CloseHandle</code> functions to handle timeouts (these methods are given in MSDN articles), you&#8217;ll fall into unrecoverable leaks of all kinds.</p>
<p>This has been tested successfully with: IE4.01SP3, IE5.0, IE5.01, IE5.5SP1 under WinNT4 on mono and multiprocessor machines, under a stressed environment (15 concurrent instances running non-stop for 12 hours on multi-proc NT server machines).</p>
<h2>Theory</h2>
<p>To use WinInet functions in full asynchronous mode, you must do things in the right order:</p>
<ol>
    <li>Use <code>INTERNET_FLAG_ASYNC</code> to open the session
    <li>Set a status callback using <code>InternetSetStatusCallback</code>
    <li>Open the connection using <code>InternetOpenUrl</code>
    <li>If <code>InternetOpenUrl</code> returned <code>NULL</code> and <code>GetLastError</code> is <code>ERROR_IO_PENDING</code>:
    <ul>
        <li>wait for the <code>INTERNET_STATUS_HANDLE_CREATED</code> notification in the callback, and save the connection Handle.
        <li>wait for the <code>INTERNET_STATUS_REQUEST_COMPLETE</code> notification in the callback before going further. </li>
    </ul>
    <li>Extract the content-length from the header and set up an <code>INTERNET_BUFFERS</code> structure:
    <ul>
        <li><code>dwStructSize</code> = <code><span class=code-keyword>sizeof</span>(INTERNET_BUFFERS)</code>
        <li><code>lpvBuffer</code> = your allocated buffer
        <li><code>dwBufferLength</code> = its length </li>
    </ul>
    <li>Use <code>InternetReadFileEx</code> with the <code>IRF_ASYNC</code> flag to read the remaining data asynchronously. Don&#8217;t use <code>InternetReadFile</code> since it is a synchronous function.
    <li>If <code>InternetReadFileEx</code> returned <code>False</code> and <code>GetLastError</code> is <code>ERROR_IO_PENDING</code>: wait for the <code>INTERNET_STATUS_REQUEST_COMPLETE</code> notification in the callback before going further.
    <p>Warning: <code>INTERNET_BUFFERS</code> members are modified asynchronously (only the <code>dwBufferLength</code> member and the content of the buffer).</p>
    <li>If the <code>dwBufferLength</code> member is not 0, move the <code>lpvBuffer</code> pointer from this amount and subtract this amount from the buffer length so <code>dwBufferLength</code> reflects the remaining size <code>lpvBuffer</code> points to, then loop to 6.
    <li>Close the connection handle with <code>InternetCloseHandle</code> and wait for <code>INTERNET_STATUS_HANDLE_CLOSING</code> and the facultative <code>INTERNET_STATUS_REQUEST_COMPLETE</code> notification (sent only if an error occurs &#8211; like a sudden closed connection -, you must test the cases). </li>
</ol>
<p>At this state, you can either begin a new connection process or close the session handle. But before closing it, you should un-register the callback function.</p>
<h2>Detail</h2>
<p>After the theory, let&#8217;s look at some code for some of the points:</p>
<h4>1&amp;2: Create the connection using INTERNET_FLAG_ASYNC and setup the callback func:</h4>
<pre>m_Session = InternetOpen(AGENT_NAME, INTERNET_OPEN_TYPE_PRECONFIG,
NULL, NULL, INTERNET_FLAG_ASYNC);
InternetSetStatusCallback( m_Session,
(INTERNET_STATUS_CALLBACK)InternetCallbackFunc );</pre>
<h4>3&amp;4: Open the connection using InternetOpenUrl and wait for INTERNET_STATUS_REQUEST_COMPLETE</h4>
<p>Use the <code>lParam</code> to send a session identifier to your callback. I always use the <code><span class=code-keyword>this</span></code> pointer of my class for it. I assume you know how to handle callbacks.</p>
<pre>InternetOpenUrl( m_Session, uurl, NULL, <span class=code-digit>0</span>,
INTERNET_FLAG_RELOAD | INTERNET_FLAG_PRAGMA_NOCACHE |
INTERNET_FLAG_NO_CACHE_WRITE, (LPARAM)<span class=code-keyword>this</span> );</pre>
<p>The callback will receive a lots of messages then. Here are their orders along with the <code>dwInternetStatus</code> value:</p>
<pre>[openUrl] InternetStatus: <span class=code-digit>60</span> INTERNET_STATUS_HANDLE_CREATED
**At <span class=code-keyword>this</span> point you can save the HINTERNET handle <span class=code-keyword>using</span> code like <span class=code-keyword>this</span> <span class=code-keyword>in</span> your callback:
INTERNET_ASYNC_RESULT* res = (INTERNET_ASYNC_RESULT*)lpvStatusInformation;
m_hHttpFile = (HINTERNET)(res-<span class=code-keyword>&gt;</span>dwResult);
[openUrl] InternetStatus: <span class=code-digit>10</span>
[openUrl] InternetStatus: <span class=code-digit>11</span>
[openUrl] InternetStatus: <span class=code-digit>20</span>
[openUrl] InternetStatus: <span class=code-digit>21</span>
[openUrl] InternetStatus: <span class=code-digit>30</span>
[openUrl] InternetStatus: <span class=code-digit>31</span>
[openUrl] InternetStatus: <span class=code-digit>40</span>
[openUrl] InternetStatus: <span class=code-digit>41</span>
[openUrl] InternetStatus: <span class=code-digit>100</span> INTERNET_STATUS_REQUEST_COMPLETE</pre>
<h4>5: Extract the content-length and set up the INTERNET_BUFFERS structure</h4>
<p>Once you have the handle, try to call <code>HttpQueryInfo</code> with <code>HTTP_QUERY_CONTENT_LENGTH</code> to get the size of the data to retrieve. This function can fail if the content-length field is not in the HTTP header.</p>
<p>Set up the <code>INTERNET_BUFFERS</code> structure.</p>
<pre lang=text>INTERNET_BUFFERS ib = { sizeof(INTERNET_BUFFERS) };
ib.lpvBuffer = your allocated buffer
ib.dwBufferLength = its length</pre>
<p>The <code>dwBufferTotal</code> is provided for your own use and is never modified by WinInet (as far as I know). I use it to store the total size of the received data.</p>
<h4>6&amp;7&amp;8 Read the remaining data in a loop</h4>
<p>Use <code>InternetReadFileEx</code> with the <code>IRF_ASYNC</code> flag to read the remaining data asynchronously. Don&#8217;t use <code>InternetReadFile</code> since it is a synchronous function. You must loop on <code>InternetReadFileEx</code> while the <code>ib.dwBufferLength</code> is not 0. Before each iteration you must adjust the <code>lpvBuffer</code> pointer and reset the <code>dwBufferLength</code> members of <code>ib</code>: add the received length to the pointer and set <code>dwBufferLength</code> to your remaining buffer size.</p>
<pre><span class=code-comment>//</span><span class=code-comment>Start the pump
</span>BOOL bOk = InternetReadFileEx( m_hHttpFile, &amp;ib, IRF_ASYNC, (LPARAM)<span class=code-keyword>this</span> );
<span class=code-keyword>if</span>(!bOk &amp;&amp; GetLastError()==ERROR_IO_PENDING)
wait...
<span class=code-comment>//</span><span class=code-comment>Pump
</span><span class=code-keyword>while</span>( bOk &amp;&amp; ib.dwBufferLength!=0 )
{
(adjust ib values)
bOk = InternetReadFileEx( m_hHttpFile, &amp;ib, IRF_ASYNC, (LPARAM)<span class=code-keyword>this</span> );
<span class=code-keyword>if</span>(!bOk &amp;&amp; GetLastError()==ERROR_IO_PENDING)
wait...
}</pre>
<p>Your callback should receive these notifications (maybe more than once):</p>
<pre lang=text>[connect] InternetStatus: 40 (receiving response)
[connect] InternetStatus: 41 (response received)
[connect] InternetStatus: 50
[connect] InternetStatus: 51
and maybe
[connect] InternetStatus: 100 INTERNET_STATUS_REQUEST_COMPLETE</pre>
<p>The last is received only if <code>GetLastError()</code> returned <code>ERROR_IO_PENDING</code>. If you stored the total data size (in bytes) in the <code>dwBufferTotal</code> member, use it to set the final &#8220;0&#8221; in your string buffer (if it&#8217;s a string).</p>
<pre>buf[ib.dwBufferTotal] = <span class=code-digit>0</span>;</pre>
<h4>9 Close the connection handle</h4>
<pre>InternetCloseHandle( m_httpFile );</pre>
<p>The callback will receive this notification when the handle is closed:</p>
<pre lang=text>[connect] InternetStatus: 70 INTERNET_STATUS_HANDLE_CLOSING</pre>
<p>In most error cases, the connection is closed unexpectedly. If it happens you&#8217;ll receive a 70 followed by a 100 (<code>INTERNET_STATUS_REQUEST_COMPLETE</code>). This can happen anywhere during the process.</p>
<h4>10 Before closing the m_Session handle</h4>
<p>You must deregister the callback:</p>
<pre>InternetSetStatusCallback( m_Session, NULL );</pre>
<p>This should help those who tried to go through asynchronous mode in WinInet! Sorry, there are no attached files but you should be able to use the functions and create nice classes now. If you liked this article please add an entry in <a href="http://www.softlion.com/" target=_blank><u><font color=#0000ff>my guestbook</font></u></a> and buy me a license of my shareware.</p>
<h2>Bibliography</h2>
<ul>
    <li><a href="http://msdn.microsoft.com/workshop/networking/wininet/reference/functions/InternetReadFileEx.asp" target=_blank><u><font color=#0000ff>Microsoft website</font></u></a>
    <li>Q176420: <a href="http://support.microsoft.com/support/kb/articles/Q176/4/20.ASP" target=_blank><u><font color=#0000ff>BUG: InternetSetOption Does Not Set Timeout Values</font></u></a>
    <li>Q176176: <a href="http://support.microsoft.com/support/kb/articles/Q176/1/76.ASP" target=_blank><u><font color=#0000ff>FIX: InternetOpenUrl Does Not Work When Called Asynchronously</font></u></a>
    <li>Q238425: INFO: WinInet Not Supported for Use in Services
    <li>Q190542: INFO: Using WinInet APIs in a System Service to access SSL sites
    <li><a href="news://microsoft.public.inetsdk.programming.wininet/" target=_blank><u><font color=#0000ff>Microsft Newsgroup</font></u></a> </li>
</ul>
<!-- Article Ends --><!-- Main Page Contents End -->
<h2>About the Author</h2>
<table cellSpacing=5 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr vAlign=top>
            <td id=ctl00_AboutAuthorRptr_ctl00_AboutAuthor_memberPhotoTable style="WIDTH: 155px" vAlign=top><strong><a id=ctl00_AboutAuthorRptr_ctl00_AboutAuthor_memberProfileLink href="http://www.codeproject.com/script/Membership/Profiles.aspx?mid=31"><u><font color=#0000ff>Benjamin Mayrargue</font></u></a></strong><br><br>
            <center><u><font color=#0000ff></font></u></center><br><span class=SmallText id=ctl00_AboutAuthorRptr_ctl00_AboutAuthor_memberType></span></td>
            <td><br>
            <table>
                <tbody>
                    <tr id=ctl00_AboutAuthorRptr_ctl00_AboutAuthor_locationRow>
                        <td class=SmallText>Location: </td>
                        <td width="100%"><span class=SmallText id=ctl00_AboutAuthorRptr_ctl00_AboutAuthor_memberLocation><img height=11 alt="United States" src="http://www.codeproject.com/script/Geo/Images/US.gif" width=16> United States</span></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br>
<table id=ctl00_PopularArticlesRow cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr vAlign=top>
            <td style="WIDTH: 100%">
            <h2>Other popular Internet / Network articles:</h2>
            <ul>
                <li><a href="http://www.codeproject.com/KB/IP/AsyncSocketServerandClien.aspx"><u><font color=#0000ff>An Asynchronous Socket Server and Client</font></u></a>
                <div class=SmallText>An asynchronous socket server and client with encryption and compression.</div>
                <li><a href="http://www.codeproject.com/KB/IP/ndk.aspx"><u><font color=#0000ff>Network Development Kit 2.0</font></u></a>
                <div class=SmallText>Network Development Kit is a set of simple classes for a client-server architecture.</div>
                <li><a href="http://www.codeproject.com/KB/IP/drvfltip.aspx"><u><font color=#0000ff>Developing Firewalls for Windows 2000/XP</font></u></a>
                <div class=SmallText>An article about developing Firewalls for Windows 2000/XP.</div>
                <li><a href="http://www.codeproject.com/KB/IP/socketsincs.aspx"><u><font color=#0000ff>Asynchronous socket communication</font></u></a>
                <div class=SmallText>An article on using sockets to communicate in a non-blocking manner. The sample works through building a simple chat client and server.</div>
                <li><a href="http://www.codeproject.com/KB/IP/serversocket.aspx"><u><font color=#0000ff>Multi-threaded Client/Server Socket Class</font></u></a>
                <div class=SmallText>A multi-threaded based Client/Server Socket Communication class</div>
                </li>
            </ul>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cppblog.com/gtwdaizi/aggbug/39107.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/gtwdaizi/" target="_blank">郭天文</a> 2007-12-20 13:49 <a href="http://www.cppblog.com/gtwdaizi/articles/39107.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++返回引用类型　指针的引用</title><link>http://www.cppblog.com/gtwdaizi/articles/38521.html</link><dc:creator>郭天文</dc:creator><author>郭天文</author><pubDate>Fri, 14 Dec 2007 07:59:00 GMT</pubDate><guid>http://www.cppblog.com/gtwdaizi/articles/38521.html</guid><wfw:comment>http://www.cppblog.com/gtwdaizi/comments/38521.html</wfw:comment><comments>http://www.cppblog.com/gtwdaizi/articles/38521.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/gtwdaizi/comments/commentRss/38521.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/gtwdaizi/services/trackbacks/38521.html</trackback:ping><description><![CDATA[<p><span style="COLOR: red"><strong>C++返回引用类型</strong></span><br>A&amp; a(){ return *this;} 就生成了一个固定地址的指针，并把指针带给你 <br><br>但A a() { return *this;}会生成一个临时对象变量，并把这个临时变量给你 <br>这样就多了一步操作 <br><br>当返回一个变量时，会产生拷贝。当返回一个引用时，不会发生拷贝，你可以将引用看作是一个变量的别名，就是其他的名字，引用和被引用的变量其实是一个东西，只是有了两个名字而已。 <br><br>问题的关键是，当你想要返回一个引用而不是一个拷贝时，你要确保这个引用的有效性，比如： <br>int &amp; fun() { int a; a=10; return a; } <br>这样是不行的，因为a会在fun退出时被销毁，这时返回的a的引用是无效的。 <br>这种情况下，如果fun的返回类型不是int &amp; 而是int就没有问题了。 </p>
<br><span style="COLOR: red"><strong>指针的引用<br></strong><font face="Courier New" color=#000000>GetNearestFontInTwips(CFont *&amp;aFont, const TFontSpec &amp;aFontSpec);<br><br>第一个参数aFont是一个指针, 前面加上*&amp;表示指针的引用, 其实可以如下看待这个方式(CFont*) &amp;aFont, 这就一目了然了.</font></span>
<img src ="http://www.cppblog.com/gtwdaizi/aggbug/38521.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/gtwdaizi/" target="_blank">郭天文</a> 2007-12-14 15:59 <a href="http://www.cppblog.com/gtwdaizi/articles/38521.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>CListCtrl::SortItems方法</title><link>http://www.cppblog.com/gtwdaizi/articles/32783.html</link><dc:creator>郭天文</dc:creator><author>郭天文</author><pubDate>Mon, 24 Sep 2007 06:48:00 GMT</pubDate><guid>http://www.cppblog.com/gtwdaizi/articles/32783.html</guid><wfw:comment>http://www.cppblog.com/gtwdaizi/comments/32783.html</wfw:comment><comments>http://www.cppblog.com/gtwdaizi/articles/32783.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/gtwdaizi/comments/commentRss/32783.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/gtwdaizi/services/trackbacks/32783.html</trackback:ping><description><![CDATA[CListCtrl::SortItems() 方法的原型如下:<br>
<p><strong>BOOL</strong> <strong>SortItems(</strong> <strong>PFNLVCOMPARE</strong> <em>pfnCompare</em><strong>,</strong> <strong>DWORD</strong> <em>dwData</em> <strong>);</strong></p>
<em>pfnCompare:</em>指定的是一个回调函数的入口地址,这个函数在列表中的连续的节点要进行比较的时候调用,比如说列表中有三个元素,I1,I2,I3, 现在要对这个三个元素排序,那么I1 跟 I2比较时会调用这个函数,I2 跟 I3比较时也会调用(当然,第二次的I2可能已经跟I1互换了). 该函数必须声明为static类型,或者一个非类成员函数.<br><br>
<p class=dt><em>dwData:</em> 指定的是列表对象.<br><br>回调函数的原型如下:<br>MyCompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)<br>参数lParam1和lParam2 其实是两个列表Item的索引值, 排序实际上就是对这个值进行比较.<br>lParamSort包含了指向TreeCtrl控件的指针,可强制转换过来.<br><br>这里需要注意的问题是,在调用SortItems()方法之前,必须调用SetItemData(<strong> int </strong><em>nItem</em>,<strong> DWORD </strong><em>dwData</em><strong> </strong>) 方法来设置回调函数中使用到LPARAM lParam1, LPARAM lParam2<br>MSDN中对于SetItemData()函数的解释如下:<br><em>This function sets the 32-bit application-specific value associated with the item specified by nItem. This value is the <strong>lParam</strong> member of the
<object id=alink_1 type=application/x-oleobject classid=clsid:adb880a6-d8ff-11cf-9377-00aa003b7a11>
</object></em><a href="javascript:alink_1.Click()"><u><font color=#800080><em>LVITEM</em></font></u></a><em> structure, as described in the Platform SDK.<br><br></em>所以不调用SetItemData方法,直接去调用SortItem方法就会出现排序无效的结果.</p>
//SortItems函数的回调函数<br>int CALLBACK CConfigDlg::ListCtrlCompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)<br>{<br>&nbsp;&nbsp;&nbsp; INT iRet = 0;<br>&nbsp;&nbsp;&nbsp; if (lParam1 &gt; lParam2)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; iRet = 1;<br>&nbsp;&nbsp;&nbsp; } <br>&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; iRet = -1;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; return iRet;<br>}
<img src ="http://www.cppblog.com/gtwdaizi/aggbug/32783.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/gtwdaizi/" target="_blank">郭天文</a> 2007-09-24 14:48 <a href="http://www.cppblog.com/gtwdaizi/articles/32783.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>VC的Bug-安装过程中创建DCOM出错(Setup Was Unable to Create a DCOM User Account)</title><link>http://www.cppblog.com/gtwdaizi/articles/32682.html</link><dc:creator>郭天文</dc:creator><author>郭天文</author><pubDate>Sat, 22 Sep 2007 11:49:00 GMT</pubDate><guid>http://www.cppblog.com/gtwdaizi/articles/32682.html</guid><wfw:comment>http://www.cppblog.com/gtwdaizi/comments/32682.html</wfw:comment><comments>http://www.cppblog.com/gtwdaizi/articles/32682.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/gtwdaizi/comments/commentRss/32682.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/gtwdaizi/services/trackbacks/32682.html</trackback:ping><description><![CDATA[<p>今天在重新安装VC6.0企业版的时候出现这个问题, 查了一下资料,原文如下:<br><br>VC6.Setup Was Unable to Create a DCOM User<br>2006-10-29 03:08<br>BUG: "Setup Was Unable to Create a DCOM User Account" Error Message in Visual Studio 6.0<br>SYMPTOMS <br>When you run the Visual Studio 6.0 Setup program, you may receive the following error message: <br>Setup was unable to create a DCOM user account in order to register &lt;path&gt;\valec.exe <br>This first error message may be followed by a second message that indicates that Setup failed. <br>&nbsp;<br>CAUSE<br>Visual Studio Analyzer, which is one of the products that is included in Visual Studio Enterprise Edition version 6.0, cannot create the local user account that it needs to run because of changes to the security policies for users in Windows 2000. <br>&nbsp;<br>RESOLUTION<br>Re-install Visual Studio 6.0 Enterprise Edition without Visual Studio Analyzer: <br><span style="COLOR: red">1. Use the Custom setup, on the Visual Studio 6.0 Enterprise - Custom page, click Enterprise Tools, and then click Change Option. <br>2. On the Visual Studio 6.0 Enterprise - Enterprise Tools page, under Options, make sure the Visual Studio Analyzer check box is not selected. <br>3. Click to select all the other Visual Studio components that you want to install, and then click OK. <br>4. Click Continue, and then follow the instructions that appear. <br>To install Visual Studio Analyzer: 1. In Control Panel, double-click Add/Remove Programs, and then click Microsoft Visual Studio 6.0 Enterprise Edition. <br>2. Click Change/Remove. <br>3. In Visual Studio 6.0 Enterprise Setup, click Add/Remove. <br>4. On the Visual Studio 6.0 Enterprise - Maintenance page, click Enterprise Tools, and then click Change Option. <br>5. On the Visual Studio 6.0 Enterprise - Enterprise Tools page, under Options, click Visual Studio Analyzer, and then click OK. <br>6. On the Visual Studio 6.0 Enterprise - Maintenance page, click Continue, and then follow the instructions that appear. </span></p>
<p>When you receive the error message that is described in the "Symptoms" section, continue with the Visual Studio Setup Wizard. Setup reports a failure. <br>To create a local account, which Visual Studio Analyzer will run as: 1. On the computer that is running Visual Studio Enterprise Edition 6.0, create a local user. For information about how to create a local user, see Windows 2000 Help.&nbsp; <br>2. Run Distributed COM Configuration (dcomcnfg.exe). <br>3. On the Applications tab, click MSVSA Local Event Concentrator Class, and then click Properties. <br>4. On the Identity tab, set This User to match the user and password you created in step 1 of this procedure. </p>
<p><br>STATUS<br>Microsoft has confirmed that this is a bug in the Microsoft products that are listed at the beginning of this article.&nbsp; <br></p>
<img src ="http://www.cppblog.com/gtwdaizi/aggbug/32682.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/gtwdaizi/" target="_blank">郭天文</a> 2007-09-22 19:49 <a href="http://www.cppblog.com/gtwdaizi/articles/32682.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>