<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
	<channel>
		<title>Xinyu Online</title>
		<link>http://www.xinyuonline.net/blog/</link>
		<description></description>
		<copyright>Copyright (C) 2004 Security Angel Team [S4T] All Rights Reserved.</copyright>
		<generator>SaBlog-X Version 1.6 Build 20080806</generator>
		<lastBuildDate>Sat, 11 Sep 2010 02:46:20 +0000</lastBuildDate>
		<ttl>30</ttl>
		<item>
			<guid>http://www.xinyuonline.net/blog/?action=show&amp;id=67</guid>
			<title>Using GRETA in VS2008</title>
			<author>
				<![CDATA[Xinyu <xinyu_gg@hotmail.com>]]>
			</author>
			<description><![CDATA[<p>GRETA就不做详细介绍了，网上资料很多，总之就是一个处理正则表达式的库。</p>
<p>一般来说，在2005和2008下使用GRETA，都会碰到几个典型的error和warning，在网上查了些资料，解决办法主要是以下两步：</p>
<p>1.在GRETA的&quot;<font color="#0000ff">restack.h</font>&quot;文件中，找到此行代码<br />
<font color="#0000ff">byte_t&nbsp;&nbsp;&nbsp; m_buf[ aligned_sizeof&lt;stack_node::header&gt;::no_rtti + StaticBlockSizeT ];</font><br />
将之改为<br />
<font color="#0000ff">byte_t&nbsp;&nbsp;&nbsp; m_buf[ aligned_sizeof&lt;<font color="#ff0000">typename</font> stack_node::header&gt;::no_rtti + StaticBlockSizeT ];</font></p>
<p>2.在&quot;<font color="#0000ff">Project Properties -&gt; Configuration Properties -&gt; C\C++ -&gt; Preprocessor -&gt; Preprocessor Definitions</font>&quot;中添加宏&quot;<font color="#ff0000">_SCL_SECURE_NO_WARNINGS</font>&quot;</p>
<p>经过上述修改，便可以正常使用GRETA而不出现error或者warning了。具体可参考示例程序。</p>
<p><u><font color="#0000ff">GRETA VS2008示例程序下载</font></u>：<a href="http://www.xinyuonline.net/blog/attachment.php?id=86" title="56.82 K, 下载次数:384" target="_blank">regrex.7z</a>.</p>
<p>&nbsp;</p>
<p>鉴于微软官网的GRETA下载链接已经失效数月，这里一并提供GRETA下载。(经测试皆可用于VS2008)</p>
<p><u><font color="#0000ff">GRETA 2.6.4 for vc7</font></u>：<a href="http://www.xinyuonline.net/blog/attachment.php?id=87" title="77.29 K, 下载次数:381" target="_blank">greta-2.6.4.7z</a><br />
<u><font color="#0000ff">GRETA 2.6.4 for vc6</font></u>：<a href="http://www.xinyuonline.net/blog/attachment.php?id=88" title="77.29 K, 下载次数:185" target="_blank">greta-2.6.4-vc6.7z</a></p>]]></description>
			<link>http://www.xinyuonline.net/blog/?action=show&amp;id=67</link>
			<category domain="http://www.xinyuonline.net/blog/?cid=5">软件开发</category>
			<pubDate>2008-10-13 11:32</pubDate>
		</item>
		<item>
			<guid>http://www.xinyuonline.net/blog/?action=show&amp;id=64</guid>
			<title>浅谈socket编程实现HTTP下载</title>
			<author>
				<![CDATA[Xinyu <xinyu_gg@hotmail.com>]]>
			</author>
			<description><![CDATA[<p>在Windows下实现HTTP下载，其实很容易，微软已经帮我们封装好了简便易用的wininet库，利用它提供的API，很容易就能开发出具备HTTP下载功能的程序，不过我在这里并不准备对wininet进行讨论，有兴趣的朋友可以自行查阅MSDN或者相关资料。</p>
<p>我在这里要说的，是直接使用socket编程，来实现HTTP的下载。这种看似底层和原始的方法，虽然实现起来比较麻烦(仅仅是麻烦而已，其实很简单)，但对我们了解HTTP的工作原理和应对非Windows平台的编程，都有一定的帮助，所以我将这几天自己学习的心得体会，记录于此。本文主要涉及socket编程实现直接HTTP下载和通过代理服务器进行HTTP下载。</p>
<p><font size="3"><em><strong>HTTP消息头</strong></em></font></p>
<p>说是socket编程，但核心其实是对HTTP消息头的处理，包括格式化发送，以及接受解析。一个典型的用于下载的HTTP请求头大概是这样的：</p>
<p>
<table height="116" cellspacing="1" cellpadding="1" width="90%" align="center" border="0">
    <tbody>
        <tr>
            <td><font color="#0000ff">GET /test/test.zip HTTP/1.1</font></td>
            <td>&nbsp;<font size="2">-- &quot;GET&quot;是命令，后接要下载的文件，HTTP表示版本</font></td>
        </tr>
        <tr>
            <td><font color="#0000ff">Host: www.gl.gx.cn</font></td>
            <td>&nbsp;-- 主机域名</td>
        </tr>
        <tr>
            <td><font color="#0000ff">Accept: */*</font></td>
            <td>&nbsp;-- 接受任何类型的文件</td>
        </tr>
        <tr>
            <td><font color="#0000ff">User-Agent: MyApp</font></td>
            <td>&nbsp;-- 浏览器的类型</td>
        </tr>
        <tr>
            <td><font color="#0000ff">Connection: Keep-Alive</font></td>
            <td>&nbsp;-- 保持连接</td>
        </tr>
        <tr>
            <td><font color="#0000ff"><br />
            </font></td>
            <td>&nbsp;-- 空行，表示请求头结束</td>
        </tr>
    </tbody>
</table>
</p>
<p>&quot;--&quot;后面的是我加的注释，请求头不包括这些东西。</p>
<p>这里注意还有一个内容没有列出来，但是对于HTTP下载来说是比较重要的，就是&quot;Range&quot;项，像这样&quot;<font color="#0000ff">Range: bytes=起始位置 - 终止位置</font>&quot;，要实现多线程下载和断点续传就都靠他了。我并不打算详细解说每一项内容，有兴趣的可以查阅相关资料。</p>
<p>一个典型的服务器返回的响应头如下：</p>
<p>
<table height="203" cellspacing="1" cellpadding="1" width="90%" align="center" border="0">
    <tbody>
        <tr>
            <td><font color="#0000ff">HTTP/1.1 200 OK</font></td>
            <td align="left">-- 响应代码及指示文本</td>
        </tr>
        <tr>
            <td><font color="#0000ff">Content-Length: 1679134430</font></td>
            <td>-- 数据块长度</td>
        </tr>
        <tr>
            <td><font color="#0000ff">Content-Type: application/x-zip-compressed</font></td>
            <td>-- 数据块文件类型</td>
        </tr>
        <tr>
            <td><font color="#0000ff">Last-Modified: Wed, 15 Mar 2006 13:40:59 GMT</font></td>
            <td>-- 修改时间</td>
        </tr>
        <tr>
            <td><font color="#0000ff">Server: Microsoft-IIS/6.0</font></td>
            <td>-- 服务器信息</td>
        </tr>
        <tr>
            <td><font color="#0000ff">Date: Fri, 26 Sep 2008 08:42:01 GMT</font></td>
            <td>-- 时间</td>
        </tr>
        <tr>
            <td>&nbsp;</td>
            <td>-- 空行，表示响应头结束</td>
        </tr>
        <tr>
            <td>XX XX XX XX XX ...</td>
            <td>-- 数据块字节数据</td>
        </tr>
    </tbody>
</table>
</p>
<p>格式化的信息清晰明了，要注意的是，请求的数据将会紧跟在表示响应头结束的空行后面，所以这里要自己进行拆分处理等等工作。</p>
<p><font size="3"><em><strong>Socket实现HTTP下载</strong></em></font></p>
<p><font color="#0000ff">1 - 和服务器建立连接<br />
2 - 格式化请求头<br />
3 - 发送请求头<br />
4 - 接收服务器返回的数据<br />
5 - 拆分、分析响应头<br />
6 - 接收数据</font></p>
<p>以上就是简要的步骤，实际编程的时候，还有些细节需要动动脑筋来实现，比如拆分响应头和返回的数据，这里提供一种思路，接收的时候一个字节一个字节的接收，然后利用换行符和最后的空行来判断响应头和数据块的分界点，达到拆分数据的目的。</p>
<p><font size="3"><em><strong>Socket通过代理服务器实现HTTP下载</strong></em></font></p>
<p>以上的方法在实际测试时，不能成功，在第一步就失败了，无法和服务器建立连接(用wininet不会有这个问题)，判断是由于公司采用代理服务器上网造成的，无法直连外网。那如何在使用代理服务器的情况下进行下载呢？</p>
<p>答案其实也很简单，上面的几个步骤稍微修改即可，如下：</p>
<p><font color="#0000ff">1 - 和<font color="#ff0000">代理服务器</font>建立连接<br />
<font color="#ff0000">2 - 向代理服务器发送对目标服务器的连接请求</font><br />
3 - 格式化请求头<br />
4 - 发送请求头<br />
5 - 接收服务器返回的数据<br />
6 - 拆分、分析响应头<br />
7 - 接收数据</font></p>
<p>第一步首先和代理服务器进行连接，而不是和目标服务器，这样就可以向代理服务器发送HTTP请求，只不过这次首先发送的是一个连接请求，如下：</p>
<p>
<table cellspacing="1" cellpadding="1" width="90%" align="center" border="0">
    <tbody>
        <tr>
            <td><font color="#0000ff">CONNECT www.gl.gx.com:80 HTTP/1.1</font></td>
            <td>-- 连接目标服务器</td>
        </tr>
        <tr>
            <td><font color="#0000ff">Connection: Keep-Alive</font></td>
            <td>-- 保持连接</td>
        </tr>
        <tr>
            <td>&nbsp;</td>
            <td>-- 空行结束</td>
        </tr>
    </tbody>
</table>
</p>
<p>成功以后，就和目标服务器连接上了，然后就和原来直连一样了，像它发送请求就可以了。</p>
<p>上述通过代理进行下载的方法有一个前提，就是你必须获得代理服务器的地址，才能和它进行连接。当然可以手动指定或者从配置文件中读取，但更多的朋友应该是希望能够取得并使用IE的代理信息，有两个方法可以实现，一个是读取注册表，这个我也没什么研究。。。另一个就是使用wininet提供的InternetQueryOption函数，具体可以参阅MSDN，如果用到了wininet，那么还不如连下载也使用它来的方便。。。</p>
<p>本文只是简单介绍了一下HTTP直接下载和代理下载的原理，都是我这几天的心得和体会，没有提供任何源代码，因为我也没写出完整的示例代码，因为我最终决定偷懒，直接使用wininet来实现http下载的功能... ╮(╯_╰)╭</p>]]></description>
			<link>http://www.xinyuonline.net/blog/?action=show&amp;id=64</link>
			<category domain="http://www.xinyuonline.net/blog/?cid=5">软件开发</category>
			<pubDate>2008-09-26 17:23</pubDate>
		</item>
		<item>
			<guid>http://www.xinyuonline.net/blog/?action=show&amp;id=59</guid>
			<title>MFC中Static控件编程应用几则</title>
			<author>
				<![CDATA[Xinyu <xinyu_gg@hotmail.com>]]>
			</author>
			<description><![CDATA[<p><font color="#0000ff">做MFC编程，Static控件是会经常用到的了，而使Static控件背景透明，以及改变文本的内容、字体、颜色等属性，都是会比较容易碰到的情况。</font></p>
<p>王道做法当然是继承CStatic然后重载OnPaint()，完全自己来画，这样能够获得最大的灵活性，但就是比较麻烦，像我这种比较懒的，就更喜欢用下面的懒方法了。</p>
<p>同样创建一个CStatic的派生类，<font color="#0000ff">处理父窗口的反射消息WM_CTLCOLOR，即添加HBRUSH CtlColor(CDC *pDC, UINT nCtlColor)这个消息映射函数</font>。<font color="#ff0000">注意，不是HBRUSH OnCtlColor(CDC *pDC, CWnd *pWnd, UINT nCtlColor)！</font>我也不知道具体原理，反正我用后者从来没成功过，甚至程序都不会运行到里面。。。&nbsp;</p>
<p>其实还有一个方法，就是处理父窗口的OnCtlColor()，更简单一点，但是不符合封装的原则，所以这里就不提了。</p>
<div class="codeText">
<div class="codeHead">C++代码</div>
<ol class="dp-cpp">
    <li class="alt"><span><span class="datatypes">HBRUSH</span><span>&nbsp;CSample::CtlColor(CDC*&nbsp;pDC,&nbsp;</span><span class="datatypes">UINT</span><span>&nbsp;nCtlColor) &nbsp;&nbsp;</span></span></li>
    <li class=""><span>{ &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;TODO:&nbsp;&nbsp;Change&nbsp;any&nbsp;attributes&nbsp;of&nbsp;the&nbsp;DC&nbsp;here </span><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;pDC-&gt;SetBkMode(TRANSPARENT);&nbsp;&nbsp;</span><span class="comment">//&nbsp;设置透明背景 </span><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;pDC-&gt;SetTextColor(RGB(0,&nbsp;0,&nbsp;255));&nbsp;&nbsp;</span><span class="comment">//&nbsp;设置文本颜色 </span><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;TODO:&nbsp;&nbsp;Return&nbsp;a&nbsp;non-NULL&nbsp;brush&nbsp;if&nbsp;the&nbsp;parent's&nbsp;handler&nbsp;should&nbsp;not&nbsp;be&nbsp;called </span><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">return</span><span>&nbsp;(</span><span class="datatypes">HBRUSH</span><span>)GetStockObject(HOLLOW_BRUSH);&nbsp;</span><span class="comment">//&nbsp;返回透明画刷 </span><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>}&nbsp;&nbsp;</span></li>
</ol>
</div>
<p>通过上述代码，就可以得到彩色的文本以及透明的背景，但是，还存在一个问题，当该Static控件的文本内容或者属性，在运行过程中发生变化的时候，由于背景一直没有擦除(为了实现透明)，会出现重影，导致文本模糊成一团。</p>
<p>解决方法是，<font color="#0000ff">让父窗口进行重绘更新</font>，对，不要看错了，是控件所属的父窗口，而不是控件本身，让控件本身重绘也不会解决问题的，同样我也不太清楚原理。。。</p>
<p>这里还会引出一个问题，如果重绘整个父窗口，由于GDI并不内嵌双缓冲，势必造成严重的闪烁问题，解决办法当然是<font color="#0000ff">只让父窗口重绘控件所占的部分，其他部分不进行重绘</font>，代码如下：</p>
<div class="codeText">
<div class="codeHead">C++代码</div>
<ol class="dp-cpp">
    <li class="alt"><span><span class="keyword">void</span><span>&nbsp;CSample::SetText(</span><span class="keyword">const</span><span>&nbsp;</span><span class="datatypes">TCHAR</span><span>&nbsp;*pszText) &nbsp;&nbsp;</span></span></li>
    <li class=""><span>{ &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">this</span><span>-&gt;SetWindowText(pszText); &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;RECT&nbsp;stRect; &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;获取控件位置 </span><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">this</span><span>-&gt;GetWindowRect(&amp;stRect); &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;重要！调用父窗口的S2C函数进行坐标转换 </span><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">this</span><span>-&gt;GetParent()-&gt;ScreenToClient(&amp;stRect); &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;重绘控件所在区域，在这里擦除背景 </span><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">this</span><span>-&gt;GetParent()-&gt;InvalidateRect(&amp;stRect,&nbsp;</span><span class="keyword">true</span><span>); &nbsp;&nbsp;</span></li>
    <li class=""><span>}&nbsp;&nbsp;</span></li>
</ol>
</div>
<p>这样就能够实现动态改变文本属性而不出现重影现象，<font color="#ff0000">注意这里调用了父窗口的ScreenToClient()函数来进行坐标的转换</font>，调用控件本身的S2C函数的话，得到的坐标无法用来进行下一步的重绘工作。</p>
<p>现在还有一个比较隐蔽的问题，就是文本字符串的长度，如果新的字符串的长度比原来的长，而之前拖放Static控件长度又不足的时候，就会造成超出的部分无法显示，当然你大可以在拖放的时候就尽量弄得长一点，但是如果能随着文本内容而自动调整控件长度，那不是会好得多么。</p>
<p>为了实现这样的效果，上面的代码要修改如下：</p>
<div class="codeText">
<div class="codeHead">C++代码</div>
<ol class="dp-cpp">
    <li class="alt"><span><span class="keyword">void</span><span>&nbsp;CSample::SetText(</span><span class="keyword">const</span><span>&nbsp;</span><span class="datatypes">TCHAR</span><span>&nbsp;*pszText）&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></span></li>
    <li class=""><span>{&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;CDC&nbsp;*pDC&nbsp;=&nbsp;</span><span class="keyword">this</span><span>-&gt;GetDC();&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;获取文本在当前绘图环境下所占的宽度和高度&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;CSize&nbsp;clSize&nbsp;=&nbsp;pDC-&gt;GetTextExtent(pszText,&nbsp;_tcslen(pszText));&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;RECT&nbsp;stRect;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;获取控件当前矩形区域&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">this</span><span>-&gt;GetWindowRect(&amp;stRect);&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;调整宽度为新文本所占宽度&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;stRect.right&nbsp;=&nbsp;stRect.left&nbsp;+&nbsp;clSize.cx;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;重要！调用父窗口S2C函数转换坐标&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">this</span><span>-&gt;GetParent()-&gt;ScreenToClient(&amp;stRect);&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;调整控件大小以适应新文本&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">this</span><span>-&gt;MoveWindow(&amp;stRect);&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;重绘控件以避免重影&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">this</span><span>-&gt;GetWindowRect(&amp;stRect);&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">this</span><span>-&gt;GetParent()-&gt;ScreenToClient(&amp;stRect);&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">this</span><span>-&gt;GetParent()-&gt;InvalidateRect(&amp;stRect,&nbsp;</span><span class="keyword">true</span><span>);&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li class=""><span>}&nbsp;&nbsp;&nbsp;</span></li>
</ol>
</div>
<p>同样，<font color="#ff0000">这里也是调用父窗口的S2C函数</font>，这样得到的坐标才能正确使用。代码经过上述修改，就实现了控件随文本动态调整宽度的效果。</p>
<p>以上只是实现Static背景透明、更改文本颜色以及动态调整控件大小的简单演示，实际的应用中可能还需要考虑很多情况，适当修改代码，但基本原理是不变的。当然要获得最大的灵活性，还是得自己来绘制了 - -</p>]]></description>
			<link>http://www.xinyuonline.net/blog/?action=show&amp;id=59</link>
			<category domain="http://www.xinyuonline.net/blog/?cid=5">软件开发</category>
			<pubDate>2008-08-22 21:44</pubDate>
		</item>
		<item>
			<guid>http://www.xinyuonline.net/blog/?action=show&amp;id=56</guid>
			<title>VC6编程枚举SQL服务器及其数据库实体</title>
			<author>
				<![CDATA[Xinyu <xinyu_gg@hotmail.com>]]>
			</author>
			<description><![CDATA[<p>近日接触到一个VC的程序，需要编程实现对某一特定的SQL Server服务器，枚举其所拥有的所有的数据库实体的名称。在MSDN和网上搜寻一番之后，找到一个自认为比较简单的方法，利用SQLBrowseConnect的不完整请求连接调用的特性，来实现枚举。</p>
<p>首先来说说原理，SQLBrowseConnect的第二个参数是一个请求字符串(request string)，用以指定要连接的对象的属性，其格式像这样，&quot;<font color="#0000ff">DRIVER={SQL Server};SERVER=myserver;DATABASE=mydb;UID=sa;PWD=123;APP=myapp;WSID=myws;</font>&quot;(不含引号，下同)，而这个函数有一个特性，当这个请求字符串包含的信息不完整的时候，它会返回提示信息，提示用户还缺少哪些信息，而当SERVER(如&quot;<font color="#0000ff">DRIVER={SQL Server};</font>&quot;)或DATABASE(如&quot;<font color="#0000ff">DRIVER={SQL Server};SERVER=myserver;UID=sa;PWD=123;</font>&quot;)没有指定的时候，它在返回的提示信息里便会包含对SERVER或者DATABASE的枚举，这正是我们所需要的。演示代码如下(为了简洁，省略了部分错误处理代码)：</p>
<div class="codeText">
<div class="codeHead">C++代码</div>
<ol class="dp-cpp">
    <li class="alt"><span><span class="preprocessor">#include&nbsp;&lt;stdio.h&gt; </span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span class="preprocessor">#include&nbsp;&lt;windows.h&gt; </span><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span class="preprocessor">#include&nbsp;&lt;sql.h&gt; </span><span>&nbsp;&nbsp;</span></li>
    <li class=""><span class="preprocessor">#include&nbsp;&lt;sqlext.h&gt; </span><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span class="preprocessor">#include&nbsp;&lt;sqltypes.h&gt; </span><span>&nbsp;&nbsp;</span></li>
    <li class=""><span class="preprocessor">#include&nbsp;&lt;odbcss.h&gt; </span><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span></li>
    <li class=""><span class="preprocessor">#define&nbsp;MAX_STR_LEN&nbsp;4096 </span><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span></li>
    <li class=""><span class="datatypes">int</span><span>&nbsp;main(</span><span class="datatypes">int</span><span>&nbsp;argc,&nbsp;</span><span class="datatypes">char</span><span>&nbsp;*&nbsp;argv[]) &nbsp;&nbsp;</span></li>
    <li class="alt"><span>{ &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="datatypes">char</span><span>&nbsp;szInfo[MAX_STR_LEN]; &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;SQLSMALLINT&nbsp;shLen&nbsp;=&nbsp;0; &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="datatypes">int</span><span>&nbsp;nRet&nbsp;=&nbsp;0; &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="datatypes">HANDLE</span><span>&nbsp;hEnv&nbsp;=&nbsp;SQL_NULL_HANDLE; &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="datatypes">HANDLE</span><span>&nbsp;hDbc&nbsp;=&nbsp;SQL_NULL_HANDLE; &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;allocate&nbsp;SQL&nbsp;environment&nbsp;handle </span><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;SQLAllocHandle(SQL_HANDLE_ENV,&nbsp;SQL_NULL_HANDLE,&nbsp;&amp;hEnv); &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;set&nbsp;SQL&nbsp;environment&nbsp;attribute&nbsp;to&nbsp;ODBC&nbsp;version&nbsp;3 </span><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;SQLSetEnvAttr(hEnv,&nbsp;SQL_ATTR_ODBC_VERSION,&nbsp;(SQLPOINTER)SQL_OV_ODBC3,&nbsp;SQL_IS_INTEGER); &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;allocate&nbsp;SQL&nbsp;connection&nbsp;handle </span><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;SQLAllocHandle(SQL_HANDLE_DBC,&nbsp;hEnv,&nbsp;&amp;hDbc); &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;1st&nbsp;time&nbsp;call,&nbsp;retrieve&nbsp;SQL&nbsp;Server&nbsp;names </span><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;SQLBrowseConnect(hDbc,&nbsp;(SQLCHAR&nbsp;*)</span><span class="string">&quot;Driver={SQL&nbsp;Server};&quot;</span><span>,&nbsp;SQL_NTS,&nbsp;(SQLCHAR&nbsp;*)szInfo,&nbsp;MAX_STR_LEN,&nbsp;&amp;shLen); &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>&nbsp;(nRet&nbsp;==&nbsp;SQL_NEED_DATA) &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span class="string">&quot;%s\n&quot;</span><span>,&nbsp;szInfo); &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;SQLDisconnect(hDbc); &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;2nd&nbsp;time&nbsp;call&nbsp;with&nbsp;server&nbsp;name,&nbsp;retrieve&nbsp;database&nbsp;names&nbsp;&nbsp;&nbsp;&nbsp; </span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;SQLBrowseConnect(hDbc,&nbsp;(SQLCHAR&nbsp;*)</span><span class="string">&quot;Driver={SQL&nbsp;Server};SERVER=(local);UID=sa;PWD=123;&quot;</span><span>),&nbsp;SQL_NTS,&nbsp;(SQLCHAR&nbsp;*)szInfo,&nbsp;MAX_STR_LEN,&nbsp;&amp;shLen); &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>&nbsp;(nRet&nbsp;==&nbsp;SQL_NEED_DATA) &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span class="string">&quot;%s\n&quot;</span><span>,&nbsp;szInfo); &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;SQLDisconnect(hDbc); &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;SQLFreeHandle(SQL_HANDLE_DBC,&nbsp;hDbc); &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;SQLFreeHandle(SQL_HANDLE_ENV,&nbsp;hEnv); &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">return</span><span>&nbsp;0; &nbsp;&nbsp;</span></li>
    <li class="alt"><span>} &nbsp;&nbsp;</span></li>
</ol>
</div>
<p>代码中第二次调用是假定本机为SQL服务器，实际引用时可能需要先调用一次以获取所有的SQL服务器，然后再指定相应的服务器来枚举数据库。</p>
<p>第一次调用后的输出形式如下：</p>
<p>&quot;<font color="#0000ff">SERVER:Server={(local),serv1,serv2,serv3};UID:Login ID=?;PWD:Password=?;*APP:AppName=?;*WSID:WorkStation ID=?;</font>&quot;</p>
<p><font color="#ff0000" size="1">P.S.感谢命令提示行的和谐输出，让我一直把&quot;(local)&quot;看成&quot;&lt;local&gt;&quot;，浪费了不少时间，希望大家不要步我后尘 orz</font></p>
<p>第二次调用后的输出形式如下：</p>
<p>&quot;<font color="#0000ff">*DATABASE:Database={master,model,pubs,tempdb,mydb};*LANGUAGE:Language={us_english,Fran&ccedil;ais,简体中文,繁体中文,日本语};</font>&quot;</p>
<p>剩下的就是稍微对这些返回的信息做做解析就大功告成了。</p>
<p>关于SQLBrowseConnect的其他相关信息可以查阅MSDN。</p>]]></description>
			<link>http://www.xinyuonline.net/blog/?action=show&amp;id=56</link>
			<category domain="http://www.xinyuonline.net/blog/?cid=5">软件开发</category>
			<pubDate>2008-08-02 11:17</pubDate>
		</item>
	</channel>
</rss>
