﻿<?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>BlogJava-SIMONE-随笔分类-C++</title><link>http://www.blogjava.net/wangxinsh55/category/20255.html</link><description /><language>zh-cn</language><lastBuildDate>Wed, 28 May 2008 09:12:24 GMT</lastBuildDate><pubDate>Wed, 28 May 2008 09:12:24 GMT</pubDate><ttl>60</ttl><item><title>MFC多线程编程注意事项</title><link>http://www.blogjava.net/wangxinsh55/archive/2008/05/28/203464.html</link><dc:creator>SIMONE</dc:creator><author>SIMONE</author><pubDate>Wed, 28 May 2008 05:08:00 GMT</pubDate><guid>http://www.blogjava.net/wangxinsh55/archive/2008/05/28/203464.html</guid><wfw:comment>http://www.blogjava.net/wangxinsh55/comments/203464.html</wfw:comment><comments>http://www.blogjava.net/wangxinsh55/archive/2008/05/28/203464.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wangxinsh55/comments/commentRss/203464.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wangxinsh55/services/trackbacks/203464.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: MFC多线程编程注意事项PeterLee整理 2008-05-261.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 表现——错误示例关于启动线程时传输窗口对象（指针？句柄？）的问题： &nbsp; &nbsp; &nbsp; &nbsp; 在选择菜单中的开始线程后： &nbsp; &nbsp; void &nbsp; cmainfra...&nbsp;&nbsp;<a href='http://www.blogjava.net/wangxinsh55/archive/2008/05/28/203464.html'>阅读全文</a><img src ="http://www.blogjava.net/wangxinsh55/aggbug/203464.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wangxinsh55/" target="_blank">SIMONE</a> 2008-05-28 13:08 <a href="http://www.blogjava.net/wangxinsh55/archive/2008/05/28/203464.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>共享内存封装类 </title><link>http://www.blogjava.net/wangxinsh55/archive/2008/05/16/200807.html</link><dc:creator>SIMONE</dc:creator><author>SIMONE</author><pubDate>Fri, 16 May 2008 02:36:00 GMT</pubDate><guid>http://www.blogjava.net/wangxinsh55/archive/2008/05/16/200807.html</guid><wfw:comment>http://www.blogjava.net/wangxinsh55/comments/200807.html</wfw:comment><comments>http://www.blogjava.net/wangxinsh55/archive/2008/05/16/200807.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wangxinsh55/comments/commentRss/200807.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wangxinsh55/services/trackbacks/200807.html</trackback:ping><description><![CDATA[<div>http://blog.chinaunix.net/u1/59687/showart_496412.html<br />
<br />
本文介绍一个共享内存封装类，使共享内存的使用更简单化，特别适合更懒的程序员使用:-)<br />
<br />
<strong><img height="16" src="http://vckbase.com/document/image/paragraph.gif" width="14"  alt="" /> 一、实现目标：</strong>简单化使用共享内存<br />
<br />
<strong><img height="16" src="http://vckbase.com/document/image/paragraph.gif" width="14"  alt="" /> 二、使用说明</strong>：<br />
<br />
<strong>1. 创建共享内存CSFMServer对象</strong>, 需要为CSFMServer对象指定专用的名字，只要<br />
系统中存在一个这样的对象，就可以在其他程序中简单方便地使用该共享内存。
<pre>CSFMServer(char *szFileName, char *szMapName, DWORD dwSize);
Create(char *szFileName, char *szMapName, DWORD dwSize); </pre>
参数1:NULL或指定的文件(将创建或打开并读写/麻烦) <br />
参数2:要创建的共享内存对象名 <br />
参数3:要创建的共享内存对象大小 <br />
<br />
例如
<pre>m_SFMS.Create(NULL, "_ZZZ_OBJ_", 1);</pre>
<strong>2. 本地使用共享内存</strong> <br />
使用 LPVOID GetBuffer() 返回共享内存地址,例如
<pre>char *p = (char*)m_SFMS.GetBuffer();
if (p)
strcpy(p, "1234567890");</pre>
<strong>3. 创建共享内存CSFMClient对象</strong>，也需要为CSFMClient对象指定专用的名字（上一步使用的那个），即可使用共享内存。
<pre>CSFMClient(DWORD dwAccess, char *szMapName);
Open(DWORD dwAccess, char *szMapName);
</pre>
参数1:共享内存对象访问方式(FILE_MAP_READ|FILE_MAP_WRITE)<br />
参数2:共享内存对象名<br />
<br />
例如:
<pre>CSFMClient *pCSFMC = new CSFMClient(FILE_MAP_READ, "_OBJ_ZZZ_");</pre>
<strong>4. 本地使用共享内存</strong><br />
使用 LPVOID GetBuffer() 返回共享内存地址，例如
<pre>char *p = (char*)pCSFMC-&gt;GetBuffer();
if (p) strcpy(p, "1234567890");
</pre>
<br />
<strong><img height="16" src="http://vckbase.com/document/image/paragraph.gif" width="14"  alt="" /> 三、该类的具体实现代码</strong>如下:<br />
<br />
<font color="#009900">//------------------------------------------------<br />
// 2002/07/05 <br />
// awzzz <br />
// SFMMem.h: interface for the CSFMServer class. <br />
//------------------------------------------------</font>
<pre>#if !defined(AFX_SFMSERVER_H__2D76A439_6388_4B07_AE7A_C82F458642ED__INCLUDED_)
#define AFX_SFMSERVER_H__2D76A439_6388_4B07_AE7A_C82F458642ED__INCLUDED_
#if _MSC_VER &gt; 1000
#pragma once
#endif // _MSC_VER &gt; 1000
#define DEFAULT_FILENAME NULL
#define DEFAULT_MAPNAME  "_SFM_OBJ_"
#define DEFAULT_MAPSIZE  (0xFFFF + 1)
</pre>
<font color="#009900">// Shared FileMap Server </font>
<pre>class CSFMServer
{
public:
CSFMServer();
virtual ~CSFMServer();
CSFMServer(char *szFileName, char *szMapName, DWORD dwSize);
protected:
HANDLE m_hFile;
HANDLE m_hFileMap;
LPVOID m_lpFileMapBuffer;
char *m_pFileName;
char *m_pMapName;
DWORD m_dwSize;
int  m_iCreateFlag;
private:
void _Init();
void _Destory();
public:
void Create(char *szFileName, char *szMapName, DWORD dwSize);
LPVOID GetBuffer();
DWORD GetSize();
};
</pre>
<font color="#009900">// Shared FileMap Client<br />
</font>
<pre>class CSFMClient
{
public:
CSFMClient();
virtual ~CSFMClient();
CSFMClient(DWORD dwAccess, char *szMapName);
protected:
HANDLE m_hFileMap;
LPVOID m_lpFileMapBuffer;
char *m_pMapName;
int  m_iOpenFlag;
private:
void _Init();
void _Destory();
public:
void Open(DWORD dwAccess, char *szMapName);
LPVOID GetBuffer();
DWORD GetSize();
};
#endif // !defined(AFX_SFMSERVER_H__2D76A439_6388_4B07_AE7A_C82F458642ED__INCLUDED_)
</pre>
<font color="#009900">//------------------------------------------------------<br />
// SFMMem.cpp: implementation of the CSFMServer class. <br />
//------------------------------------------------------</font><br />
<pre>#include "stdafx.h"
#include "SFMMem.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
<font color="#009900">// Construction/Destruction</font>
CSFMServer::CSFMServer()
{
_Init();
//
Create(DEFAULT_FILENAME, DEFAULT_MAPNAME, DEFAULT_MAPSIZE);
}
CSFMServer::~CSFMServer()
{
_Destory();
}
CSFMServer::CSFMServer(char *szFileName, char *szMapName, DWORD dwSize)
{
_Init();
//
Create(szFileName, szMapName, dwSize);
}
void CSFMServer::_Init()
{
m_hFile = NULL;
m_hFileMap = NULL;
m_lpFileMapBuffer = NULL;
m_pFileName = NULL;
m_pMapName = NULL;
m_dwSize = 0;
m_iCreateFlag = 0;
}
void CSFMServer::_Destory()
{
if (m_lpFileMapBuffer)
{
UnmapViewOfFile(m_lpFileMapBuffer);
m_lpFileMapBuffer = NULL;
}
if (m_hFileMap)
{
CloseHandle(m_hFileMap);
m_hFileMap = NULL;
}
if (m_hFile &amp;&amp; m_hFile != INVALID_HANDLE_VALUE)
{
CloseHandle(m_hFile);
m_hFile = NULL;
}
if (m_pFileName)
{
free(m_pFileName);
m_pFileName = NULL;
}
if (m_pMapName)
{
free(m_pMapName);
m_pMapName = NULL;
}
_Init();
}
void CSFMServer::Create(char *szFileName, char *szMapName, DWORD dwSize)
{
if (m_iCreateFlag)
_Destory();
if (szFileName)
m_pFileName = _strdup(szFileName);
//else m_pFileName = NULL;
if (szMapName)
m_pMapName = _strdup(szMapName);
else m_pMapName = _strdup(DEFAULT_MAPNAME);
if (dwSize &gt; 0)
m_dwSize = dwSize;
else m_dwSize = DEFAULT_MAPSIZE;
if (m_pFileName)
{
// file
m_hFile = CreateFile(
m_pFileName,
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,
OPEN_ALWAYS,//OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
}
else
{
// system
m_hFile = (HANDLE)0xFFFFFFFF;
}
if (m_hFile)
{
m_hFileMap = CreateFileMapping(
m_hFile,
NULL,
PAGE_READWRITE,
0,
m_dwSize,
m_pMapName
);
//使只有一个CSFMServer对象能操作内存对象
//if (m_hFileMap != NULL &amp;&amp; ERROR_ALREADY_EXISTS == GetLastError())
//{
// CloseHandle(m_hFileMap);
// m_hFileMap = NULL;
//}
}
if (m_hFileMap)
{
m_lpFileMapBuffer = MapViewOfFile(
m_hFileMap,
FILE_MAP_ALL_ACCESS,//FILE_MAP_WRITE|FILE_MAP_READ,
0,
0,
m_dwSize
);
}
m_iCreateFlag = 1;
}
LPVOID CSFMServer::GetBuffer()
{
return (m_lpFileMapBuffer)?(m_lpFileMapBuffer):(NULL);
}
DWORD CSFMServer::GetSize()
{
return m_dwSize;
}
<font color="#009900">// Construction/Destruction</font>
CSFMClient::CSFMClient()
{
_Init();
//
Open(FILE_MAP_READ, DEFAULT_MAPNAME);
}
CSFMClient::~CSFMClient()
{
_Destory();
}
CSFMClient::CSFMClient(DWORD dwAccess, char *szMapName)
{
_Init();
//
Open(dwAccess, szMapName);
}
void CSFMClient::Open(DWORD dwAccess, char *szMapName)
{
if (m_iOpenFlag)
_Destory();
if (szMapName)
m_pMapName = _strdup(szMapName);
else m_pMapName = _strdup(DEFAULT_MAPNAME);
m_hFileMap = OpenFileMapping(
dwAccess,
TRUE,
m_pMapName
);
if (m_hFileMap)
{
m_lpFileMapBuffer = MapViewOfFile(
m_hFileMap,
dwAccess,
0,
0,
0
);
}
m_iOpenFlag = 1;
}
void CSFMClient::_Init()
{
m_hFileMap = NULL;
m_lpFileMapBuffer = NULL;
m_pMapName = NULL;
m_iOpenFlag = 0;
}
void CSFMClient::_Destory()
{
if (m_lpFileMapBuffer)
{
UnmapViewOfFile(m_lpFileMapBuffer);
m_lpFileMapBuffer = NULL;
}
if (m_hFileMap)
{
CloseHandle(m_hFileMap);
m_hFileMap = NULL;
}
if (m_pMapName)
{
free(m_pMapName);
m_pMapName = NULL;
}
_Init();
}
LPVOID CSFMClient::GetBuffer()
{
return (m_lpFileMapBuffer)?(m_lpFileMapBuffer):(NULL);
}
DWORD CSFMClient::GetSize()
{
// unnable use
return 0;
}
</pre>
测试环境：Win2K+VC6+普通应用程序<br />
未对服务程序/ISAPI等其他应用进行测试，应该也是可以的<br />
</div>
<!-- 广告开始 -->
<img src ="http://www.blogjava.net/wangxinsh55/aggbug/200807.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wangxinsh55/" target="_blank">SIMONE</a> 2008-05-16 10:36 <a href="http://www.blogjava.net/wangxinsh55/archive/2008/05/16/200807.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Socket基础</title><link>http://www.blogjava.net/wangxinsh55/archive/2008/05/15/200596.html</link><dc:creator>SIMONE</dc:creator><author>SIMONE</author><pubDate>Thu, 15 May 2008 03:29:00 GMT</pubDate><guid>http://www.blogjava.net/wangxinsh55/archive/2008/05/15/200596.html</guid><wfw:comment>http://www.blogjava.net/wangxinsh55/comments/200596.html</wfw:comment><comments>http://www.blogjava.net/wangxinsh55/archive/2008/05/15/200596.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wangxinsh55/comments/commentRss/200596.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wangxinsh55/services/trackbacks/200596.html</trackback:ping><description><![CDATA[<p class="MsoNormal" style="margin: 0cm 0cm 0pt"><font size="3"><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">一．</span><span lang="EN-US"><font face="Times New Roman">socket</font></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的一些基本结构</span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt"><font size="3"><span lang="EN-US"><font face="Times New Roman">1</font></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">．</span><span lang="EN-US"><font face="Times New Roman">32</font></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">位的</span><span lang="EN-US"><font face="Times New Roman">IP</font></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">地址：</span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 21.75pt"><span lang="EN-US"><font face="Times New Roman" size="3">struct in_addr</font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 21.75pt"><span lang="EN-US"><font face="Times New Roman" size="3">{</font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 21.75pt"><span lang="EN-US"><font face="Times New Roman"><font size="3"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>unsigned long s_addr;</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 21.75pt"><span lang="EN-US"><font face="Times New Roman" size="3">}</font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt"><span lang="EN-US"><O:P><font face="Times New Roman" size="3">&nbsp;</font></O:P></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt"><font size="3"><span lang="EN-US"><font face="Times New Roman">2</font></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">．</span><span lang="EN-US"><font face="Times New Roman">TCP/IP</font></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">所在的网际通信域使用的套接字地址格式：</span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span lang="EN-US"><font face="Times New Roman" size="3">struct sockaddr_in</font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span lang="EN-US"><font face="Times New Roman" size="3">{</font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 21pt"><font size="3"><span lang="EN-US"><font face="Times New Roman"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>short int sin_family;<span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; </span>//</font></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">表示所属地址簇，</span><span lang="EN-US"><font face="Times New Roman">TCP/IP</font></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">必须是</span><span lang="EN-US"><font face="Times New Roman">AF_INET</font></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 21pt"><font size="3"><span lang="EN-US"><font face="Times New Roman"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>unsigned short int sin_port;<span style="mso-spacerun: yes">&nbsp;&nbsp; </span>//</font></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">表示端口号</span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 21pt"><font size="3"><span lang="EN-US"><font face="Times New Roman"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>struct in_addr sin_addr; <span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span>//</font></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">表示</span><span lang="EN-US"><font face="Times New Roman">32</font></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">位的</span><span lang="EN-US"><font face="Times New Roman">IP</font></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">地址，用</span><span lang="EN-US"><font face="Times New Roman">in_addr</font></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">结构表示</span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 21pt"><font size="3"><span lang="EN-US"><font face="Times New Roman"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>unsigned char sin_zero[8];<span style="mso-spacerun: yes">&nbsp;&nbsp; </span>//</font></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">表示全部填充</span><span lang="EN-US"><font face="Times New Roman">0</font></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，保证和</span><span lang="EN-US"><font face="Times New Roman">sockaddr</font></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">大小相同</span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span lang="EN-US"><font face="Times New Roman" size="3">}</font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt"><span lang="EN-US"><O:P><font face="Times New Roman" size="3">&nbsp;</font></O:P></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt"><font size="3"><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">二．</span><span lang="EN-US"><font face="Times New Roman">socket</font></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的一些辅助函数</span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt"><font size="3"><span lang="EN-US"><font face="Times New Roman">1</font></span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">．字节序转换函数：</span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt"><font size="3"><span lang="EN-US"><span style="mso-spacerun: yes"><font face="Times New Roman">&nbsp; </font></span></span><span lang="EN-US" style="font-family: 宋体"><span style="mso-spacerun: yes">&nbsp; </span></span><span style="font-family: 宋体">&#183;<span lang="EN-US">htons : host to network byte order , short (unsigned) integer<O:P></O:P></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt"><font size="3"><span lang="EN-US" style="font-family: 宋体"><span style="mso-spacerun: yes">&nbsp;&nbsp; </span></span><span style="font-family: 宋体">&#183;<span lang="EN-US">htonl : host to network byte order , long (unsigned) integer<O:P></O:P></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt"><font size="3"><span lang="EN-US" style="font-family: 宋体"><span style="mso-spacerun: yes">&nbsp;&nbsp; </span></span><span style="font-family: 宋体">&#183;<span lang="EN-US">ntohs : network to host byte order , short (unsigned) integer<O:P></O:P></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt"><font size="3"><span lang="EN-US" style="font-family: 宋体"><span style="mso-spacerun: yes">&nbsp;&nbsp; </span></span><span style="font-family: 宋体">&#183;<span lang="EN-US">ntohl : network to host byte order , long (unsigned) integer<O:P></O:P></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: 宋体"><font size="3">记忆方法：<span lang="EN-US"><O:P></O:P></span></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 21pt"><font size="3"><span lang="EN-US" style="font-family: 宋体"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>h</span><span style="font-family: 宋体">表示<span lang="EN-US">host</span>，<span lang="EN-US">n</span>表示<span lang="EN-US">network</span>，<span lang="EN-US">l</span>表示<span lang="EN-US">long</span>，<span lang="EN-US">s</span>表示<span lang="EN-US">short<O:P></O:P></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: 宋体"><font size="3">例如：<span lang="EN-US"><O:P></O:P></span></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span lang="EN-US" style="font-family: 宋体"><font size="3"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>num = htons(Port_NUM);<O:P></O:P></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 21pt"><font size="3"><span lang="EN-US" style="font-family: 宋体"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体">表示将一个名叫<span lang="EN-US">Port_NUM</span>的端口号转换成网络字节顺序并赋值给<span lang="EN-US">num</span>变量<span lang="EN-US"><O:P></O:P></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt"><span lang="EN-US" style="font-family: 宋体"><O:P><font size="3">&nbsp;</font></O:P></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt"><font size="3"><span lang="EN-US" style="font-family: 宋体">2</span><span style="font-family: 宋体">．<span lang="EN-US">IP</span>地址转换函数：<span lang="EN-US"><O:P></O:P></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt"><font size="3"><span lang="EN-US" style="font-family: 宋体"><span style="mso-spacerun: yes">&nbsp;&nbsp; </span></span><span style="font-family: 宋体">&#183;<span lang="EN-US">inet_addr()<O:P></O:P></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 26.25pt; text-indent: -26.25pt; mso-char-indent-count: -2.5"><font size="3"><span lang="EN-US" style="font-family: 宋体"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体">将一个用点分十进制表示的<span lang="EN-US">IP</span>地址字符串转换成<span lang="EN-US">32</span>位无符号整数。此整数已经是</span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 26.25pt; text-indent: -26.25pt; mso-char-indent-count: -2.5"><font size="3"><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;网络字节顺序，无需再调用<span lang="EN-US">htonl()<O:P></O:P></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 26.25pt; text-indent: -26.25pt; mso-char-indent-count: -2.5"><font size="3"><span lang="EN-US" style="font-family: 宋体"><span style="mso-spacerun: yes">&nbsp;</span><span style="mso-spacerun: yes">&nbsp;</span><span style="mso-spacerun: yes">&nbsp;</span></span><span style="font-family: 宋体">&#183;<span lang="EN-US">inet_ntoa()<O:P></O:P></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 26.25pt; text-indent: -26.25pt; mso-char-indent-count: -2.5"><font size="3"><span lang="EN-US" style="font-family: 宋体"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体">将一个<span lang="EN-US">32</span>位无符号整数转换成一个点分十进制表示的<span lang="EN-US">IP</span>地址字符串，此函数接受</span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 26.25pt; text-indent: -26.25pt; mso-char-indent-count: -2.5"><font size="3"><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp;&nbsp; 一个<span lang="EN-US">in_addr</span>结构作为参数，将返回的字符串存储再一个<span lang="EN-US">static</span>的缓冲区内，因</span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 26.25pt; text-indent: -26.25pt; mso-char-indent-count: -2.5"><font size="3"><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp;&nbsp; 此，下次调用此函数时，将改变上次调用的结果。<span lang="EN-US"><O:P></O:P></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 26.25pt; text-indent: -26.25pt; mso-char-indent-count: -2.5"><span lang="EN-US" style="font-family: 宋体"><O:P><font size="3">&nbsp;</font></O:P></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 26.25pt; text-indent: -26.25pt; mso-char-indent-count: -2.5"><font size="3"><span lang="EN-US" style="font-family: 宋体">3</span><span style="font-family: 宋体">．查找主机信息函数：<span lang="EN-US"><O:P></O:P></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 26.25pt; text-indent: -26.25pt; mso-char-indent-count: -2.5"><span lang="EN-US" style="font-family: 宋体"><font size="3"><span style="mso-spacerun: yes">&nbsp;&nbsp; </span>struct hostent* gethostbyname(const char *name);<O:P></O:P></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 26.25pt; text-indent: -26.25pt; mso-char-indent-count: -2.5"><font size="3"><span lang="EN-US" style="font-family: 宋体"><span style="mso-spacerun: yes">&nbsp;&nbsp; </span></span><span style="font-family: 宋体">参数：输入参数时需要解析的主机名，也可以是域名<span lang="EN-US"><O:P></O:P></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 26.25pt; text-indent: -26.25pt; mso-char-indent-count: -2.5"><font size="3"><span lang="EN-US" style="font-family: 宋体"><span style="mso-spacerun: yes">&nbsp;&nbsp; </span></span><span style="font-family: 宋体">返回值：返回一个描述主机信息的结构<span lang="EN-US">hostent</span>的指针<span lang="EN-US"><O:P></O:P></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 26.25pt; text-indent: -26.25pt; mso-char-indent-count: -2.5"><span lang="EN-US" style="font-family: 宋体"><font size="3"><span style="mso-spacerun: yes">&nbsp;&nbsp; </span>struct hostent<O:P></O:P></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 26.25pt; text-indent: -26.25pt; mso-char-indent-count: -2.5"><span lang="EN-US" style="font-family: 宋体"><font size="3"><span style="mso-spacerun: yes">&nbsp;&nbsp; </span>{<O:P></O:P></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 26.25pt; text-indent: -26.25pt; mso-char-indent-count: -2.5"><font size="3"><span lang="EN-US" style="font-family: 宋体"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>char *h_name;<span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>//</span><span style="font-family: 宋体">主机的正式名称<span lang="EN-US"><O:P></O:P></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 26.25pt; text-indent: -26.25pt; mso-char-indent-count: -2.5"><font size="3"><span lang="EN-US" style="font-family: 宋体"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>char **h_aliases;<span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>//</span><span style="font-family: 宋体">主机的别名<span lang="EN-US"><O:P></O:P></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 26.25pt; text-indent: -26.25pt; mso-char-indent-count: -2.5"><font size="3"><span lang="EN-US" style="font-family: 宋体"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int h_addrtype;<span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>//</span><span style="font-family: 宋体">主机地址类型 <span lang="EN-US"><O:P></O:P></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 26.25pt; text-indent: -26.25pt; mso-char-indent-count: -2.5"><font size="3"><span lang="EN-US" style="font-family: 宋体"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int h_length;<span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>//</span><span style="font-family: 宋体">地址长度<span lang="EN-US"><O:P></O:P></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 26.25pt; text-indent: -26.25pt; mso-char-indent-count: -2.5"><font size="3"><span lang="EN-US" style="font-family: 宋体"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>char **h_addr_list;<span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>//</span><span style="font-family: 宋体">存储主机地址的数组<span lang="EN-US"><O:P></O:P></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 26.25pt; text-indent: -26.25pt; mso-char-indent-count: -2.5"><font size="3"><span lang="EN-US" style="font-family: 宋体"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>#define h_addr h_addr_list[0]<span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>//</span><span style="font-family: 宋体">为向后兼容<span lang="EN-US"><O:P></O:P></span></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 21pt; mso-char-indent-count: 2.0"><span lang="EN-US" style="font-family: 宋体"><font size="3">};<O:P></O:P></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 21pt; mso-para-margin-left: 2.0gd"><span style="font-family: 宋体"><font size="3">对于<span lang="EN-US">TCP/IP</span>协议而言，<span lang="EN-US">h_addrtype</span>的值等于<span lang="EN-US">AF_INET</span>，<span lang="EN-US">h_length</span>的值是<span lang="EN-US">4</span>，因为<span lang="EN-US">IP</span>地址是<span lang="EN-US">4</span>字节。<span lang="EN-US">h_addr_list</span>实质上是一个存储地址的缓冲区，如果后续的代码要利用这个缓冲区，需要进行强制类型转换。</font></span></p>
<p>&nbsp;</p>
<img src ="http://www.blogjava.net/wangxinsh55/aggbug/200596.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wangxinsh55/" target="_blank">SIMONE</a> 2008-05-15 11:29 <a href="http://www.blogjava.net/wangxinsh55/archive/2008/05/15/200596.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Socket中如何设置连接超时【转】</title><link>http://www.blogjava.net/wangxinsh55/archive/2008/05/12/200054.html</link><dc:creator>SIMONE</dc:creator><author>SIMONE</author><pubDate>Mon, 12 May 2008 09:09:00 GMT</pubDate><guid>http://www.blogjava.net/wangxinsh55/archive/2008/05/12/200054.html</guid><wfw:comment>http://www.blogjava.net/wangxinsh55/comments/200054.html</wfw:comment><comments>http://www.blogjava.net/wangxinsh55/archive/2008/05/12/200054.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wangxinsh55/comments/commentRss/200054.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wangxinsh55/services/trackbacks/200054.html</trackback:ping><description><![CDATA[<span style="color: #333333; font-family: 宋体">
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; line-height: normal; text-align: left; mso-pagination: widow-orphan; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto" align="left"><span style="color: #333333; font-family: 宋体; mso-ascii-font-family: &aposCourier New' mso-hansi-font-family:&aposCourier New' mso-bidi-font-family:&aposCourier New' mso-font-kerning: 0pt"><a href="http://fcxz.blogbus.com/logs/1564481.html">http://fcxz.blogbus.com/logs/1564481.html</a><br />
<br />
<br />
&nbsp;&nbsp;&nbsp; 设置</span><span lang="EN-US" style="color: #333333; font-family: "  Courier New?; mso-font-kerning: 0pt?>connect</span><span style="color: #333333; font-family: 宋体; mso-ascii-font-family: &aposCourier New' mso-hansi-font-family:&aposCourier New' mso-bidi-font-family:&aposCourier New' mso-font-kerning: 0pt">的超时很简单，</span><span lang="EN-US" style="color: #333333; font-family: "  Courier New?; mso-font-kerning: 0pt?>CSDN</span><span style="color: #333333; font-family: 宋体; mso-ascii-font-family: &aposCourier New' mso-hansi-font-family:&aposCourier New' mso-bidi-font-family:&aposCourier New' mso-font-kerning: 0pt">上也有人提到过使用</span><span lang="EN-US" style="color: #333333; font-family: "  Courier New?; mso-font-kerning: 0pt?>select</span><span style="color: #333333; font-family: 宋体; mso-ascii-font-family: &aposCourier New' mso-hansi-font-family:&aposCourier New' mso-bidi-font-family:&aposCourier New' mso-font-kerning: 0pt">，但却没有一个令人满意与完整的答案。偶所讲的也正是</span><span lang="EN-US" style="color: #333333; font-family: "  Courier New?; mso-font-kerning: 0pt?>select</span><span style="color: #333333; font-family: 宋体; mso-ascii-font-family: &aposCourier New' mso-hansi-font-family:&aposCourier New' mso-bidi-font-family:&aposCourier New' mso-font-kerning: 0pt">函数，此函数集成在</span><span lang="EN-US" style="color: #333333; font-family: "  Courier New?; mso-font-kerning: 0pt?>winsock1.1</span><span style="color: #333333; font-family: 宋体; mso-ascii-font-family: &aposCourier New' mso-hansi-font-family:&aposCourier New' mso-bidi-font-family:&aposCourier New' mso-font-kerning: 0pt">中，简单点讲，</span><span lang="EN-US" style="color: #333333; font-family: "  Courier New?; mso-font-kerning: 0pt?>"</span><span style="color: #333333; font-family: 宋体; mso-ascii-font-family: &aposCourier New' mso-hansi-font-family:&aposCourier New' mso-bidi-font-family:&aposCourier New' mso-font-kerning: 0pt">作用使那些想避免在套接字调用过程中被锁定的应用程序，采取一种有序的方式，同时对多个套接字进行管理</span><span lang="EN-US" style="color: #333333; font-family: "  Courier New?; mso-font-kerning: 0pt?>"(</span><span style="color: #333333; font-family: 宋体; mso-ascii-font-family: &aposCourier New' mso-hansi-font-family:&aposCourier New' mso-bidi-font-family:&aposCourier New' mso-font-kerning: 0pt">《</span><span lang="EN-US" style="color: #333333; font-family: "  Courier New?; mso-font-kerning: 0pt?>Windows</span><span style="color: #333333; font-family: 宋体; mso-ascii-font-family: &aposCourier New' mso-hansi-font-family:&aposCourier New' mso-bidi-font-family:&aposCourier New' mso-font-kerning: 0pt">网络编程技术》原话</span><span lang="EN-US" style="color: #333333; font-family: "  Courier New?; mso-font-kerning: 0pt?>)</span><span style="color: #333333; font-family: 宋体; mso-ascii-font-family: &aposCourier New' mso-hansi-font-family:&aposCourier New' mso-bidi-font-family:&aposCourier New' mso-font-kerning: 0pt">。使用方法与解释请见《</span><span lang="EN-US" style="color: #333333; font-family: "  Courier New?; mso-font-kerning: 0pt?>Windows</span><span style="color: #333333; font-family: 宋体; mso-ascii-font-family: &aposCourier New' mso-hansi-font-family:&aposCourier New' mso-bidi-font-family:&aposCourier New' mso-font-kerning: 0pt">网络编程技术》。</span><span lang="EN-US" style="color: #333333; font-family: "  Courier New?; mso-font-kerning: 0pt?><br />
</span><span style="color: #333333; font-family: 宋体; mso-ascii-font-family: "  Courier New?; mso-font-kerning: 0pt? mso-hansi-font-family:?Courier mso-bidi-font-family:?Courier>　　在使用此函数前，需先将</span><span lang="EN-US" style="color: #333333; font-family: "  Courier New?; mso-font-kerning: 0pt?>socket</span><span style="color: #333333; font-family: 宋体; mso-ascii-font-family: "  Courier New?; mso-font-kerning: 0pt? mso-hansi-font-family:?Courier mso-bidi-font-family:?Courier>设置为非锁定模式，这样，在</span><span lang="EN-US" style="color: #333333; font-family: "  Courier New?; mso-font-kerning: 0pt?>connect</span><span style="color: #333333; font-family: 宋体; mso-ascii-font-family: "  Courier New?; mso-font-kerning: 0pt? mso-hansi-font-family:?Courier mso-bidi-font-family:?Courier>时，才会立马跳过，同时，通常也会产生一个</span><span lang="EN-US" style="color: #333333; font-family: "  Courier New?; mso-font-kerning: 0pt?>WSAEWOULDBLOCK</span><span style="color: #333333; font-family: 宋体; mso-ascii-font-family: "  Courier New?; mso-font-kerning: 0pt? mso-hansi-font-family:?Courier mso-bidi-font-family:?Courier>错误，这个错误没关系。再执行</span><span lang="EN-US" style="color: #333333; font-family: "  Courier New?; mso-font-kerning: 0pt?>select</span><span style="color: #333333; font-family: 宋体; mso-ascii-font-family: "  Courier New?; mso-font-kerning: 0pt? mso-hansi-font-family:?Courier mso-bidi-font-family:?Courier>则是真正的超时。</span><span lang="EN-US" style="color: #333333; font-family: Arial; mso-font-kerning: 0pt">
<p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; line-height: normal; text-align: left; mso-pagination: widow-orphan; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto" align="left"><span lang="EN-US" style="color: #333333; font-family: "  Courier New?; mso-font-kerning: 0pt?>WSADATA wsd;<br />
SOCKET cClient;<br />
int ret;<br />
struct sockaddr_in server;<br />
hostent *host=NULL;</span><span lang="EN-US" style="color: #333333; font-family: Arial; mso-font-kerning: 0pt">
<p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; line-height: normal; text-align: left; mso-pagination: widow-orphan; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto" align="left"><span lang="EN-US" style="color: #333333; font-family: "  Courier New?; mso-font-kerning: 0pt?>if(WSAStartup(MAKEWORD(2,0),&amp;wsd)){return 0;}<br />
cClient=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);<br />
if(cClient==INVALID_SOCKET){return 0;}<br />
//set Recv and Send time out<br />
int TimeOut=6000; //</span><span style="color: #333333; font-family: 宋体; mso-ascii-font-family: "  Courier New?; mso-font-kerning: 0pt? mso-hansi-font-family:?Courier mso-bidi-font-family:?Courier>设置发送超时</span><span lang="EN-US" style="color: #333333; font-family: "  Courier New?; mso-font-kerning: 0pt?>6</span><span style="color: #333333; font-family: 宋体; mso-ascii-font-family: "  Courier New?; mso-font-kerning: 0pt? mso-hansi-font-family:?Courier mso-bidi-font-family:?Courier>秒</span><span lang="EN-US" style="color: #333333; font-family: "  Courier New?; mso-font-kerning: 0pt?><br />
if(::setsockopt(cClient,SOL_SOCKET,SO_SNDTIMEO,(char *)&amp;TimeOut,sizeof(TimeOut))==SOCKET_ERROR){<br />
return 0;<br />
}<br />
TimeOut=6000;//</span><span style="color: #333333; font-family: 宋体; mso-ascii-font-family: "  Courier New?; mso-font-kerning: 0pt? mso-hansi-font-family:?Courier mso-bidi-font-family:?Courier>设置接收超时</span><span lang="EN-US" style="color: #333333; font-family: "  Courier New?; mso-font-kerning: 0pt?>6</span><span style="color: #333333; font-family: 宋体; mso-ascii-font-family: "  Courier New?; mso-font-kerning: 0pt? mso-hansi-font-family:?Courier mso-bidi-font-family:?Courier>秒</span><span lang="EN-US" style="color: #333333; font-family: "  Courier New?; mso-font-kerning: 0pt?><br />
if(::setsockopt(cClient,SOL_SOCKET,SO_RCVTIMEO,(char *)&amp;TimeOut,sizeof(TimeOut))==SOCKET_ERROR){<br />
return 0;<br />
}<br />
//</span><span style="color: #333333; font-family: 宋体; mso-ascii-font-family: "  Courier New?; mso-font-kerning: 0pt? mso-hansi-font-family:?Courier mso-bidi-font-family:?Courier>设置非阻塞方式连接</span><span lang="EN-US" style="color: #333333; font-family: "  Courier New?; mso-font-kerning: 0pt?><br />
unsigned long ul = 1;<br />
ret = ioctlsocket(cClient, FIONBIO, (unsigned long*)&amp;ul);<br />
if(ret==SOCKET_ERROR)return 0;</span><span lang="EN-US" style="color: #333333; font-family: Arial; mso-font-kerning: 0pt">
<p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; line-height: normal; text-align: left; mso-pagination: widow-orphan; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto" align="left"><span lang="EN-US" style="color: #333333; font-family: "  Courier New?; mso-font-kerning: 0pt?>//</span><span style="color: #333333; font-family: 宋体; mso-ascii-font-family: "  Courier New?; mso-font-kerning: 0pt? mso-hansi-font-family:?Courier mso-bidi-font-family:?Courier>连接</span><span lang="EN-US" style="color: #333333; font-family: "  Courier New?; mso-font-kerning: 0pt?><br />
server.sin_family = AF_INET;<br />
server.sin_port = htons(25);<br />
server.sin_addr .s_addr = inet_addr((LPCSTR)pSmtp);<br />
if(server.sin_addr.s_addr == INADDR_NONE){return 0;}</span><span lang="EN-US" style="color: #333333; font-family: Arial; mso-font-kerning: 0pt">
<p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; line-height: normal; text-align: left; mso-pagination: widow-orphan; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto" align="left"><span lang="EN-US" style="color: #333333; font-family: "  Courier New?; mso-font-kerning: 0pt?>connect(cClient,(const struct sockaddr *)&amp;server,sizeof(server));</span><span lang="EN-US" style="color: #333333; font-family: Arial; mso-font-kerning: 0pt">
<p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; line-height: normal; text-align: left; mso-pagination: widow-orphan; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto" align="left"><span lang="EN-US" style="color: #333333; font-family: "  Courier New?; mso-font-kerning: 0pt?>//select</span><span style="color: #333333; font-family: 宋体; mso-ascii-font-family: "  Courier New?; mso-font-kerning: 0pt? mso-hansi-font-family:?Courier mso-bidi-font-family:?Courier>模型，即设置超时</span><span lang="EN-US" style="color: #333333; font-family: "  Courier New?; mso-font-kerning: 0pt?><br />
struct timeval timeout ;<br />
fd_set r;</span><span lang="EN-US" style="color: #333333; font-family: Arial; mso-font-kerning: 0pt">
<p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; line-height: normal; text-align: left; mso-pagination: widow-orphan; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto" align="left"><span lang="EN-US" style="color: #333333; font-family: "  Courier New?; mso-font-kerning: 0pt?>FD_ZERO(&amp;r);<br />
FD_SET(cClient,&amp;r);<br />
timeout.tv_sec = 15; //</span><span style="color: #333333; font-family: 宋体; mso-ascii-font-family: "  Courier New?; mso-font-kerning: 0pt? mso-hansi-font-family:?Courier mso-bidi-font-family:?Courier>连接超时</span><span lang="EN-US" style="color: #333333; font-family: "  Courier New?; mso-font-kerning: 0pt?>15</span><span style="color: #333333; font-family: 宋体; mso-ascii-font-family: "  Courier New?; mso-font-kerning: 0pt? mso-hansi-font-family:?Courier mso-bidi-font-family:?Courier>秒</span><span lang="EN-US" style="color: #333333; font-family: "  Courier New?; mso-font-kerning: 0pt?><br />
timeout.tv_usec =0;<br />
ret = select(0, 0,&amp;r, 0,&amp;timeout);<br />
if ( ret&lt;= 0 )<br />
{<br />
::closesocket(cClient);<br />
return 0;<br />
}<br />
//</span><span style="color: #333333; font-family: 宋体; mso-ascii-font-family: "  Courier New?; mso-font-kerning: 0pt? mso-hansi-font-family:?Courier mso-bidi-font-family:?Courier>一般非锁定模式套接比较难控制，可以根据实际情况考虑</span><span style="color: #333333; font-family: "  Courier New?; mso-font-kerning: 0pt?>&nbsp;</span><span style="color: #333333; font-family: 宋体; mso-ascii-font-family: "  Courier New?; mso-font-kerning: 0pt? mso-hansi-font-family:?Courier mso-bidi-font-family:?Courier>再设回阻塞模式</span><span lang="EN-US" style="color: #333333; font-family: "  Courier New?; mso-font-kerning: 0pt?><br />
unsigned long ul1= 0 ;<br />
ret = ioctlsocket(cClient, FIONBIO, (unsigned long*)&amp;ul1);<br />
if(ret==SOCKET_ERROR){<br />
::closesocket (cClient);<br />
return 0;<br />
}</span><span lang="EN-US" style="color: #333333; font-family: Arial; mso-font-kerning: 0pt">
<p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt"><span lang="EN-US">
<p>&nbsp;</p>
</span></span>
<img src ="http://www.blogjava.net/wangxinsh55/aggbug/200054.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wangxinsh55/" target="_blank">SIMONE</a> 2008-05-12 17:09 <a href="http://www.blogjava.net/wangxinsh55/archive/2008/05/12/200054.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何获取BMP的数据 </title><link>http://www.blogjava.net/wangxinsh55/archive/2007/09/29/149643.html</link><dc:creator>SIMONE</dc:creator><author>SIMONE</author><pubDate>Sat, 29 Sep 2007 09:42:00 GMT</pubDate><guid>http://www.blogjava.net/wangxinsh55/archive/2007/09/29/149643.html</guid><wfw:comment>http://www.blogjava.net/wangxinsh55/comments/149643.html</wfw:comment><comments>http://www.blogjava.net/wangxinsh55/archive/2007/09/29/149643.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wangxinsh55/comments/commentRss/149643.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wangxinsh55/services/trackbacks/149643.html</trackback:ping><description><![CDATA[<div class="postbody">
<p><font color="#808080">下面是一段屏幕拷贝的代码，共两个功能函数，其中一个函数可以把 HBITMAP 保存为 BMP 文件</font> </p>
<p><font color="#808080">// scr2bmp.cpp : Defines the entry point for the application.<br />
//</font> </p>
<p><font color="#808080">#include "stdafx.h"<br />
#include "resource.h"</font> </p>
<p><br />
<font color="#808080">LRESULT CALLBACKAbout(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);<br />
HBITMAP WINAPIcopyScreenToBitmap(LPRECT lpRect);<br />
BOOL WINAPIsaveBitmapToFile(HBITMAP hBitmap,LPSTR lpFileName);</font> </p>
<p><br />
<font color="#808080">int APIENTRY WinMain(HINSTANCE hInstance,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HINSTANCE hPrevInstance,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LPSTR lpCmdLine,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int nCmdShow)<br />
{<br />
DialogBox(hInstance,(LPCTSTR)IDD_ABOUTBOX,NULL,(DLGPROC)About);</font> </p>
<p><font color="#808080">return 0;<br />
}</font> </p>
<p><font color="#808080">LRESULT CALLBACK About(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)<br />
{<br />
RECTrc;</font> </p>
<p><font color="#808080">switch(uMsg)<br />
{<br />
case WM_INITDIALOG:<br />
return TRUE;</font> </p>
<p><font color="#808080">case WM_COMMAND:<br />
switch(LOWORD(wParam))<br />
{<br />
case IDOK:<br />
GetWindowRect(hwnd,&amp;rc);<br />
saveBitmapToFile(copyScreenToBitmap(&amp;rc),"D:\\1.bmp");<br />
EndDialog(hwnd,LOWORD(wParam));<br />
break;</font> </p>
<p><font color="#808080">case IDCANCEL:<br />
EndDialog(hwnd,LOWORD(wParam));<br />
break;<br />
}<br />
return TRUE;<br />
}</font> </p>
<p><font color="#808080">return FALSE;<br />
}</font> </p>
<p><font color="#808080">HBITMAP WINAPI copyScreenToBitmap(LPRECT lpRect)// lpRect 代表选定区域<br />
{<br />
HDChScrDC, hMemDC;// 屏幕和内存设备描述表<br />
HBITMAPhBitmap,hOldBitmap;// 位图句柄<br />
intnX, nY, nX2, nY2;// 选定区域坐标<br />
intnWidth, nHeight;// 位图宽度和高度<br />
intxScrn, yScrn;// 屏幕分辨率</font> </p>
<p><font color="#808080">// 确保选定区域不为空矩形<br />
if(IsRectEmpty(lpRect))return NULL;</font> </p>
<p><font color="#808080">// 为屏幕创建设备描述表<br />
hScrDC = CreateDC("DISPLAY",NULL,NULL,NULL);</font> </p>
<p><font color="#808080">// 为屏幕设备描述表创建兼容的内存设备描述表<br />
hMemDC = CreateCompatibleDC(hScrDC);</font> </p>
<p><font color="#808080">// 获得选定区域坐标<br />
nX = lpRect-&gt;left;<br />
nY = lpRect-&gt;top;<br />
nX2 = lpRect-&gt;right;<br />
nY2 = lpRect-&gt;bottom;</font> </p>
<p><font color="#808080">// 获得屏幕分辨率<br />
xScrn = GetDeviceCaps(hScrDC,HORZRES);<br />
yScrn = GetDeviceCaps(hScrDC,VERTRES);</font> </p>
<p><font color="#808080">// 确保选定区域是可见的<br />
if(nX &lt; 0)nX = 0;<br />
if(nY &lt; 0)nY = 0;<br />
if(nX2 &gt; xScrn)nX2 = xScrn;<br />
if(nY2 &gt; yScrn)nY2 = yScrn;</font> </p>
<p><font color="#808080">nWidth = nX2 - nX;<br />
nHeight = nY2 - nY;</font> </p>
<p><font color="#808080">// 创建一个与屏幕设备描述表兼容的位图<br />
hBitmap = CreateCompatibleBitmap(hScrDC,nWidth,nHeight);</font> </p>
<p><font color="#808080">// 把新位图选到内存设备描述表中<br />
hOldBitmap = (HBITMAP)SelectObject(hMemDC,hBitmap);</font> </p>
<p><font color="#808080">// 把屏幕设备描述表拷贝到内存设备描述表中<br />
BitBlt(hMemDC,0,0,nWidth,nHeight,hScrDC,nX,nY,SRCCOPY);</font> </p>
<p><font color="#808080">// 得到屏幕位图的句柄<br />
hBitmap = (HBITMAP)SelectObject(hMemDC,hOldBitmap);</font> </p>
<p><font color="#808080">// 清除<br />
DeleteDC(hScrDC);<br />
DeleteDC(hMemDC);</font> </p>
<p><font color="#808080">// 返回位图句柄<br />
return hBitmap;<br />
}&nbsp;&nbsp; </font></p>
<p><font color="#808080">BOOL WINAPI saveBitmapToFile(HBITMAP hBitmap,// hBitmap 为刚才的屏幕位图句柄<br />
&nbsp;LPSTR lpFileName)// lpFileName 为位图文件名<br />
{<br />
HDChDC;// 设备描述表<br />
intiBits;// 当前显示分辨率下每个像素所占字节数<br />
WORDwBitCount;// 位图中每个像素所占字节数<br />
DWORDdwPaletteSize = 0,// 定义调色板大小<br />
dwBmBitsSize,// 位图中像素字节大小<br />
dwDIBSize,// 位图文件大小<br />
dwWritten;// 写入文件字节数<br />
BITMAPBitmap;// 位图属性结构<br />
BITMAPFILEHEADERbmfHdr;// 位图文件头结构<br />
BITMAPINFOHEADERbi;// 位图信息头结构<br />
LPBITMAPINFOHEADERlpbi;// 指向位图信息头结构<br />
HANDLEfh,// 定义文件<br />
hDib,// 分配内存句柄<br />
hPal;// 调色板句柄<br />
HPALETTEhOldPal = NULL;</font> </p>
<p><font color="#808080">// 计算位图文件每个像素所占字节数<br />
hDC = CreateDC("DISPLAY",NULL,NULL,NULL);<br />
iBits = GetDeviceCaps(hDC,BITSPIXEL) * GetDeviceCaps(hDC,PLANES);<br />
DeleteDC(hDC);</font> </p>
<p><font color="#808080">if(iBits &lt;= 1)wBitCount = 1;<br />
else if(iBits &lt;= 4)wBitCount = 4;<br />
else if(iBits &lt;= 8)wBitCount = 8;<br />
else if(iBits &lt;= 24)wBitCount = 24;<br />
else wBitCount = 24;</font> </p>
<p><font color="#808080">// 计算调色板大小<br />
if(wBitCount &lt;= 8)dwPaletteSize = (1 &lt;&lt; wBitCount) * sizeof(RGBQUAD);</font> </p>
<p><font color="#808080">// 设置位图信息头结构<br />
GetObject(hBitmap,sizeof(BITMAP),(LPSTR)&amp;Bitmap);</font> </p>
<p><font color="#808080">bi.biSize = sizeof(BITMAPINFOHEADER);<br />
bi.biWidth = Bitmap.bmWidth;<br />
bi.biHeight = Bitmap.bmHeight;<br />
bi.biPlanes = 1;<br />
bi.biBitCount = wBitCount;<br />
bi.biCompression = BI_RGB;<br />
bi.biSizeImage = 0;<br />
bi.biXPelsPerMeter = 0;<br />
bi.biYPelsPerMeter = 0;<br />
bi.biClrUsed = 0;<br />
bi.biClrImportant = 0;</font> </p>
<p><font color="#808080">dwBmBitsSize = ((Bitmap.bmWidth * wBitCount + 31) / 32) * 4 * Bitmap.bmHeight;</font> </p>
<p><font color="#808080">// 为位图内容分配内存<br />
hDib = GlobalAlloc(GHND,dwBmBitsSize + dwPaletteSize + sizeof(BITMAPINFOHEADER));<br />
lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDib);<br />
*lpbi = bi;</font> </p>
<p><font color="#808080">// 处理调色板<br />
hPal = GetStockObject(DEFAULT_PALETTE);<br />
if(hPal)<br />
{<br />
hDC = GetDC(NULL);<br />
hOldPal = SelectPalette(hDC,(HPALETTE)hPal,FALSE);<br />
RealizePalette(hDC);<br />
}</font> </p>
<p><font color="#808080">// 获取该调色板下新的像素值<br />
GetDIBits(hDC,hBitmap,0,(UINT)Bitmap.bmHeight,(LPSTR)lpbi + sizeof(BITMAPINFOHEADER) + dwPaletteSize,(BITMAPINFO*)lpbi,DIB_RGB_COLORS);</font> </p>
<p><font color="#808080">// 恢复调色板<br />
if(hOldPal)<br />
{<br />
SelectPalette(hDC,hOldPal,TRUE);<br />
RealizePalette(hDC);<br />
ReleaseDC(NULL,hDC);<br />
}</font> </p>
<p><font color="#808080">// 创建位图文件<br />
fh = CreateFile(lpFileName,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,NULL);<br />
if(fh == INVALID_HANDLE_VALUE)return FALSE;</font> </p>
<p><font color="#808080">// 设置位图文件头<br />
bmfHdr.bfType = 0x4D42;// "BM"<br />
dwDIBSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + dwPaletteSize + dwBmBitsSize;<br />
bmfHdr.bfSize = dwDIBSize;<br />
bmfHdr.bfReserved1 = 0;<br />
bmfHdr.bfReserved2 = 0;<br />
bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER) + dwPaletteSize;</font> </p>
<p><font color="#808080">// 写入位图文件头<br />
WriteFile(fh,(LPSTR)&amp;bmfHdr,sizeof(BITMAPFILEHEADER),&amp;dwWritten,NULL);</font> </p>
<p><font color="#808080">// 写入位图文件其余内容<br />
WriteFile(fh,(LPSTR)lpbi,dwDIBSize,&amp;dwWritten,NULL);</font> </p>
<p><font color="#808080">// 清除<br />
GlobalUnlock(hDib);<br />
GlobalFree(hDib);<br />
CloseHandle(fh);</font> </p>
<p><font color="#808080">return TRUE;<br />
}<br />
/////////////////////////////////////////////////////////////////////////////////////////////<br />
CPaintDC dc(this);<br />
&nbsp;CDC cdcMem;<br />
&nbsp;CBitmap m_bitmap;<br />
&nbsp;cdcMem.CreateCompatibleDC(&amp;dc); <br />
&nbsp;m_bitmap.CreateCompatibleBitmap(&amp;cdcMem, 100, 100);<br />
&nbsp;cdcMem.SelectObject(&amp;m_bitmap);<br />
&nbsp;cdcMem.LineXY(100,100);</font> </p>
<p><font color="#808080">&nbsp;在上面代码中，象访问数组一样访问cdcMem的内存块，并取得象素的数据，该如何操作？<br />
//////////////////////////////////////////////////////<br />
// 获取位图信息大小<br />
BITMAPINFO&nbsp;bmpInfo;<br />
ZeroMemory(&amp;bmpInfo, sizeof(BITMAPINFO));<br />
bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);<br />
int nRet = GetDIBits(dc.m_hDC, m_bitmap, 0, 0, NULL, &amp;bmpInfo, DIB_RGB_COLORS);<br />
if (nRet == 0)<br />
{<br />
&nbsp;return;<br />
}<br />
if (bmpInfo.bmiHeader.biSizeImage &lt;= 0)<br />
{<br />
&nbsp;bmpInfo.bmiHeader.biSizeImage = bmpInfo.bmiHeader.biWidth * abs&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(bmpInfo.bmiHeader.biHeight) * (bmpInfo.bmiHeader.biBitCount+7)/8;<br />
}</font> </p>
<p><font color="#808080">// 开辟位图数据占用的空间<br />
LPVOID&nbsp;lpvBits = malloc(bmpInfo.bmiHeader.biSizeImage);<br />
if (lpvBits == NULL)<br />
{<br />
&nbsp;return;<br />
}</font> </p>
<p><font color="#808080">// 获取位图数据信息<br />
bmpInfo.bmiHeader.biCompression = BI_RGB;<br />
nRet = GetDIBits(hDC, hBitmap, 0, bmpInfo.bmiHeader.biHeight, lpvBits, &amp;bmpInfo, DIB_RGB_COLORS);<br />
if (nRet == 0)<br />
{<br />
&nbsp;return;<br />
}<br />
// 对位图信息lpvBits的操作。。。。。<br />
// ......<br />
// 释放开辟的空间<br />
free(lpvBits);<br />
lpvBits = NULL;</font> </p>
</div>
<img src ="http://www.blogjava.net/wangxinsh55/aggbug/149643.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wangxinsh55/" target="_blank">SIMONE</a> 2007-09-29 17:42 <a href="http://www.blogjava.net/wangxinsh55/archive/2007/09/29/149643.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>CListCtrl 使用技巧</title><link>http://www.blogjava.net/wangxinsh55/archive/2007/09/14/145181.html</link><dc:creator>SIMONE</dc:creator><author>SIMONE</author><pubDate>Fri, 14 Sep 2007 08:52:00 GMT</pubDate><guid>http://www.blogjava.net/wangxinsh55/archive/2007/09/14/145181.html</guid><wfw:comment>http://www.blogjava.net/wangxinsh55/comments/145181.html</wfw:comment><comments>http://www.blogjava.net/wangxinsh55/archive/2007/09/14/145181.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wangxinsh55/comments/commentRss/145181.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wangxinsh55/services/trackbacks/145181.html</trackback:ping><description><![CDATA[【原】CListCtrl 使用技巧<span class="Title"><br />
</span>
<p>作者：lixiaosan<br />
时间：04/06/2006<br />
</p>
<p>以下未经说明，listctrl默认view 风格为report</p>
<p>相关类及处理函数<br />
</p>
<p>MFC：CListCtrl类</p>
<p>SDK：以 &#8220;ListView_&#8221;开头的一些宏。如 ListView_InsertColumn<br />
</p>
<hr style="width: 100%; height: 2px" />
<h3><span style="color: rgb(153,51,102)"><span style="color: rgb(153,51,0)">1. CListCtrl 风格</span></span></h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LVS_ICON: 为每个item显示大图标<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LVS_SMALLICON: 为每个item显示小图标<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LVS_LIST: 显示一列带有小图标的item<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LVS_REPORT: 显示item详细资料</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 直观的理解：windows资源管理器，&#8220;查看&#8221;标签下的&#8220;大图标，小图标，列表，详细资料&#8221;</p>
<p><br />
</p>
<hr style="width: 100%; height: 2px" />
<h3><span style="color: rgb(153,51,0)">2. 设置listctrl 风格及扩展风格</span></h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LONG lStyle;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lStyle = GetWindowLong(m_list.m_hWnd, GWL_STYLE);//获取当前窗口style<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lStyle &amp;= ~LVS_TYPEMASK; //清除显示方式位<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lStyle |= LVS_REPORT; //设置style<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SetWindowLong(m_list.m_hWnd, GWL_STYLE, lStyle);//设置style<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DWORD dwStyle = m_list.GetExtendedStyle();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dwStyle |= LVS_EX_FULLROWSELECT;//选中某行使整行高亮（只适用与report风格的listctrl）<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dwStyle |= LVS_EX_GRIDLINES;//网格线（只适用与report风格的listctrl）<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dwStyle |= LVS_EX_CHECKBOXES;//item前生成checkbox控件<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_list.SetExtendedStyle(dwStyle); //设置扩展风格<br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 注：listview的style请查阅msdn<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wceshellui5/html/wce50lrflistviewstyles.asp">http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wceshellui5/html/wce50lrflistviewstyles.asp</a><br />
</p>
<p>&nbsp;</p>
<hr style="width: 100%; height: 2px" />
<h3><span style="color: rgb(153,51,0)">3. 插入数据</span></h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_list.InsertColumn( 0, "ID", LVCFMT_LEFT, 40 );//插入列<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_list.InsertColumn( 1, "NAME", LVCFMT_LEFT, 50 );<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int nRow = m_list.InsertItem(0, &#8220;11&#8221;);//插入行<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_list.SetItemText(nRow, 1, &#8220;jacky&#8221;);//设置数据<br />
</p>
<p>&nbsp;</p>
<hr style="width: 100%; height: 2px" />
<h3><span style="color: rgb(51,102,255)"><span style="color: rgb(153,51,0)">4. 一直选中item</span></span></h3>
<span style="font-weight: bold">&nbsp;&nbsp;&nbsp; </span>选中style中的Show selection always，或者在上面第2点中设置LVS_SHOWSELALWAYS<br />
<br />
<br />
<hr style="width: 100%; height: 2px" />
<h3><span style="color: rgb(153,51,0)">5. 选中和取消选中一行</span></h3>
<p>&nbsp;&nbsp;&nbsp; int nIndex = 0;<br />
&nbsp;&nbsp;&nbsp; //选中<br />
&nbsp;&nbsp;&nbsp; m_list.SetItemState(nIndex, LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED|LVIS_FOCUSED);<br />
&nbsp;&nbsp;&nbsp; //取消选中<br />
&nbsp;&nbsp;&nbsp; m_list.SetItemState(nIndex, 0, LVIS_SELECTED|LVIS_FOCUSED);<br />
&nbsp;</p>
<hr style="width: 100%; height: 2px" />
<h3><span style="color: rgb(153,51,0)">6. 得到listctrl中所有行的checkbox的状态</span></h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_list.SetExtendedStyle(LVS_EX_CHECKBOXES);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CString str;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(int i=0; i&lt;m_list.GetItemCount(); i++)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if( m_list.GetItemState(i, LVIS_SELECTED) == LVIS_SELECTED || m_list.GetCheck(i))<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; str.Format(_T("第%d行的checkbox为选中状态"), i);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AfxMessageBox(str);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
</p>
<p><br />
</p>
<hr style="width: 100%; height: 2px" />
<h3><span style="color: rgb(153,51,0)">7. 得到listctrl中所有选中行的序号</span></h3>
<h3></h3>
<p><span style="color: rgb(153,51,0)"><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 方法一：<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CString str;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(int i=0; i&lt;m_list.GetItemCount(); i++)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if( m_list.GetItemState(i, LVIS_SELECTED) == LVIS_SELECTED )<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; str.Format(_T("选中了第%d行"), i);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AfxMessageBox(str);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 方法二：<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; POSITION pos = m_list.GetFirstSelectedItemPosition();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (pos == NULL)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TRACE0("No items were selected!\n");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while (pos)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int nItem = m_list.GetNextSelectedItem(pos);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TRACE1("Item %d was selected!\n", nItem);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // you could do your own processing on nItem here<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
</p>
<p><br />
</p>
<hr style="width: 100%; height: 2px" />
<h3><span style="color: rgb(153,51,0)">8. 得到item的信息</span></h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TCHAR szBuf[1024];<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LVITEM lvi;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lvi.iItem = nItemIndex;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lvi.iSubItem = 0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lvi.mask = LVIF_TEXT;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lvi.pszText = szBuf;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lvi.cchTextMax = 1024;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_list.GetItem(&amp;lvi);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 关于得到设置item的状态，还可以参考msdn文章<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Q173242: Use Masks to Set/Get Item States in CListCtrl<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://support.microsoft.com/kb/173242/en-us">http://support.microsoft.com/kb/173242/en-us</a><br />
</p>
<p><br />
</p>
<hr style="width: 100%; height: 2px" />
<h3><span style="color: rgb(51,102,255)"><span style="color: rgb(153,51,0)">9. 得到listctrl的所有列的header字符串内容</span></span></h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LVCOLUMN lvcol;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char&nbsp; str[256];<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp; nColNum;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CString&nbsp; strColumnName[4];//假如有4列</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nColNum = 0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lvcol.mask = LVCF_TEXT;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lvcol.pszText = str;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lvcol.cchTextMax = 256;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while(m_list.GetColumn(nColNum, &amp;lvcol))<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strColumnName[nColNum] = lvcol.pszText;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nColNum++;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
</p>
<p><br />
</p>
<hr style="width: 100%; height: 2px" />
<h3><span style="color: rgb(153,51,0)">10. 使listctrl中一项可见，即滚动滚动条</span></h3>
&nbsp;&nbsp;&nbsp; m_list.EnsureVisible(i, FALSE);<br />
<br />
<hr style="width: 100%; height: 2px" />
<h3><span style="color: rgb(153,51,0)">11. 得到listctrl列数</span></h3>
&nbsp;&nbsp;&nbsp; int nHeadNum = m_list.GetHeaderCtrl()-&gt;GetItemCount();<br />
<br />
<hr style="width: 100%; height: 2px" />
<h3><span style="color: rgb(153,51,0)">12. 删除所有列</span></h3>
<p>&nbsp;&nbsp;&nbsp; &nbsp; 方法一：<br />
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; while ( m_list.DeleteColumn (0))<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; 因为你删除了第一列后，后面的列会依次向上移动。</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp; 方法二：<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int nColumns = 4;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i=nColumns-1; i&gt;=0; i--)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; m_list.DeleteColumn (i);<br />
</p>
<p><br />
</p>
<hr style="width: 100%; height: 2px" />
<h3><span style="color: rgb(153,51,0)">13. 得到单击的listctrl的行列号</span></h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 添加listctrl控件的NM_CLICK消息相应函数<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void CTest6Dlg::OnClickList1(NMHDR* pNMHDR, LRESULT* pResult)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 方法一：<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DWORD dwPos = GetMessagePos();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CPoint point( LOWORD(dwPos), HIWORD(dwPos) );<br />
&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_list.ScreenToClient(&amp;point);<br />
&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LVHITTESTINFO lvinfo;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lvinfo.pt = point;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lvinfo.flags = LVHT_ABOVE;<br />
&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int nItem = m_list.SubItemHitTest(&amp;lvinfo);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(nItem != -1)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CString strtemp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strtemp.Format("单击的是第%d行第%d列", lvinfo.iItem, lvinfo.iSubItem);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AfxMessageBox(strtemp);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br />
&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 方法二:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(pNMListView-&gt;iItem != -1)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CString strtemp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strtemp.Format("单击的是第%d行第%d列",<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; pNMListView-&gt;iItem, pNMListView-&gt;iSubItem);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AfxMessageBox(strtemp);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *pResult = 0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
</p>
<p>&nbsp;</p>
<hr style="width: 100%; height: 2px" />
<h3><span style="color: rgb(153,51,0)">14. 判断是否点击在listctrl的checkbox上</span></h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 添加listctrl控件的NM_CLICK消息相应函数<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void CTest6Dlg::OnClickList1(NMHDR* pNMHDR, LRESULT* pResult)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DWORD dwPos = GetMessagePos();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CPoint point( LOWORD(dwPos), HIWORD(dwPos) );<br />
&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_list.ScreenToClient(&amp;point);<br />
&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LVHITTESTINFO lvinfo;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lvinfo.pt = point;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lvinfo.flags = LVHT_ABOVE;<br />
&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; UINT nFlag;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int nItem = m_list.HitTest(point, &amp;nFlag);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //判断是否点在checkbox上<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(nFlag == LVHT_ONITEMSTATEICON)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AfxMessageBox("点在listctrl的checkbox上");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *pResult = 0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
</p>
<p><br />
</p>
<hr style="width: 100%; height: 2px" />
<h3><span style="color: rgb(153,51,0)">15. 右键点击listctrl的item弹出菜单</span></h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 添加listctrl控件的NM_RCLICK消息相应函数<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void CTest6Dlg::OnRclickList1(NMHDR* pNMHDR, LRESULT* pResult)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(pNMListView-&gt;iItem != -1)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DWORD dwPos = GetMessagePos();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CPoint point( LOWORD(dwPos), HIWORD(dwPos) );<br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CMenu menu;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; VERIFY( menu.LoadMenu( IDR_MENU1 ) );<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CMenu* popup = menu.GetSubMenu(0);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ASSERT( popup != NULL );<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; popup-&gt;TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this );<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *pResult = 0;<br />
&nbsp; }<br />
</p>
<p><br />
</p>
<p>&nbsp;</p>
<hr style="width: 100%; height: 2px" />
<h3><span style="color: rgb(153,51,0)">16. item切换焦点时(包括用键盘和鼠标切换item时)，状态的一些变化顺序</span></h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 添加listctrl控件的LVN_ITEMCHANGED消息相应函数<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void CTest6Dlg::OnItemchangedList1(NMHDR* pNMHDR, LRESULT* pResult)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // TODO: Add your control notification handler code here<br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CString sTemp;<br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if((pNMListView-&gt;uOldState &amp; LVIS_FOCUSED) == LVIS_FOCUSED &amp;&amp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (pNMListView-&gt;uNewState &amp; LVIS_FOCUSED) == 0)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sTemp.Format("%d losted focus",pNMListView-&gt;iItem);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if((pNMListView-&gt;uOldState &amp; LVIS_FOCUSED) == 0 &amp;&amp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (pNMListView-&gt;uNewState &amp; LVIS_FOCUSED) == LVIS_FOCUSED)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sTemp.Format("%d got focus",pNMListView-&gt;iItem);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if((pNMListView-&gt;uOldState &amp; LVIS_SELECTED) == LVIS_SELECTED &amp;&amp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (pNMListView-&gt;uNewState &amp; LVIS_SELECTED) == 0)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sTemp.Format("%d losted selected",pNMListView-&gt;iItem);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if((pNMListView-&gt;uOldState &amp; LVIS_SELECTED) == 0 &amp;&amp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (pNMListView-&gt;uNewState &amp; LVIS_SELECTED) == LVIS_SELECTED)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sTemp.Format("%d got selected",pNMListView-&gt;iItem);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *pResult = 0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<br />
<br />
<hr style="width: 100%; height: 2px" />
<h3><span style="color: rgb(153,51,0)">17. 得到另一个进程里的listctrl控件的item内容</span></h3>
<h3></h3>
<a href="http://www.codeproject.com/threads/int64_memsteal.asp">http://www.codeproject.com/threads/int64_memsteal.asp</a><br />
<br />
<br />
<hr style="width: 100%; height: 2px" />
<h3><span style="color: rgb(51,102,255)"><span style="color: rgb(153,51,0)">18. 选中listview中的item</span></span> </h3>
Q131284: How To Select a Listview Item Programmatically<br />
<a href="http://support.microsoft.com/kb/131284/en-us">http://support.microsoft.com/kb/131284/en-us</a><br />
<br />
<br />
<hr style="width: 100%; height: 2px" />
<h3><span style="color: rgb(153,51,0)">19. 如何在CListView中使用CListCtrl的派生类</span></h3>
<p><a href="http://www.codeguru.com/cpp/controls/listview/introduction/article.php/c919/">http://www.codeguru.com/cpp/controls/listview/introduction/article.php/c919/</a><br />
</p>
<p><br />
</p>
<hr style="width: 100%; height: 2px" />
<h3><span style="color: rgb(153,51,0)">20. listctrl的subitem添加图标</span></h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_list.SetExtendedStyle(LVS_EX_SUBITEMIMAGES);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_list.SetItem(..); //具体参数请参考msdn<br />
</p>
<p>&nbsp;</p>
<hr style="width: 100%; height: 2px" />
<h3><span style="color: rgb(153,51,0)">21. 在CListCtrl显示文件，并根据文件类型来显示图标</span></h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 网上找到的代码，share<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BOOL CTest6Dlg::OnInitDialog()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CDialog::OnInitDialog();<br />
&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HIMAGELIST himlSmall;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HIMAGELIST himlLarge;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SHFILEINFO sfi;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char&nbsp; cSysDir[MAX_PATH];<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CString&nbsp; strBuf;<br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; memset(cSysDir, 0, MAX_PATH);<br />
&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GetWindowsDirectory(cSysDir, MAX_PATH);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strBuf = cSysDir;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sprintf(cSysDir, "%s", strBuf.Left(strBuf.Find("<a href="file://%22)+1/">\\")+1</a>));<br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; himlSmall = (HIMAGELIST)SHGetFileInfo ((LPCSTR)cSysDir,&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0,&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;sfi, <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sizeof(SHFILEINFO),&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SHGFI_SYSICONINDEX | SHGFI_SMALLICON );<br />
&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; himlLarge = (HIMAGELIST)SHGetFileInfo((LPCSTR)cSysDir,&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0,&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;sfi,&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sizeof(SHFILEINFO),&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SHGFI_SYSICONINDEX | SHGFI_LARGEICON);<br />
&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (himlSmall &amp;&amp; himlLarge)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ::SendMessage(m_list.m_hWnd, LVM_SETIMAGELIST,<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; (WPARAM)LVSIL_SMALL, (LPARAM)himlSmall);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ::SendMessage(m_list.m_hWnd, LVM_SETIMAGELIST,<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; (WPARAM)LVSIL_NORMAL, (LPARAM)himlLarge);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return TRUE;&nbsp; // return TRUE&nbsp; unless you set the focus to a control<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void CTest6Dlg::AddFiles(LPCTSTR lpszFileName, BOOL bAddToDocument)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int nIcon = GetIconIndex(lpszFileName, FALSE, FALSE);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CString strSize;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CFileFind filefind;<br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp; get file size<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (filefind.FindFile(lpszFileName))<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; filefind.FindNextFile();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strSize.Format("%d", filefind.GetLength());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strSize = "0";<br />
&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // split path and filename<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CString strFileName = lpszFileName;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CString strPath;<br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int nPos = strFileName.ReverseFind('\\');<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (nPos != -1)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strPath = strFileName.Left(nPos);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strFileName = strFileName.Mid(nPos + 1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // insert to list<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int nItem = m_list.GetItemCount();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_list.InsertItem(nItem, strFileName, nIcon);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_list.SetItemText(nItem, 1, strSize);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_list.SetItemText(nItem, 2, strFileName.Right(3));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_list.SetItemText(nItem, 3, strPath);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int CTest6Dlg::GetIconIndex(LPCTSTR lpszPath, BOOL bIsDir, BOOL bSelected)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SHFILEINFO sfi;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; memset(&amp;sfi, 0, sizeof(sfi));<br />
&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (bIsDir)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SHGetFileInfo(lpszPath,&nbsp; <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; FILE_ATTRIBUTE_DIRECTORY,&nbsp; <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; &amp;sfi,&nbsp; <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; sizeof(sfi),&nbsp; <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; SHGFI_SMALLICON | SHGFI_SYSICONINDEX |<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; SHGFI_USEFILEATTRIBUTES |(bSelected ? SHGFI_OPENICON : 0));&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp; sfi.iIcon;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SHGetFileInfo (lpszPath,&nbsp; <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; FILE_ATTRIBUTE_NORMAL,&nbsp; <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; &amp;sfi,&nbsp; <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; sizeof(sfi),&nbsp; <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; SHGFI_SMALLICON | SHGFI_SYSICONINDEX |&nbsp; <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; SHGFI_USEFILEATTRIBUTES | (bSelected ? SHGFI_OPENICON : 0));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp;&nbsp; sfi.iIcon;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp; -1;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
</p>
<p><br />
</p>
<hr style="width: 100%; height: 2px" />
<h3><span style="color: rgb(153,51,0)">22. listctrl内容进行大数据量更新时，避免闪烁</span></h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_list.SetRedraw(FALSE);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //更新内容<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_list.SetRedraw(TRUE);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_list.Invalidate();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_list.UpdateWindow();<br />
&nbsp; <br />
或者参考 <br />
</p>
<p><a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_mfc_cwnd.3a3a.setredraw.asp">http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_mfc_cwnd.3a3a.setredraw.asp</a><br />
</p>
<p><br />
</p>
<hr style="width: 100%; height: 2px" />
<h3><span style="color: rgb(153,51,0)">23. listctrl排序 </span></h3>
<p>Q250614：How To Sort Items in a CListCtrl in Report View<br />
<a href="http://support.microsoft.com/kb/250614/en-us">http://support.microsoft.com/kb/250614/en-us</a><br />
</p>
<p><br />
</p>
<hr style="width: 100%; height: 2px" />
<h3><span style="color: rgb(153,51,0)">24. 在listctrl中选中某个item时动态改变其icon或bitmap</span></h3>
Q141834: How to change the icon or the bitmap of a CListCtrl item in Visual C++<br />
<a href="http://support.microsoft.com/kb/141834/en-us">http://support.microsoft.com/kb/141834/en-us</a><br />
<br />
<br />
<hr style="width: 100%; height: 2px" />
<h3><span style="color: rgb(153,51,0)">25. 在添加item后，再InsertColumn()后导致整列数据移动的问题</span></h3>
<p>Q151897: CListCtrl::InsertColumn() Causes Column Data to Shift <br />
<a href="http://support.microsoft.com/kb/151897/en-us">http://support.microsoft.com/kb/151897/en-us</a><br />
</p>
<p><br />
</p>
<hr style="width: 100%; height: 2px" />
<h3><span style="color: rgb(153,51,0)">26. 关于listctrl第一列始终居左的问题</span></h3>
<p>解决办法：把第一列当一个虚列，从第二列开始插入列及数据，最后删除第一列。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
具体解释参阅&nbsp;&nbsp; <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/listview/structures/lvcolumn.asp">http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/listview/structures/lvcolumn.asp</a><br />
</p>
<p>&nbsp;</p>
<hr style="width: 100%; height: 2px" />
<h3><span style="color: rgb(153,51,0)">27. 锁定column header的拖动</span></h3>
<p><a href="http://msdn.microsoft.com/msdnmag/issues/03/06/CQA/">http://msdn.microsoft.com/msdnmag/issues/03/06/CQA/</a><br />
</p>
<p><br />
</p>
<hr style="width: 100%; height: 2px" />
<h3 style="color: rgb(153,51,0)">28. 如何隐藏clistctrl的列</h3>
<p>&nbsp;&nbsp;&nbsp; 把需隐藏的列的宽度设为0,然后检测当该列为隐藏列时，用上面第27点的锁定column 的拖动来实现<br />
<br />
</p>
<hr style="width: 100%; height: 2px" />
<h3><span style="color: rgb(153,51,0)">29. listctrl进行大数据量操作时，使用virtual list&nbsp;</span>&nbsp;&nbsp; </h3>
<p><a href="http://www.microsoft.com/msj/archive/S2061.aspx">http://www.microsoft.com/msj/archive/S2061.aspx</a><br />
<a href="http://www.codeguru.com/cpp/controls/listview/advanced/article.php/c4151/">http://www.codeguru.com/cpp/controls/listview/advanced/article.php/c4151/</a><br />
<a href="http://www.codeproject.com/listctrl/virtuallist.asp">http://www.codeproject.com/listctrl/virtuallist.asp</a><br />
</p>
<p><br />
</p>
<hr style="width: 100%; height: 2px" />
<h3><span style="color: rgb(153,51,0)">30. 关于item只能显示259个字符的问题</span></h3>
<p>解决办法：需要在item上放一个edit。<br />
</p>
<p><br />
</p>
<hr style="width: 100%; height: 2px" />
<h3><span style="color: rgb(153,51,0)">31. 响应在listctrl的column header上的鼠标右键单击</span></h3>
<p>Q125694: How To Find Out Which Listview Column Was Right-Clicked<br />
<a href="http://support.microsoft.com/kb/125694/en-us">http://support.microsoft.com/kb/125694/en-us</a><br />
</p>
<p><br />
</p>
<hr style="width: 100%; height: 2px" />
<h3><span style="color: rgb(153,51,0)">32. 类似于windows资源管理器的listview</span></h3>
<p>Q234310: How to implement a ListView control that is similar to Windows Explorer by using DirLV.exe<br />
<a href="http://support.microsoft.com/kb/234310/en-us">http://support.microsoft.com/kb/234310/en-us</a><br />
</p>
<p>&nbsp;</p>
<hr style="width: 100%; height: 2px" />
<h3 style="color: rgb(153,51,0)">33. 在ListCtrl中OnTimer只响应两次的问题</h3>
<p>Q200054：<br />
PRB: OnTimer() Is Not Called Repeatedly for a List Control<br />
<a href="http://support.microsoft.com/kb/200054/en-us">http://support.microsoft.com/kb/200054/en-us</a><br />
</p>
<hr style="width: 100%; height: 2px" />
<h3><span style="color: rgb(153,51,0)">34. 以下为一些为实现各种自定义功能的listctrl派生类</span></h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (1)&nbsp;&nbsp;&nbsp; <span style="color: rgb(153,51,0)">拖放</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://www.codeproject.com/listctrl/dragtest.asp">http://www.codeproject.com/listctrl/dragtest.asp</a></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: rgb(153,51,0)">在CListCtrl和CTreeCtrl间拖放</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://support.microsoft.com/kb/148738/en-us">http://support.microsoft.com/kb/148738/en-us</a><br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (2)&nbsp;&nbsp;&nbsp; <span style="color: rgb(153,51,0)">多功能listctrl</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 支持subitem可编辑，图标，radiobutton，checkbox，字符串改变颜色的类<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://www.codeproject.com/listctrl/quicklist.asp">http://www.codeproject.com/listctrl/quicklist.asp</a><br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: rgb(153,51,0)">支持排序，subitem可编辑，subitem图标，subitem改变颜色的类</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://www.codeproject.com/listctrl/ReportControl.asp">http://www.codeproject.com/listctrl/ReportControl.asp</a></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (3)&nbsp;&nbsp;&nbsp; <span style="color: rgb(153,51,0)">subitem中显示超链接</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://www.codeproject.com/listctrl/CListCtrlLink.asp">http://www.codeproject.com/listctrl/CListCtrlLink.asp</a></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (4)&nbsp;&nbsp;&nbsp; <span style="color: rgb(153,51,0)">subitem的tooltip提示</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://www.codeproject.com/listctrl/ctooltiplistctrl.asp">http://www.codeproject.com/listctrl/ctooltiplistctrl.asp</a></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (5)&nbsp;&nbsp;&nbsp;<span style="color: rgb(153,51,0)"> subitem中显示进度条</span>&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://www.codeproject.com/listctrl/ProgressListControl.asp">http://www.codeproject.com/listctrl/ProgressListControl.asp</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://www.codeproject.com/listctrl/napster.asp">http://www.codeproject.com/listctrl/napster.asp</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://www.codeguru.com/Cpp/controls/listview/article.php/c4187/">http://www.codeguru.com/Cpp/controls/listview/article.php/c4187/</a></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (6)&nbsp;&nbsp;&nbsp; <span style="color: rgb(153,51,0)">动态改变subitem的颜色和背景色</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://www.codeproject.com/listctrl/highlightlistctrl.asp">http://www.codeproject.com/listctrl/highlightlistctrl.asp</a><br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <a href="http://www.codeguru.com/Cpp/controls/listbox/colorlistboxes/article.php/c4757/">http://www.codeguru.com/Cpp/controls/listbox/colorlistboxes/article.php/c4757/</a><br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (7)&nbsp;&nbsp;&nbsp; <span style="color: rgb(153,51,0)">类vb属性对话框</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://www.codeproject.com/listctrl/propertylistctrl.asp">http://www.codeproject.com/listctrl/propertylistctrl.asp</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://www.codeguru.com/Cpp/controls/listview/propertylists/article.php/c995/">http://www.codeguru.com/Cpp/controls/listview/propertylists/article.php/c995/</a> <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://www.codeguru.com/Cpp/controls/listview/propertylists/article.php/c1041/">http://www.codeguru.com/Cpp/controls/listview/propertylists/article.php/c1041/</a> <br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (8)&nbsp;&nbsp;&nbsp; <span style="color: rgb(153,51,0)">选中subitem(只高亮选中的item)</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://www.codeproject.com/listctrl/SubItemSel.asp">http://www.codeproject.com/listctrl/SubItemSel.asp</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://www.codeproject.com/listctrl/ListSubItSel.asp">http://www.codeproject.com/listctrl/ListSubItSel.asp</a><br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (9)&nbsp;&nbsp;&nbsp; <span style="color: rgb(153,51,0)">改变行高</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://www.codeproject.com/listctrl/changerowheight.asp">http://www.codeproject.com/listctrl/changerowheight.asp</a><br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (10)&nbsp;&nbsp; <span style="color: rgb(153,51,0)">改变行颜色</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://www.codeproject.com/listctrl/coloredlistctrl.asp">http://www.codeproject.com/listctrl/coloredlistctrl.asp</a><br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (11)&nbsp;&nbsp; <span style="color: rgb(153,51,0)">可编辑subitem的listctrl</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://www.codeproject.com/listctrl/nirs2000.asp">http://www.codeproject.com/listctrl/nirs2000.asp</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://www.codeproject.com/listctrl/editing_subitems_in_listcontrol.asp">http://www.codeproject.com/listctrl/editing_subitems_in_listcontrol.asp</a><br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (12)&nbsp;&nbsp; <span style="color: rgb(153,51,0)">subitem可编辑，插入combobox，改变行颜色，subitem的tooltip提示</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://www.codeproject.com/listctrl/reusablelistcontrol.asp">http://www.codeproject.com/listctrl/reusablelistcontrol.asp</a><br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (13)&nbsp;&nbsp; <span style="color: rgb(153,51,0)">header 中允许多行字符串</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://www.codeproject.com/listctrl/headerctrlex.asp">http://www.codeproject.com/listctrl/headerctrlex.asp</a><br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (14)&nbsp;&nbsp; <span style="color: rgb(153,51,0)">插入combobox</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://www.codeguru.com/Cpp/controls/listview/editingitemsandsubitem/article.php/c979/">http://www.codeguru.com/Cpp/controls/listview/editingitemsandsubitem/article.php/c979/</a><br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (15)&nbsp;&nbsp; <span style="color: rgb(153,51,0)">添加背景图片</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://www.codeguru.com/Cpp/controls/listview/backgroundcolorandimage/article.php/c4173/">http://www.codeguru.com/Cpp/controls/listview/backgroundcolorandimage/article.php/c4173/</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://www.codeguru.com/Cpp/controls/listview/backgroundcolorandimage/article.php/c983/">http://www.codeguru.com/Cpp/controls/listview/backgroundcolorandimage/article.php/c983/</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://www.vchelp.net/vchelp/archive.asp?type_id=9&amp;class_id=1&amp;cata_id=1&amp;article_id=1088&amp;search_term">http://www.vchelp.net/vchelp/archive.asp?type_id=9&amp;class_id=1&amp;cata_id=1&amp;article_id=1088&amp;search_term</a>=<br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (16)&nbsp; <span style="color: rgb(153,51,0)">自适应宽度的listctrl</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://www.codeproject.com/useritems/AutosizeListCtrl.asp">http://www.codeproject.com/useritems/AutosizeListCtrl.asp</a><br />
</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; (17)&nbsp; <span style="color: rgb(153,51,0)">改变ListCtrl高亮时的颜色(默认为蓝色)</span><br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; 处理<code> NM_CUSTOMDRAW <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://www.codeproject.com/listctrl/lvcustomdraw.asp">http://www.codeproject.com/listctrl/lvcustomdraw.asp</a></code></p>
<p><code>&nbsp;&nbsp;&nbsp;&nbsp; (18)&nbsp; 改变header颜色<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; <a href="http://www.pocketpcdn.com/articles/hdr_color.html">http://www.pocketpcdn.com/articles/hdr_color.html</a><br />
</code></p>
<p>&nbsp; <br />
&nbsp;<a href="http://www.bc-cn.net/Article/kfyy/vc/jszl/200607/4212.html">http://www.bc-cn.net/Article/kfyy/vc/jszl/200607/4212.html</a><br />
</p>
<br />
&nbsp;<br />
<br />
<p id="TBPingURL">Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1771869</p>
<img src ="http://www.blogjava.net/wangxinsh55/aggbug/145181.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wangxinsh55/" target="_blank">SIMONE</a> 2007-09-14 16:52 <a href="http://www.blogjava.net/wangxinsh55/archive/2007/09/14/145181.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>VC/MFC.CString操作指南 </title><link>http://www.blogjava.net/wangxinsh55/archive/2007/09/08/143671.html</link><dc:creator>SIMONE</dc:creator><author>SIMONE</author><pubDate>Sat, 08 Sep 2007 12:42:00 GMT</pubDate><guid>http://www.blogjava.net/wangxinsh55/archive/2007/09/08/143671.html</guid><wfw:comment>http://www.blogjava.net/wangxinsh55/comments/143671.html</wfw:comment><comments>http://www.blogjava.net/wangxinsh55/archive/2007/09/08/143671.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wangxinsh55/comments/commentRss/143671.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wangxinsh55/services/trackbacks/143671.html</trackback:ping><description><![CDATA[<font face="Comic Sans MS" size="4">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CString 是一种很有用的数据类型。它们很大程度上简化了MFC中的许多操作，使得MFC在做字符串操作的时候方便了很多。不管怎样，使用CString有很多特殊的技巧，特别是对于纯C背景下走出来的程序员来说有点难以学习。这篇文章就来讨论这些技巧。<br />
　　使用CString可以让你对字符串的操作更加直截了当。这篇文章不是CString的完全手册，但囊括了大部分常见基本问题。<br />
<br />
这篇文章包括以下内容： <br />
<br />
CString 对象的连接 <br />
<br />
格式化字符串（包括 int 型转化为 CString ） <br />
CString 型转化成 int 型 <br />
CString 型和 char* 类型的相互转化<br />
<br />
char* 转化成 CString <br />
CString 转化成 char* 之一：使用LPCTSTR强制转化 <br />
CString 转化成 char* 之二：使用CString对象的GetBuffer方法 <br />
CString 转化成 char* 之三: 和控件的接口<br />
CString 型转化成 BSTR 型； <br />
BSTR 型转化成 CString 型； <br />
VARIANT 型转化成 CString 型； <br />
载入字符串表资源； <br />
CString 和临时对象； <br />
CString 的效率； <br />
总结 <br />
下面我分别讨论。<br />
<br />
1、<strong>CString 对象的连接</strong><br />
<br />
　　能体现出 CString 类型方便性特点的一个方面就字符串的连接，使用 CString 类型，你能很方便地连接两个字符串，正如下面的例子：<br />
<br />
CString gray("Gray");<br />
CString cat("Cat");<br />
CString graycat = gray + cat;<br />
要比用下面的方法好得多：<br />
<br />
char gray[] = "Gray";<br />
char cat[] = "Cat";<br />
char * graycat = malloc(strlen(gray) + strlen(cat) + 1);<br />
strcpy(graycat, gray);<br />
strcat(graycat, cat);<br />
<br />
2、<strong>格式化字符串</strong><br />
<br />
　　与其用 sprintf() 函数或 wsprintf() 函数来格式化一个字符串，还不如用 CString 对象的Format()方法：<br />
<br />
CString s;<br />
s.Format(_T("The total is %d"), total);<br />
　　用这种方法的好处是你不用担心用来存放格式化后数据的缓冲区是否足够大，这些工作由CString类替你完成。<br />
　　格式化是一种把其它不是字符串类型的数据转化为CString类型的最常用技巧，比如，把一个整数转化成CString类型，可用如下方法：<br />
<br />
CString s;<br />
s.Format(_T("%d"), total);<br />
　　我总是对我的字符串使用_T()宏，这是为了让我的代码至少有Unicode的意识，当然，关于Unicode的话题不在这篇文章的讨论范围。_T()宏在8位字符环境下是如下定义的：<br />
<br />
#define _T(x) x // 非Unicode版本（non-Unicode version）<br />
而在Unicode环境下是如下定义的：<br />
<br />
#define _T(x) L##x // Unicode版本（Unicode version）<br />
所以在Unicode环境下，它的效果就相当于：<br />
<br />
s.Format(L"%d", total);<br />
　　如果你认为你的程序可能在Unicode的环境下运行，那么开始在意用 Unicode 编码。比如说，不要用 sizeof() 操作符来获得字符串的长度，因为在Unicode环境下就会有2倍的误差。我们可以用一些方法来隐藏Unicode的一些细节，比如在我需要获得字符长度的时候，我会用一个叫做DIM的宏，这个宏是在我的dim.h文件中定义的，我会在我写的所有程序中都包含这个文件：<br />
<br />
#define DIM(x) ( sizeof((x)) / sizeof((x)[0]) )<br />
　　这个宏不仅可以用来解决Unicode的字符串长度的问题，也可以用在编译时定义的表格上，它可以获得表格的项数，如下：<br />
<br />
class Whatever { ... };<br />
Whatever data[] = {<br />
 { ... },<br />
...<br />
 { ... },<br />
};<br />
for(int i = 0; i &lt; DIM(data); i++) // 扫描表格寻找匹配项。<br />
<br />
　　这里要提醒你的就是一定要注意那些在参数中需要真实字节数的API函数调用，如果你传递字符个数给它，它将不能正常工作。如下：<br />
TCHAR data[20];<br />
lstrcpyn(data, longstring, sizeof(data) - 1); // WRONG!<br />
lstrcpyn(data, longstring, DIM(data) - 1); // RIGHT<br />
WriteFile(f, data, DIM(data), &amp;bytesWritten, NULL); // WRONG!<br />
WriteFile(f, data, sizeof(data), &amp;bytesWritten, NULL); // RIGHT<br />
造成以上原因是因为lstrcpyn需要一个字符个数作为参数，但是WriteFile却需要字节数作为参数。<br />
同样需要注意的是有时候需要写出数据的所有内容。如果你仅仅只想写出数据的真实长度，你可能会认为你应该这样做：<br />
<br />
WriteFile(f, data, lstrlen(data), &amp;bytesWritten, NULL); // WRONG<br />
但是在Unicode环境下，它不会正常工作。正确的做法应该是这样：<br />
<br />
WriteFile(f, data, lstrlen(data) * sizeof(TCHAR), &amp;bytesWritten, NULL); // RIGHT<br />
　　因为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 />
3、<strong>CString 型转化成 int 型</strong><br />
<br />
　　把 CString 类型的数据转化成整数类型最简单的方法就是使用标准的字符串到整数转换例程。<br />
　　虽然通常你怀疑使用_atoi()函数是一个好的选择，它也很少会是一个正确的选择。如果你准备使用 Unicode 字符，你应该用_ttoi()，它在 ANSI 编码系统中被编译成_atoi()，而在 Unicode 编码系统中编译成_wtoi()。你也可以考虑使用_tcstoul()或者_tcstol()，它们都能把字符串转化成任意进制的长整数（如二进制、八进制、十进制或十六进制），不同点在于前者转化后的数据是无符号的（unsigned），而后者相反。看下面的例子：<br />
<br />
CString hex = _T("FAB");<br />
CString decimal = _T("4011");<br />
ASSERT(_tcstoul(hex, 0, 16) == _ttoi(decimal));<br />
<br />
4、<strong>CString 型和 char* 类型的相互转化</strong><br />
<br />
　　这是初学者使用 CString 时最常见的问题。有了 C++ 的帮助，很多问题你不需要深入的去考虑它，直接拿来用就行了，但是如果你不能深入了解它的运行机制，又会有很多问题让你迷惑，特别是有些看起来没有问题的代码，却偏偏不能正常工作。<br />
比如，你会奇怪为什么不能写向下面这样的代码呢：<br />
<br />
CString graycat = "Gray" + "Cat";<br />
或者这样：<br />
<br />
CString graycat("Gray" + "Cat");<br />
　　事实上，编译器将抱怨上面的这些尝试。为什么呢？因为针对CString 和 LPCTSTR数据类型的各种各样的组合，&#8220; +&#8221; 运算符 被定义成一个重载操作符。而不是两个 LPCTSTR 数据类型，它是底层数据类型。你不能对基本数据（如 int、char 或者 char*）类型重载 C++ 的运算符。你可以象下面这样做：<br />
<br />
CString graycat = CString("Gray") + CString("Cat");<br />
或者这样：<br />
<br />
CString graycat = CString("Gray") + "Cat";<br />
研究一番就会发现：&#8220; +&#8221;总是使用在至少有一个 CString 对象和一个 LPCSTR 的场合。<br />
<br />
注意，编写有 Unicode 意识的代码总是一件好事，比如：<br />
<br />
CString graycat = CString(_T("Gray")) + _T("Cat");<br />
这将使得你的代码可以直接移植。<br />
<br />
char* 转化为 CString<br />
<br />
　　现在你有一个 char* 类型的数据，或者说一个字符串。怎么样创建 CString 对象呢？这里有一些例子：<br />
<br />
char * p = "This is a test";<br />
或者象下面这样更具有 Unicode 意识：<br />
<br />
TCHAR * p = _T("This is a test")<br />
或<br />
<br />
LPTSTR p = _T("This is a test");<br />
你可以使用下面任意一种写法：<br />
<br />
CString s = "This is a test"; // 8-bit only<br />
CString s = _T("This is a test"); // Unicode-aware<br />
CString s("This is a test"); // 8-bit only<br />
CString s(_T("This is a test")); // Unicode-aware<br />
CString s = p;<br />
CString s(p);<br />
　　用这些方法可以轻松将常量字符串或指针转换成 CString。需要注意的是，字符的赋值总是被拷贝到 CString 对象中去的，所以你可以象下面这样操作：<br />
<br />
TCHAR * p = _T("Gray");<br />
CString s(p);<br />
p = _T("Cat");<br />
s += p;<br />
结果字符串肯定是&#8220;GrayCat&#8221;。<br />
<br />
CString 类还有几个其它的构造函数，但是这里我们不考虑它，如果你有兴趣可以自己查看相关文档。<br />
<br />
事实上，CString 类的构造函数比我展示的要复杂，比如：<br />
<br />
CString s = "This is a test"; <br />
　　这是很草率的编码，但是实际上它在 Unicode 环境下能编译通过。它在运行时调用构造函数的 MultiByteToWideChar 操作将 8 位字符串转换成 16 位字符串。不管怎样，如果 char * 指针是网络上传输的 8 位数据，这种转换是很有用的。<br />
<br />
CString 转化成 char* 之一：强制类型转换为 LPCTSTR；<br />
<br />
　　这是一种略微硬性的转换，有关&#8220;正确&#8221;的做法，人们在认识上还存在许多混乱，正确的使用方法有很多，但错误的使用方法可能与正确的使用方法一样多。<br />
　　我们首先要了解 CString 是一种很特殊的 C++ 对象，它里面包含了三个值：一个指向某个数据缓冲区的指针、一个是该缓冲中有效的字符记数以及一个缓冲区长度。 有效字符数的大小可以是从0到该缓冲最大长度值减1之间的任何数（因为字符串结尾有一个NULL字符）。字符记数和缓冲区长度被巧妙隐藏。<br />
　　除非你做一些特殊的操作，否则你不可能知道给CString对象分配的缓冲区的长度。这样，即使你获得了该0缓冲的地址，你也无法更改其中的内容，不能截短字符串，也 绝对没有办法加长它的内容，否则第一时间就会看到溢出。<br />
　　LPCTSTR 操作符（或者更明确地说就是 TCHAR * 操作符）在 CString 类中被重载了，该操作符的定义是返回缓冲区的地址，因此，如果你需要一个指向 CString 的 字符串指针的话，可以这样做：<br />
<br />
<br />
CString s("GrayCat");<br />
LPCTSTR p = s;<br />
　　它可以正确地运行。这是由C语言的强制类型转化规则实现的。当需要强制类型转化时，C++规测容许这种选择。比如，你可以将（浮点数）定义为将某个复数 （有一对浮点数）进行强制类型转换后只返回该复数的第一个浮点数（也就是其实部）。可以象下面这样：<br />
<br />
Complex c(1.2f, 4.8f);<br />
float realpart = c;<br />
如果(float)操作符定义正确的话，那么实部的的值应该是1.2。<br />
　　这种强制转化适合所有这种情况，例如，任何带有 LPCTSTR 类型参数的函数都会强制执行这种转换。 于是，你可能有这样一个函数（也许在某个你买来的DLL中）：<br />
<br />
BOOL DoSomethingCool(LPCTSTR s);<br />
你象下面这样调用它：<br />
<br />
CString file("c:\\myfiles\\coolstuff")<br />
BOOL result = DoSomethingCool(file);<br />
　　它能正确运行。因为 DoSomethingCool 函数已经说明了需要一个 LPCTSTR 类型的参数，因此 LPCTSTR 被应用于该参数，在 MFC 中就是返回的串地址。<br />
<br />
如果你要格式化字符串怎么办呢？<br />
<br />
CString graycat("GrayCat");<br />
CString s;<br />
s.Format("Mew! I love %s", graycat);<br />
　　注意由于在可变参数列表中的值（在函数说明中是以&#8220;...&#8221;表示的）并没有隐含一个强制类型转换操作符。你会得到什么结果呢？<br />
　　一个令人惊讶的结果，我们得到的实际结果串是：<br />
<br />
"Mew! I love GrayCat"。<br />
　　因为 MFC 的设计者们在设计 CString 数据类型时非常小心， CString 类型表达式求值后指向了字符串，所以这里看不到任何象 Format 或 sprintf 中的强制类型转换，你仍然可以得到正确的行为。描述 CString 的附加数据实际上在 CString 名义地址之后。<br />
　　有一件事情你是不能做的，那就是修改字符串。比如，你可能会尝试用&#8220;,&#8221;代替&#8220;.&#8221;（不要做这样的，如果你在乎国际化问题，你应该使用十进制转换的 National Language Support 特性，），下面是个简单的例子：<br />
<br />
CString v("1.00"); // 货币金额，两位小数<br />
LPCTSTR p = v;<br />
p[lstrlen(p) - 3] = '','';<br />
　　这时编译器会报错，因为你赋值了一个常量串。如果你做如下尝试，编译器也会错：<br />
<br />
strcat(p, "each");<br />
　　因为 strcat 的第一个参数应该是 LPTSTR 类型的数据，而你却给了一个 LPCTSTR。<br />
<br />
　　不要试图钻这个错误消息的牛角尖，这只会使你自己陷入麻烦！<br />
<br />
　　原因是缓冲有一个计数，它是不可存取的（它位于 CString 地址之下的一个隐藏区域），如果你改变这个串，缓冲中的字符计数不会反映所做的修改。此外，如果字符串长度恰好是该字符串物理限制的长度（梢后还会讲到这个问题），那么扩展该字符串将改写缓冲以外的任何数据，那是你无权进行写操作的内存（不对吗？），你会毁换坏不属于你的内存。这是应用程序真正的死亡处方。<br />
<br />
CString转化成char* 之二：使用 CString 对象的 GetBuffer 方法；<br />
<br />
　　如果你需要修改 CString 中的内容，它有一个特殊的方法可以使用，那就是 GetBuffer，它的作用是返回一个可写的缓冲指针。 如果你只是打算修改字符或者截短字符串，你完全可以这样做：<br />
<br />
CString s(_T("File.ext"));<br />
LPTSTR p = s.GetBuffer();<br />
LPTSTR dot = strchr(p, ''.''); // OK, should have used s.Find...<br />
if(p != NULL)<br />
*p = _T(''\0'');<br />
s.ReleaseBuffer();<br />
　　这是 GetBuffer 的第一种用法，也是最简单的一种，不用给它传递参数，它使用默认值 0，意思是：&#8220;给我这个字符串的指针，我保证不加长它&#8221;。当你调用 ReleaseBuffer 时，字符串的实际长度会被重新计算，然后存入 CString 对象中。<br />
　　必须强调一点，在 GetBuffer 和 ReleaseBuffer 之间这个范围，一定不能使用你要操作的这个缓冲的 CString 对象的任何方法。因为 ReleaseBuffer 被调用之前，该 CString 对象的完整性得不到保障。研究以下代码：<br />
<br />
CString s(...);<br />
<br />
LPTSTR p = s.GetBuffer();<br />
<br />
//... 这个指针 p 发生了很多事情<br />
<br />
int n = s.GetLength(); // 很糟D!!!!! 有可能给出错误的答案!!!<br />
<br />
s.TrimRight(); // 很糟!!!!! 不能保证能正常工作!!!!<br />
<br />
s.ReleaseBuffer(); // 现在应该 OK<br />
<br />
int m = s.GetLength(); // 这个结果可以保证是正确的。<br />
<br />
s.TrimRight(); // 将正常工作。<br />
　　假设你想增加字符串的长度，你首先要知道这个字符串可能会有多长，好比是声明字符串数组的时候用：<br />
<br />
char buffer[1024];<br />
表示 1024 个字符空间足以让你做任何想做得事情。在 CString 中与之意义相等的表示法：<br />
<br />
LPTSTR p = s.GetBuffer(1024);<br />
　　调用这个函数后，你不仅获得了字符串缓冲区的指针，而且同时还获得了长度至少为 1024 个字符的空间（注意，我说的是&#8220;字符&#8221;，而不是&#8220;字节&#8221;，因为 CString 是以隐含方式感知 Unicode 的）。<br />
　　同时，还应该注意的是，如果你有一个常量串指针，这个串本身的值被存储在只读内存中，如果试图存储它，即使你已经调用了 GetBuffer ，并获得一个只读内存的指针，存入操作会失败，并报告存取错误。我没有在 CString 上证明这一点，但我看到过大把的 C 程序员经常犯这个错误。<br />
　　C 程序员有一个通病是分配一个固定长度的缓冲，对它进行 sprintf 操作，然后将它赋值给一个 CString：<br />
<br />
char buffer[256];<br />
sprintf(buffer, "%......", args, ...); // ... 部分省略许多细节<br />
CString s = buffer;<br />
虽然更好的形式可以这么做：<br />
<br />
CString s;<br />
s.Format(_T("%...."), args, ...);<br />
如果你的字符串长度万一超过 256 个字符的时候，不会破坏堆栈。<br />
<br />
　　另外一个常见的错误是：既然固定大小的内存不工作，那么就采用动态分配字节，这种做法弊端更大：<br />
<br />
int len = lstrlen(parm1) + 13lstrlen(parm2) + 10 + 100;<br />
<br />
char * buffer = new char[len];<br />
<br />
sprintf(buffer, "%s is equal to %s, valid data", parm1, parm2);<br />
<br />
CString s = buffer;<br />
<br />
......<br />
<br />
delete [] buffer;<br />
它可以能被简单地写成：<br />
<br />
CString s;<br />
<br />
s.Format(_T("%s is equal to %s, valid data"), parm1, parm2);<br />
　　需要注意 sprintf 例子都不是 Unicode 就绪的，尽管你可以使用 tsprintf 以及用 _T() 来包围格式化字符串，但是基本 思路仍然是在走弯路，这这样很容易出错。<br />
<br />
CString to char * 之三：和控件的接口；<br />
<br />
　　我们经常需要把一个 CString 的值传递给一个控件，比如，CTreeCtrl。MFC为我们提供了很多便利来重载这个操作，但是 在大多数情况下，你使用&#8220;原始&#8221;形式的更新，因此需要将墨某个串指针存储到 TVINSERTITEMSTRUCT 结构的 TVITEM 成员中。如下：<br />
<br />
TVINSERTITEMSTRUCT tvi;<br />
CString s;<br />
// ... 为s赋一些值。<br />
tvi.item.pszText = s; // Compiler yells at you here<br />
// ... 填写tvi的其他域<br />
HTREEITEM ti = c_MyTree.InsertItem(&amp;tvi);<br />
　　为什么编译器会报错呢？明明看起来很完美的用法啊！但是事实上如果你看看 TVITEM 结构的定义你就会明白，在 TVITEM 结构中 pszText 成员的声明如下：<br />
<br />
LPTSTR pszText;<br />
int cchTextMax;<br />
　　因此，赋值不是赋给一个 LPCTSTR 类型的变量，而且编译器无法知道如何将赋值语句右边强制转换成 LPCTSTR。好吧，你说，那我就改成这样：<br />
<br />
tvi.item.pszText = (LPCTSTR)s; //编译器依然会报错。<br />
　　编译器之所以依然报错是因为你试图把一个 LPCTSTR 类型的变量赋值给一个 LPTSTR 类型的变量，这种操作在C或C++中是被禁止的。你不能用这种方法 来滥用常量指针与非常量指针概念，否则，会扰乱编译器的优化机制，使之不知如何优化你的程序。比如，如果你这么做：<br />
<br />
const int i = ...;<br />
//... do lots of stuff<br />
... = a[i]; // usage 1<br />
// ... lots more stuff<br />
... = a[i]; // usage 2<br />
　　那么，编译器会以为既然 i 是 const ，所以 usage1和usage2的值是相同的，并且它甚至能事先计算好 usage1 处的 a[i] 的地址，然后保留着在后面的 usage2 处使用，而不是重新计算。如果你按如下方式写的话：<br />
<br />
const int i = ...;<br />
int * p = &amp;i;<br />
//... do lots of stuff<br />
... = a[i]; // usage 1<br />
// ... lots more stuff<br />
(*p)++; // mess over compiler''s assumption<br />
// ... and other stuff<br />
... = a[i]; // usage 2<br />
　　编译器将认为 i 是常量，从而 a[i] 的位置也是常量，这样间接地破坏了先前的假设。因此，你的程序将会在 debug 编译模式（没有优化）和 release 编译模式（完全优化）中反映出不同的行为，这种情况可不好，所以当你试图把指向 i 的指针赋值给一个 可修改的引用时，会被编译器诊断为这是一种伪造。这就是为什么（LPCTSTR）强制类型转化不起作用的原因。<br />
　　为什么不把该成员声明成 LPCTSTR 类型呢？因为这个结构被用于读写控件。当你向控件写数据时，文本指针实际上被当成 LPCTSTR，而当你从控件读数据 时，你必须有一个可写的字符串。这个结构无法区分它是用来读还是用来写。<br />
<br />
因此，你会常常在我的代码中看到如下的用法：<br />
<br />
tvi.item.pszText = (LPTSTR)(LPCTSTR)s;<br />
　　它把 CString 强制类型转化成 LPCTSTR，也就是说先获得改字符串的地址，然后再强制类型转化成 LPTSTR，以便可以对之进行赋值操作。 注意这只有在使用 Set 或 Insert 之类的方法才有效！如果你试图获取数据，则不能这么做。<br />
　　如果你打算获取存储在控件中的数据，则方法稍有不同，例如，对某个 CTreeCtrl 使用 GetItem 方法，我想获取项目的文本。我知道这些 文本的长度不会超过 MY_LIMIT，因此我可以这样写：<br />
<br />
TVITEM tvi;<br />
// ... assorted initialization of other fields of tvi<br />
tvi.pszText = s.GetBuffer(MY_LIMIT);<br />
tvi.cchTextMax = MY_LIMIT;<br />
c_MyTree.GetItem(&amp;tvi);<br />
s.ReleaseBuffer();<br />
　　可以看出来，其实上面的代码对所有类型的 Set 方法都适用，但是并不需要这么做，因为所有的类 Set 方法（包括 Insert方法）不会改变字符串的内容。但是当你需要写 CString 对象时，必须保证缓冲是可写的，这正是 GetBuffer 所做的事情。再次强调： 一旦做了一次 GetBuffer 调用，那么在调用 ReleaseBuffer 之前不要对这个 CString 对象做任何操作。<br />
<br />
5、<strong>CString 型转化成 BSTR 型</strong><br />
<br />
　　当我们使用 ActiveX 控件编程时，经常需要用到将某个值表示成 BSTR 类型。BSTR 是一种记数字符串，Intel平台上的宽字符串（Unicode），并且 可以包含嵌入的 NULL 字符。<br />
<br />
你可以调用 CString 对象的 AllocSysString 方法将 CString 转化成 BSTR：<br />
<br />
CString s;<br />
s = ... ; // whatever<br />
BSTR b = s.AllocSysString();<br />
　　现在指针 b 指向的就是一个新分配的 BSTR 对象，该对象是 CString 的一个拷贝，包含终结 NULL字符。现在你可以将它传递给任何需要 BSTR 的接口。通常，BSTR 由接收它的组件来释放，如果你需要自己释放 BSTR 的话，可以这么做：<br />
<br />
::SysFreeString(b);<br />
　　对于如何表示传递给 ActiveX 控件的字符串，在微软内部曾一度争论不休，最后 Visual Basic 的人占了上风，BSTR（&#8220;Basic String&#8221;的首字母缩写）就是这场争论的结果。<br />
<br />
6、<strong>BSTR 型转化成 CString 型</strong><br />
<br />
　　由于 BSTR 是记数 Unicode 字符串，你可以用标准转换方法来创建 8 位的 CString。实际上，这是 CString 内建的功能。在 CString 中 有特殊的构造函数可以把 ANSI 转化成 Unicode，也可以把Unicode 转化成 ANSI。你同样可以从 VARIANT 类型的变量中获得 BSTR 类型的字符串，VARIANT 类型是 由各种 COM 和 Automation (自动化)调用返回的类型。<br />
<br />
例如，在一个ANSI程序中：<br />
<br />
BSTR b;<br />
b = ...; // whatever<br />
CString s(b == NULL ? L"" : b)<br />
　　对于单个的 BSTR 串来说，这种用法可以工作得很好，这是因为 CString 有一个特殊的构造函数以LPCWSTR（BSTR正是这种类型） 为参数，并将它转化成 ANSI 类型。专门检查是必须的，因为 BSTR 可能为空值，而 CString 的构造函数对于 NULL 值情况考虑的不是很周到，（感谢 Brian Ross 指出这一点!）。这种用法也只能处理包含 NUL 终结字符的单字符串；如果要转化含有多个 NULL 字符 串，你得额外做一些工作才行。在 CString 中内嵌的 NULL 字符通常表现不尽如人意，应该尽量避免。<br />
　　根据 C/C++ 规则，如果你有一个 LPWSTR，那么它别无选择，只能和 LPCWSTR 参数匹配。<br />
<br />
在 Unicode 模式下，它的构造函数是：<br />
<br />
CString::CString(LPCTSTR);<br />
正如上面所表示的，在 ANSI 模式下，它有一个特殊的构造函数：<br />
<br />
CString::CString(LPCWSTR); <br />
　　它会调用一个内部的函数将 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 />
<br />
<br />
CString convert(BSTR b)<br />
{<br />
if(b == NULL)<br />
return CString(_T(""));<br />
CString s(b); // in UNICODE mode<br />
return s;<br />
}<br />
<br />
　　如果是 ANSI 模式，则需要更复杂的过程来转换。注意这个代码使用与 ::WideCharToMultiByte 相同的参数值。所以你 只能在想要改变这些参数进行转换时使用该技术。例如，指定不同的默认字符，不同的标志集等。 <br />
CString convert(BSTR b)<br />
{<br />
CString s;<br />
if(b == NULL)<br />
 return s; // empty for NULL BSTR<br />
#ifdef UNICODE<br />
s = b;<br />
#else<br />
LPSTR p = s.GetBuffer(SysStringLen(b) + 1); <br />
::WideCharToMultiByte(CP_ACP,// ANSI Code Page<br />
0, // no flags<br />
b, // source widechar string<br />
-1,// assume NUL-terminated<br />
p, // target buffer<br />
SysStringLen(b)+1, // target buffer length<br />
NULL,// use system default char<br />
NULL); // don''t care if default used<br />
s.ReleaseBuffer();<br />
#endif<br />
return s;<br />
}<br />
<br />
　　我并不担心如果 BSTR 包含没有映射到 8 位字符集的 Unicode 字符时会发生什么，因为我指定了::WideCharToMultiByte 的最后两个参数为 NULL。这就是你可能需要改变的地方。 <br />
<br />
7、<strong>VARIANT 型转化成 CString 型</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 转换的告诫，以及你觉得是否可以接受（大多数情况下，你会满意的）。<br />
VARIANT vaData;<br />
vaData = m_com.YourMethodHere();<br />
ASSERT(vaData.vt == VT_BSTR);<br />
CString strData(vaData.bstrVal);<br />
你还可以根据 vt 域的不同来建立更通用的转换例程。为此你可能会考虑：<br />
<br />
<br />
CString VariantToString(VARIANT * va)<br />
{<br />
CString s;<br />
switch(va-&gt;vt)<br />
{ /* vt */<br />
 case VT_BSTR:<br />
return CString(vaData-&gt;bstrVal);<br />
 case VT_BSTR | VT_BYREF:<br />
return CString(*vaData-&gt;pbstrVal);<br />
 case VT_I4:<br />
s.Format(_T("%d"), va-&gt;lVal);<br />
return s;<br />
 case VT_I4 | VT_BYREF:<br />
s.Format(_T("%d"), *va-&gt;plVal);<br />
 case VT_R8:<br />
s.Format(_T("%f"), va-&gt;dblVal);<br />
return s;<br />
 ... 剩下的类型转换由读者自己完成<br />
 default:<br />
ASSERT(FALSE); // unknown VARIANT type (this ASSERT is optional)<br />
return CString("");<br />
} /* vt */<br />
}<br />
<br />
8、<strong>载入字符串表资源</strong><br />
<br />
　　如果你想创建一个容易进行语言版本移植的应用程序，你就不能在你的源代码中直接包含本土语言字符串 （下面这些例子我用的语言都是英语，因为我的本土语是英语），比如下面这种写法就很糟：<br />
CString s = "There is an error";<br />
　　你应该把你所有特定语言的字符串单独摆放（调试信息、在发布版本中不出现的信息除外）。这意味着向下面这样写比较好：<br />
<br />
s.Format(_T("%d - %s"), code, text);<br />
　　在你的程序中，文字字符串不是语言敏感的。不管怎样，你必须很小心，不要使用下面这样的串：<br />
<br />
// fmt is "Error in %s file %s"<br />
// readorwrite is "reading" or "writing"<br />
s.Format(fmt, readorwrite, filename); <br />
　　这是我的切身体会。在我的第一个国际化的应用程序中我犯了这个错误，尽管我懂德语，知道在德语的语法中动词放在句子的最后面，我们的德国方面的发行人还是苦苦的抱怨他们不得不提取那些不可思议的德语错误提示信息然后重新格式化以让它们能正常工作。比较好的办法（也是我现在使用的办法）是使用两个字符串，一个用 于读，一个用于写，在使用时加载合适的版本，使得它们对字符串参数是非敏感的。也就是说加载整个格式，而不是加载串&#8220;reading&#8221;，&#8220;writing&#8221;：<br />
<br />
// fmt is "Error in reading file %s"<br />
// "Error in writing file %s"<br />
s.Format(fmt, filename);<br />
　　一定要注意，如果你有好几个地方需要替换，你一定要保证替换后句子的结构不会出现问题，比如在英语中，可以是主语-宾语，主语-谓语，动词-宾语的结构等等。<br />
　　在这里，我们并不讨论 FormatMessage，其实它比 sprintf/Format 还要有优势，但是不太容易和CString 结合使用。解决这种问题的办法就是我们按照参数出现在参数表中的位置给参数取名字，这样在你输出的时候就不会把他们的位置排错了。<br />
　　接下来我们讨论我们这些独立的字符串放在什么地方。我们可以把字符串的值放入资源文件中的一个称为 STRINGTABLE 的段中。过程如下：首先使用 Visual Studio 的资源编辑器创建一个字符串，然后给每一个字符串取一个ID，一般我们给它取名字都以 IDS_开头。所以如果你有一个信息，你可以创建一个字符串资源然后取名为 IDS_READING_FILE，另外一个就取名为 IDS_WRITING_FILE。它们以下面的形式出现在你的 .rc 文件中：<br />
<br />
STRINGTABLE<br />
IDS_READING_FILE "Reading file %s"<br />
IDS_WRITING_FILE "Writing file %s"<br />
END<br />
注意：这些资源都以 Unicode 的格式保存，不管你是在什么环境下编译。他们在Win9x系统上也是以Unicode 的形式存在，虽然 Win9x 不能真正处理 Unicode。<br />
然后你可以这样使用这些资源：<br />
// 在使用资源串表之前，程序是这样写的：<br />
<br />
<br />
 CString fmt;<br />
if(...)<br />
fmt = "Reading file %s";<br />
 else<br />
 fmt = "Writing file %s";<br />
...<br />
// much later<br />
CString s;<br />
s.Format(fmt, filename); <br />
<br />
// 使用资源串表之后，程序这样写： <br />
CString fmt;<br />
if(...)<br />
 fmt.LoadString(IDS_READING_FILE);<br />
else<br />
 fmt.LoadString(DS_WRITING_FILE);<br />
...<br />
// much later<br />
CString s;<br />
s.Format(fmt, filename);<br />
<br />
　　现在，你的代码可以移植到任何语言中去。LoadString 方法需要一个字符串资源的 ID 作为参数，然后它从 STRINGTABLE 中取出它对应的字符串，赋值给 CString 对象。 CString 对象的构造函数还有一个更加聪明的特征可以简化 STRINGTABLE 的使用。这个用法在 CString::CString 的文档中没有指出，但是在 构造函数的示例程序中使用了。（为什么这个特性没有成为正式文档的一部分，而是放在了一个例子中，我记不得了！）——【译者注：从这句话看，作者可能是CString的设计者。其实前面还有一句类似的话。说他没有对使用GetBuffer(0)获得的指针指向的地址是否可读做有效性检查 】。这个特征就是：如果你将一个字符串资源的ID强制类型转换为 LPCTSTR，将会隐含调用 LoadString。因此，下面两个构造字符串的例子具有相同的效果，而且其 ASSERT 在debug模式下不会被触发：<br />
CString s;<br />
s.LoadString(IDS_WHATEVER);<br />
CString t( (LPCTSTR)IDS_WHATEVER );<br />
ASSERT(s == t);//不会被触发，说明s和t是相同的。<br />
　　现在，你可能会想：这怎么可能工作呢？我们怎么能把 STRINGTABLE ID 转化成一个指针呢？很简单：所有的字符串 ID 都在1~65535这个范围内，也就是说，它所有的高位都是0，而我们在程序中所使用的指针是不可能小于65535的，因为程序的低 64K 内存永远也不可能存在的，如果你试图访问0x00000000到0x0000FFFF之间的内存，将会引发一个内存越界错误。所以说1~65535的值不可能是一个内存地址，所以我们可以用这些值来作为字符串资源的ID。<br />
　　我倾向于使用 MAKEINTRESOURCE 宏显式地做这种转换。我认为这样可以让代码更加易于阅读。这是个只适合在 MFC 中使用的标准宏。你要记住，大多数的方法即可以接受一个 UINT 型的参数，也可以接受一个 LPCTSTR 型的参数，这是依赖 C++ 的重载功能做到的。C++重载函数带来的 弊端就是造成所有的强制类型转化都需要显示声明。同样，你也可以给很多种结构只传递一个资源名。<br />
<br />
CString s;<br />
s.LoadString(IDS_WHATEVER);<br />
CString t( MAKEINTRESOURCE(IDS_WHATEVER));<br />
ASSERT(s == t);<br />
　　告诉你吧：我不仅只是在这里鼓吹，事实上我也是这么做的。在我的代码中，你几乎不可能找到一个字符串，当然，那些只是偶然在调试中出现的或者和语言无关的字符串除外。<br />
<br />
9、<strong>CString 和临时对象</strong><br />
<br />
　　这是出现在 microsoft.public.vc.mfc 新闻组中的一个小问题，我简单的提一下，这个问题是有个程序员需要往注册表中写入一个字符串，他写道：<br />
　　我试着用 RegSetvalueEx() 设置一个注册表键的值，但是它的结果总是令我困惑。当我用char[]声明一个变量时它能正常工作，但是当我用 CString 的时候，总是得到一些垃圾："&#221;&#221;&#221;&#221;...&#221;&#221;&#221;&#221;&#221;&#221;"为了确认是不是我的 CString 数据出了问题，我试着用 GetBuffer，然后强制转化成 char*，LPCSTR。GetBuffer 返回的值是正确的，但是当我把它赋值给 char* 时，它就变成垃圾了。以下是我的程序段：<br />
<br />
char* szName = GetName().GetBuffer(20);<br />
RegSetvalueEx(hKey, "Name", 0, REG_SZ, <br />
 (CONST BYTE *) szName,<br />
 strlen (szName + 1));<br />
这个 Name 字符串的长度小于 20，所以我不认为是 GetBuffer 的参数的问题。<br />
<br />
真让人困惑，请帮帮我。<br />
<br />
亲爱的 Frustrated，<br />
<br />
你犯了一个相当微妙的错误，聪明反被聪明误，正确的代码应该象下面这样：<br />
<br />
<br />
CString Name = GetName();<br />
RegSetvalueEx(hKey, _T("Name"), 0, REG_SZ, <br />
(CONST BYTE *) (LPCTSTR)Name,<br />
(Name.GetLength() + 1) * sizeof(TCHAR));<br />
<br />
　　为什么我写的代码能行而你写的就有问题呢？主要是因为当你调用 GetName 时返回的 CString 对象是一个临时对象。参见：《C++ Reference manual》&#167;12.2<br />
　　在一些环境中，编译器有必要创建一个临时对象，这样引入临时对象是依赖于实现的。如果编译器引入的这个临时对象所属的类有构造函数的话，编译器要确保这个类的构造函数被调用。同样的，如果这个类声明有析构函数的话，也要保证这个临时对象的析构函数被调用。<br />
　　编译器必须保证这个临时对象被销毁了。被销毁的确切地点依赖于实现.....这个析构函数必须在退出创建该临时对象的范围之前被调用。<br />
　　大部分的编译器是这样设计的：在临时对象被创建的代码的下一个执行步骤处隐含调用这个临时对象的析构函数，实现起来，一般都是在下一个分号处。因此，这个 CString 对象在 GetBuffer 调用之后就被析构了（顺便提一句，你没有理由给 GetBuffer 函数传递一个参数，而且没有使用ReleaseBuffer 也是不对的）。所以 GetBuffer 本来返回的是指向这个临时对象中字符串的地址的指针，但是当这个临时对象被析构后，这块内存就被释放了。然后 MFC 的调试内存分配器会重新为这块内存全部填上 0xDD，显示出来刚好就是&#8220;&#221;&#8221;符号。在这个时候你向注册表中写数据，字符串的内容当然全被破坏了。<br />
　　我们不应该立即把这个临时对象转化成 char* 类型，应该先把它保存到一个 CString 对象中，这意味着把临时对象复制了一份，所以当临时的 CString 对象被析构了之后，这个 CString 对象中的值依然保存着。这个时候再向注册表中写数据就没有问题了。<br />
　　此外，我的代码是具有 Unicode 意识的。那个操作注册表的函数需要一个字节大小，使用lstrlen(Name+1) 得到的实际结果对于 Unicode 字符来说比 ANSI 字符要小一半，而且它也不能从这个字符串的第二个字符起开始计算，也许你的本意是 lstrlen(Name) + 1（OK，我承认，我也犯了同样的错误！）。不论如何，在 Unicode 模式下，所有的字符都是2个字节大小，我们需要处理这个问题。微软的文档令人惊讶地对此保持缄默：REG_SZ 的值究竟是以字节计算还是以字符计算呢？我们假设它指的是以字节为单位计算，你需要对你的代码做一些修改来计算这个字符串所含有的字节大小。<br />
<br />
10、<strong>CString 的效率</strong><br />
<br />
　　CString 的一个问题是它确实掩藏了一些低效率的东西。从另外一个方面讲，它也确实可以被实现得更加高效，你可能会说下面的代码：<br />
CString s = SomeCString1;<br />
s += SomeCString2;<br />
s += SomeCString3;<br />
s += ",";<br />
s += SomeCString4;<br />
比起下面的代码来，效率要低多了：<br />
<br />
char s[1024];<br />
lstrcpy(s, SomeString1);<br />
lstrcat(s, SomeString2);<br />
lstrcat(s, SomeString 3);<br />
lstrcat(s, ",");<br />
lstrcat(s, SomeString4);<br />
　　总之，你可能会想，首先，它为 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 真的这么"高效"吗？比如，如果我创建<br />
<br />
CString cat("Mew!");<br />
　　然后我并不是得到了一个高效的、精简的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 />
　　记住，+= 运算符只是一种特例，如果你写成下面这样：<br />
<br />
CString s = SomeCString1 + SomeCString2 + SomeCString3 + "," + SomeCString4;<br />
则每一个 + 的应用会造成一个新的字符串被创建和一次复制操作。<br />
<br />
总结<br />
<br />
　　以上是使用 CString 的一些技巧。我每天写程序的时候都会用到这些。CString 并不是一种很难使用的类，但是 MFC 没有很明显的指出这些特征，需要你自己去探索、去发现。</font><br />
<br />
&nbsp;<br />
<br />
<img src ="http://www.blogjava.net/wangxinsh55/aggbug/143671.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wangxinsh55/" target="_blank">SIMONE</a> 2007-09-08 20:42 <a href="http://www.blogjava.net/wangxinsh55/archive/2007/09/08/143671.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OllyDBG破解入门教程</title><link>http://www.blogjava.net/wangxinsh55/archive/2007/06/27/126616.html</link><dc:creator>SIMONE</dc:creator><author>SIMONE</author><pubDate>Wed, 27 Jun 2007 08:14:00 GMT</pubDate><guid>http://www.blogjava.net/wangxinsh55/archive/2007/06/27/126616.html</guid><wfw:comment>http://www.blogjava.net/wangxinsh55/comments/126616.html</wfw:comment><comments>http://www.blogjava.net/wangxinsh55/archive/2007/06/27/126616.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wangxinsh55/comments/commentRss/126616.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wangxinsh55/services/trackbacks/126616.html</trackback:ping><description><![CDATA[<table cellSpacing=0 cellPadding=0 width="96%" border=0>
    <tbody>
        <tr>
            <td>
            <table class=t_msg cellSpacing=0 cellPadding=４ border=0>
                <tbody>
                    <tr>
                        <td class=line style="PADDING-TOP: 10px" vAlign=top height="100%">
                        <div style="FONT-SIZE: 14px">一、OllyDBG 的安装与配置<br>OllyDBG 1.10 版的发布版本是个 ZIP 压缩包，只要解压到一个目录下，运行 OllyDBG.exe 就可以了。汉化版的发布版本是个 RAR 压缩包，同样只需解压到一个目录下运行 OllyDBG.exe 即可：</div>
                        <div style="FONT-SIZE: 14px" align=left><img onmousewheel="return bbimg(this)" style="ZOOM: 70%" alt="" hspace=0 src="http://www.955263.com/Article/UploadFiles/200706/20070608082210126.jpg" onload=javascript:resizepic(this) border=0><br><br>OllyDBG 中各个窗口的功能如上图。简单解释一下各个窗口的功能，更详细的内容可以参考 TT 小组翻译的中文帮助：<br>反汇编窗口：显示被调试程序的反汇编代码，标题栏上的地址、HEX 数据、反汇编、注释可以通过在窗口中右击出现的菜单 界面选项-&gt;隐藏标题 或 显示标题 来进行切换是否显示。用鼠标左键点击注释标签可以切换注释显示的方式。<br>寄存器窗口：显示当前所选线程的 CPU 寄存器内容。同样点击标签 寄存器 (FPU) 可以切换显示寄存器的方式。<br>信息窗口：显示反汇编窗口中选中的第一个命令的参数及一些跳转目标地址、字串等。<br>数据窗口：显示内存或文件的内容。右键菜单可用于切换显示方式。<br>堆栈窗口：显示当前线程的堆栈。<br>要调整上面各个窗口的大小的话，只需左键按住边框拖动，等调整好了，重新启动一下 OllyDBG 就可以生效了。<br>启动后我们要把插件及 UDD 的目录配置为绝对路径，点击菜单上的 选项-&gt;界面，将会出来一个界面选项的对话框，我们点击其中的目录标签：<br><img onmousewheel="return bbimg(this)" alt="" hspace=0 src="http://www.955263.com/Article/UploadFiles/200706/20070608082211857.jpg" onload=javascript:resizepic(this) border=0><br>因为我这里是把 OllyDBG 解压在 F:\OllyDBG 目录下，所以相应的 UDD 目录及插件目录按图上配置。还有一个常用到的标签就是上图后面那个字体，在这里你可以更改 OllyDBG 中显示的字体。上图中其它的选项可以保留为默认，若有需要也可以自己修改。修改完以后点击确定，弹出一个对话框，说我们更改了插件路径，要重新启动 OllyDBG。在这个对话框上点确定，重新启动一下 OllyDBG，我们再到界面选项中看一下，会发现我们原先设置好的路径都已保存了。有人可能知道插件的作用，但对那个 UDD 目录不清楚。我这简单解释一下：这个 UDD 目录的作用是保存你调试的工作。比如你调试一个软件，设置了断点，添加了注释，一次没做完，这时 OllyDBG 就会把你所做的工作保存到这个 UDD 目录，以便你下次调试时可以继续以前的工作。<br><br>如果不设置这个 UDD 目录，OllyDBG 默认是在其安装目录下保存这些后缀名为 udd 的文件，时间长了就会显的很乱，所以还是建议专门设置一个目录来保存这些文件。<br>另外一个重要的选项就是调试选项，可通过菜单 选项-&gt;调试设置 来配置：<br><img onmousewheel="return bbimg(this)" alt="" hspace=0 src="http://www.955263.com/Article/UploadFiles/200706/20070608082211889.jpg" onload=javascript:resizepic(this) border=0><br>新手一般不需更改这里的选项，默认已配置好，可以直接使用。建议在对 OllyDBG 已比较熟的情况下再来进行配置。上面那个异常标签中的选项经常会在脱壳中用到，建议在有一定调试基础后学脱壳时再配置这里。<br>除了直接启动 OllyDBG 来调试外，我们还可以把 OllyDBG 添加到资源管理器右键菜单，这样我们就可以直接在 .exe 及 .dll 文件上点右键选择&#8220;用Ollydbg打开&#8221;菜单来进行调试。要把 OllyDBG 添加到资源管理器右键菜单，只需点菜单 选项-&gt;添加到浏览器，将会出现一个对话框，先点击&#8220;添加 Ollydbg 到系统资源管理器菜单&#8221;，再点击&#8220;完成&#8221;按钮即可。要从右键菜单中删除也很简单，还是这个对话框，点击&#8220;从系统资源管理器菜单删除 Ollydbg&#8221;，再点击&#8220;完成&#8221;就行了。<br><br>OllyDBG 支持插件功能，插件的安装也很简单，只要把下载的插件（一般是个 DLL 文件）复制到 OllyDBG 安装目录下的 PLUGIN 目录中就可以了，OllyDBG 启动时会自动识别。要注意的是 OllyDBG 1.10 对插件的个数有限制，最多不能超过 32 个，否则会出错。建议插件不要添加的太多。<br>到这里基本配置就完成了，OllyDBG 把所有配置都放在安装目录下的 ollydbg.ini 文件中。</div>
                        </td>
                    </tr>
                    <tr>
                        <td vAlign=bottom><br></td>
                    </tr>
                </tbody>
            </table>
            <div style="FONT-SIZE: 14px">二、基本调试方法<br>OllyDBG 有三种方式来载入程序进行调试，一种是点击菜单 文件-&gt;打开 （快捷键是 F3）来打开一个可执行文件进行调试，另一种是点击菜单 文件-&gt;附加 来附加到一个已运行的进程上进行调试。注意这里要附加的程序必须已运行。第三种就是用右键菜单来载入程序（不知这种算不算）。一般情况下我们选第一种方式。比如我们选择一个 test.exe 来调试，通过菜单 文件-&gt;打开 来载入这个程序，OllyDBG 中显示的内容将会是这样：<br><img onmousewheel="return bbimg(this)" alt="" hspace=0 src="http://www.955263.com/Article/UploadFiles/200706/20070608082211911.jpg" onload=javascript:resizepic(this) border=0><br>调试中我们经常要用到的快捷键有这些：<br>F2：设置断点，只要在光标定位的位置（上图中灰色条）按F2键即可，再按一次F2键则会删除断点。（相当于 SoftICE 中的 F9）<br>F8：单步步过。每按一次这个键执行一条反汇编窗口中的一条指令，遇到 CALL 等子程序不进入其代码。（相当于 SoftICE 中的 F10）<br>F7：单步步入。功能同单步步过(F8)类似，区别是遇到 CALL 等子程序时会进入其中，进入后首先会停留在子程序的第一条指令上。（相当于 SoftICE 中的 F8）<br>F4：运行到选定位置。作用就是直接运行到光标所在位置处暂停。（相当于 SoftICE 中的 F7）<br>F9：运行。按下这个键如果没有设置相应断点的话，被调试的程序将直接开始运行。（相当于 SoftICE 中的 F5）<br>CTR+F9：执行到返回。此命令在执行到一个 ret (返回指令)指令时暂停，常用于从系统领空返回到我们调试的程序领空。（相当于 SoftICE 中的 F12）<br>ALT+F9：执行到用户代码。可用于从系统领空快速返回到我们调试的程序领空。（相当于 SoftICE 中的 F11）<br>上面提到的几个快捷键对于一般的调试基本上已够用了。要开始调试只需设置好断点，找到你感兴趣的代码段再按 F8 或 F7 键来一条条分析指令功能就可以了。就写到这了，改天有空再接着灌。<br><br>OllyDBG 入门系列（二）－字串参考<br><br>作者：CCDebuger<br>上一篇是使用入门，现在我们开始正式进入破解。今天的目标程序是看雪兄《加密与解密》第一版附带光盘中的 crackmes.cjb.net 镜像打包中的 CFF Crackme #3，采用用户名/序列号保护方式。原版加了个 UPX 的壳。刚开始学破解先不涉及壳的问题，我们主要是熟悉用 OllyDBG 来破解的一般方法。我这里把壳脱掉来分析，附件是脱壳后的文件，直接就可以拿来用。先说一下一般软件破解的流程：拿到一个软件先别接着马上用 OllyDBG 调试，先运行一下，有帮助文档的最好先看一下帮助，熟悉一下软件的使用方法，再看看注册的方式。如果是序列号方式可以先输个假的来试一下，看看有什么反应，也给我们破解留下一些有用的线索。如果没有输入注册码的地方，要考虑一下是不是读取注册表或 Key 文件（一般称 keyfile，就是程序读取一个文件中的内容来判断是否注册），这些可以用其它工具来辅助分析。如果这些都不是，原程序只是一个功能不全的试用版，那要注册为正式版本就要自己来写代码完善了。有点跑题了，呵呵。获得程序的一些基本信息后，还要用查壳的工具来查一下程序是否加了壳，若没壳的话看看程序是什么编译器编的，如 VC、Delphi、VB 等。这样的查壳工具有 PEiD 和 FI。有壳的话我们要尽量脱了壳后再来用 OllyDBG 调试，特殊情况下也可带壳调试。下面进入正题：<br>我们先来运行一下这个 crackme（用 PEiD 检测显示是 Delphi 编的），界面如图：<br><img onmousewheel="return bbimg(this)" alt="" hspace=0 src="http://www.955263.com/Article/UploadFiles/200706/20070608082212476.jpg" onload=javascript:resizepic(this) border=0><br>这个 crackme 已经把用户名和注册码都输好了，省得我们动手^_^。我们在那个&#8220;Register now !&#8221;按钮上点击一下，将会跳出一个对话框：<br><img onmousewheel="return bbimg(this)" alt="" hspace=0 src="http://www.955263.com/Article/UploadFiles/200706/20070608082456255.jpg" onload=javascript:resizepic(this) border=0><br><br><br>好了，今天我们就从这个错误对话框中显示的&#8220;Wrong Serial, try again!&#8221;来入手。启动 OllyDBG，选择菜单 文件-&gt;打开 载入 CrackMe3.exe 文件，我们会停在这里：<br><img onmousewheel="return bbimg(this)" alt="" hspace=0 src="http://www.955263.com/Article/UploadFiles/200706/20070608082457538.jpg" onload=javascript:resizepic(this) border=0><br>我们在反汇编窗口中右击，出来一个菜单，我们在 查找-&gt;所有参考文本字串 上左键点击：<br><img onmousewheel="return bbimg(this)" alt="" hspace=0 src="http://www.955263.com/Article/UploadFiles/200706/20070608082457793.jpg" onload=javascript:resizepic(this) border=0><br>当然如果用上面那个 超级字串参考＋ 插件会更方便。但我们的目标是熟悉 OllyDBG 的一些操作，我就尽量使用 OllyDBG 自带的功能，少用插件。</div>
            <div style="FONT-SIZE: 14px">
            <div style="FONT-SIZE: 14px">好了，现在出来另一个对话框，我们在这个对话框里右击，选择&#8220;查找文本&#8221;菜单项，输入&#8220;Wrong Serial, try again!&#8221;的开头单词&#8220;Wrong&#8221;（注意这里查找内容要区分大小写）来查找，找到一处：<br><img onmousewheel="return bbimg(this)" alt="" hspace=0 src="http://www.955263.com/Article/UploadFiles/200706/20070608082458390.jpg" onload=javascript:resizepic(this) border=0><br>在我们找到的字串上右击，再在出来的菜单上点击&#8220;反汇编窗口中跟随&#8221;，我们来到这里：<br><img onmousewheel="return bbimg(this)" alt="" hspace=0 src="http://www.955263.com/Article/UploadFiles/200706/20070608082458680.jpg" onload=javascript:resizepic(this) border=0><br>见上图，为了看看是否还有其他的参考，可以通过选择右键菜单查找参考-&gt;立即数，会出来一个对话框：<br><img onmousewheel="return bbimg(this)" alt="" hspace=0 src="http://www.955263.com/Article/UploadFiles/200706/20070608082635680.jpg" onload=javascript:resizepic(this) border=0><br><br><br>见上图，为了看看是否还有其他的参考，可以通过选择右键菜单查找参考-&gt;立即数，会出来一个对话框：<br><img onmousewheel="return bbimg(this)" alt="" hspace=0 src="http://www.955263.com/Article/UploadFiles/200706/20070608082635269.jpg" onload=javascript:resizepic(this) border=0><br>我们在上图中地址 00440F2C 处按 F2 键设个断点，现在我们按 F9 键，程序已运行起来了。我在上面那个编辑框中随便输入一下，如 CCDebuger，下面那个编辑框我还保留为原来的&#8220;754-GFX-IER-954&#8221;，我们点一下那个&#8220;Register now !&#8221;按钮，呵，OllyDBG 跳了出来，暂停在我们下的断点处。我们看一下信息窗口，你应该发现了你刚才输入的内容了吧？我这里显示是这样：<br>堆栈 SS:[0012F9AC]=00D44DB4, (ASCII "CCDebuger")<br>EAX=00000009<br>上面的内存地址 00D44DB4 中就是我们刚才输入的内容，我这里是 CCDebuger。你可以在 堆栈 SS:[0012F9AC]=00D44DB4, (ASCII "CCDebuger") 这条内容上左击选择一下，再点右键，在弹出菜单中选择&#8220;数据窗口中跟随数值&#8221;，你就会在下面的数据窗口中看到你刚才输入的内容。而 EAX=00000009 指的是你输入内容的长度。如我输入的 CCDebuger 是9个字符。如下图所示：<br><img onmousewheel="return bbimg(this)" alt="" hspace=0 src="http://www.955263.com/Article/UploadFiles/200706/20070608082635976.jpg" onload=javascript:resizepic(this) border=0><br>现在我们来按 F8 键一步步分析一下：<br>00440F2C |. 8B45 FC&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;MOV EAX,DWORD PTR SS:[EBP-4]&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ; 把我们输入的内容送到EAX，我这里是&#8220;CCDebuger&#8221;<br>00440F2F |. BA 14104400&nbsp; &nbsp;&nbsp;&nbsp;MOV EDX,CrackMe3.00441014&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ; ASCII "Registered User"<br>00440F34 |. E8 F32BFCFF&nbsp; &nbsp;&nbsp;&nbsp;CALL CrackMe3.00403B2C&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ; 关键，要用F7跟进去<br>00440F39 |. 75 51&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;JNZ SHORT CrackMe3.00440F8C&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;; 这里跳走就完蛋<br>当我们按 F8 键走到 00440F34 |. E8 F32BFCFF&nbsp; &nbsp;&nbsp;&nbsp;CALL CrackMe3.00403B2C 这一句时，我们按一下 F7 键，进入这个 CALL，进去后光标停在这一句：<br><img onmousewheel="return bbimg(this)" alt="" hspace=0 src="http://www.955263.com/Article/UploadFiles/200706/20070608082635755.jpg" onload=javascript:resizepic(this) border=0><br>我们所看到的那些 PUSH EBX、 PUSH ESI 等都是调用子程序保存堆栈时用的指令，不用管它，按 F8 键一步步过来，我们只关心关键部分：<br>00403B2C /$ 53&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;PUSH EBX<br>00403B2D |. 56&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;PUSH ESI<br>00403B2E |. 57&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;PUSH EDI<br>00403B2F |. 89C6&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;MOV ESI,EAX&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ; 把EAX内我们输入的用户名送到 ESI<br>00403B31 |. 89D7&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;MOV EDI,EDX&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ; 把EDX内的数据&#8220;Registered User&#8221;送到EDI<br>00403B33 |. 39D0&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;CMP EAX,EDX&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ; 用&#8220;Registered User&#8221;和我们输入的用户名作比较<br>00403B35 |. 0F84 8F000000&nbsp; &nbsp;JE CrackMe3.00403BCA&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ; 相同则跳<br>00403B3B |. 85F6&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;TEST ESI,ESI&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;; 看看ESI中是否有数据，主要是看看我们有没有输入用户名<br>00403B3D |. 74 68&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;JE SHORT CrackMe3.00403BA7&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ; 用户名为空则跳<br>00403B3F |. 85FF&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;TEST EDI,EDI<br>00403B41 |. 74 6B&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;JE SHORT CrackMe3.00403BAE<br>00403B43 |. 8B46 FC&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;MOV EAX,DWORD PTR DS:[ESI-4]&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;; 用户名长度送EAX<br>00403B46 |. 8B57 FC&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;MOV EDX,DWORD PTR DS:[EDI-4]&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;; &#8220;Registered User&#8221;字串的长度送EDX<br>00403B49 |. 29D0&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;SUB EAX,EDX&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ; 把用户名长度和&#8220;Registered User&#8221;字串长度相减<br>00403B4B |. 77 02&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;JA SHORT CrackMe3.00403B4F&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ; 用户名长度大于&#8220;Registered User&#8221;长度则跳<br>00403B4D |. 01C2&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;ADD EDX,EAX&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ; 把减后值与&#8220;Registered User&#8221;长度相加，即用户名长度<br>00403B4F |&gt; 52&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;PUSH EDX<br>00403B50 |. C1EA 02&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;SHR EDX,2&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;; 用户名长度值右移2位，这里相当于长度除以4<br>00403B53 |. 74 26&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;JE SHORT CrackMe3.00403B7B&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ; 上面的指令及这条指令就是判断用户名长度最少不能低于4<br>00403B55 |&gt; 8B0E&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;MOV ECX,DWORD PTR DS:[ESI]&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ; 把我们输入的用户名送到ECX<br>00403B57 |. 8B1F&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;MOV EBX,DWORD PTR DS:[EDI]&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ; 把&#8220;Registered User&#8221;送到EBX<br>00403B59 |. 39D9&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;CMP ECX,EBX&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ; 比较<br>00403B5B |. 75 58&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;JNZ SHORT CrackMe3.00403BB5&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;; 不等则完蛋<br>根据上面的分析，我们知道用户名必须是&#8220;Registered User&#8221;。我们按 F9 键让程序运行，出现错误对话框，点确定，重新在第一个编辑框中输入&#8220;Registered User&#8221;，再次点击那个&#8220;Register now !&#8221;按钮，被 OllyDBG 拦下。因为地址 00440F34 处的那个 CALL 我们已经分析清楚了，这次就不用再按 F7 键跟进去了，直接按 F8 键通过。我们一路按 F8 键，来到第二个关键代码处：<br>00440F49 |. 8B45 FC&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;MOV EAX,DWORD PTR SS:[EBP-4]&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;; 取输入的注册码<br>00440F4C |. BA 2C104400&nbsp; &nbsp;&nbsp;&nbsp;MOV EDX,CrackMe3.0044102C&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;; ASCII "GFX-754-IER-954"<br>00440F51 |. E8 D62BFCFF&nbsp; &nbsp;&nbsp;&nbsp;CALL CrackMe3.00403B2C&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;; 关键，要用F7跟进去<br>00440F56 |. 75 1A&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;JNZ SHORT CrackMe3.00440F72&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ; 这里跳走就完蛋<br>大家注意看一下，地址 00440F51 处的 CALL CrackMe3.00403B2C 和上面我们分析的地址 00440F34 处的 CALL CrackMe3.00403B2C 是不是汇编指令都一样啊？这说明检测用户名和注册码是用的同一个子程序。而这个子程序 CALL 我们在上面已经分析过了。我们执行到现在可以很容易得出结论，这个 CALL 也就是把我们输入的注册码与 00440F4C 地址处指令后的&#8220;GFX-754-IER-954&#8221;作比较，相等则 OK。</div>
            <div style="FONT-SIZE: 14px">&nbsp;</div>
            <div style="FONT-SIZE: 14px">
            <table class=t_msg cellSpacing=0 cellPadding=４ border=0>
                <tbody>
                    <tr>
                        <td class=line style="PADDING-TOP: 10px" vAlign=top height="100%"><a title="评分 0" href="http://bbs.hackbase.com/misc.php?action=viewratings&amp;tid=3123683&amp;pid=4758144" name=pid4758144></a>
                        <div style="FONT-SIZE: 14px">好了，我们已经得到足够的信息了。现在我们在菜单 查看-&gt;断点 上点击一下，打开断点窗口（也可以通过组合键 ALT+B 或点击工具栏上那个&#8220;B&#8221;图标打开断点窗口）：<br><img onmousewheel="return bbimg(this)" alt="" hspace=0 src="http://www.955263.com/Article/UploadFiles/200706/20070608082635813.jpg" onload=javascript:resizepic(this) border=0><br>为什么要做这一步，而不是把这个断点删除呢？这里主要是为了保险一点，万一分析错误，我们还要接着分析，要是把断点删除了就要做一些重复工作了。还是先禁用一下，如果经过实际验证证明我们的分析是正确的，再删不迟。现在我们把断点禁用，在 OllyDBG 中按 F9 键让程序运行。输入我们经分析得出的内容：<br>用户名：Registered User<br>注册码：GFX-754-IER-954<br>点击&#8220;Register now !&#8221;按钮，呵呵，终于成功了：<br><img onmousewheel="return bbimg(this)" alt="" hspace=0 src="http://www.955263.com/Article/UploadFiles/200706/20070608082756541.jpg" onload=javascript:resizepic(this) border=0><br><br>OllyDBG 入门系列（三）－函数参考<br><br>作者：CCDebuger<br>现在进入第三篇，这一篇我们重点讲解怎样使用 OllyDBG 中的函数参考（即名称参考）功能。仍然选择 crackmes.cjb.net 镜像打包中的一个名称为 CrackHead 的crackme。老规矩，先运行一下这个程序看看：<br><img onmousewheel="return bbimg(this)" alt="" hspace=0 src="http://www.955263.com/Article/UploadFiles/200706/20070608082757602.jpg" onload=javascript:resizepic(this) border=0><br>呵，竟然没找到输入注册码的地方！别急，我们点一下程序上的那个菜单&#8220;Shit&#8221;（真是 Shit 啊，呵呵），在下拉菜单中选&#8220;Try It&#8221;，会来到如下界面：<br><img onmousewheel="return bbimg(this)" alt="" hspace=0 src="http://www.955263.com/Article/UploadFiles/200706/20070608082758209.jpg" onload=javascript:resizepic(this) border=0><br>我们点一下那个&#8220;Check It&#8221;按钮试一下，哦，竟然没反应！我再输个&#8220;78787878&#8221;试试，还是没反应。再试试输入字母或其它字符，输不进去。由此判断注册码应该都是数字，只有输入正确的注册码才有动静。用 PEiD 检测一下，结果为 MASM32 / TASM32，怪不得程序比较小。信息收集的差不多了，现在关掉这个程序，我们用 OllyDBG 载入，按 F9 键直接让它运行起来，依次点击上面图中所说的菜单，使被调试程序显示如上面的第二个图。先不要点那个&#8220;Check It&#8221;按钮，保留上图的状态。现在我们没有什么字串好参考了，我们就在 API 函数上下断点，来让被调试程序中断在我们希望的地方。我们在 OllyDBG 的反汇编窗口中右击鼠标，在弹出菜单中选择 查找-&gt;当前模块中的名称 (标签)，或者我们通过按 CTR+N 组合键也可以达到同样的效果（注意在进行此操作时要在 OllyDBG 中保证是在当前被调试程序的领空，我在第一篇中已经介绍了领空的概念，如我这里调试这个程序时 OllyDBG 的标题栏显示的就是&#8220;[CPU - 主线程, 模块 - CrackHea]&#8221;，这表明我们当前在被调试程序的领空）。通过上面的操作后会弹出一个对话框，如图：<br><img onmousewheel="return bbimg(this)" alt="" hspace=0 src="http://www.955263.com/Article/UploadFiles/200706/20070608082758100.jpg" onload=javascript:resizepic(this) border=0><br>对于这样的编辑框中输注册码的程序我们要设断点首选的 API 函数就是 GetDlgItemText 及 GetWindowText。每个函数都有两个版本，一个是 ASCII 版，在函数后添加一个 A 表示，如 GetDlgItemTextA，另一个是 UNICODE 版，在函数后添加一个 W 表示。如 GetDlgItemTextW。对于编译为 UNCODE 版的程序可能在 Win98 下不能运行，因为 Win98 并非是完全支持 UNICODE 的系统。而 NT 系统则从底层支持 UNICODE，它可以在操作系统内对字串进行转换，同时支持 ASCII 和 UNICODE 版本函数的调用。一般我们打开的程序看到的调用都是 ASCII 类型的函数，以&#8220;A&#8221;结尾。又跑题了，呵呵。现在回到我们调试的程序上来，我们现在就是要找一下我们调试的程序有没有调用 GetDlgItemTextA 或 GetWindowTextA 函数。还好，找到一个 GetWindowTextA。在这个函数上右击，在弹出菜单上选择&#8220;在每个参考上设置断点&#8221;，我们会在 OllyDBG 窗口最下面的那个状态栏里看到&#8220;已设置 2 个断点&#8221;。另一种方法就是那个 GetWindowTextA 函数上右击，在弹出菜单上选择&#8220;查找输入函数参考&#8221;（或者按回车键），将会出现下面的对话框：<br><img onmousewheel="return bbimg(this)" alt="" hspace=0 src="http://www.955263.com/Article/UploadFiles/200706/20070608082759750.jpg" onload=javascript:resizepic(this) border=0><br>看上图，我们可以把两条都设上断点。这个程序只需在第一条指令设断点就可以了。好，我们现在按前面提到的第一条方法，就是&#8220;在每个参考上设置断点&#8221;，这样上图中的两条指令都会设上断点。断点设好后我们转到我们调试的程序上来，现在我们在被我们调试的程序上点击那个&#8220;Check It&#8221;按钮，被 OllyDBG 断下：<br>00401323 |. E8 4C010000&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;CALL&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;; GetWindowTextA<br>00401328 |. E8 A5000000&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;CALL CrackHea.004013D2&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ; 关键，要按F7键跟进去<br>0040132D |. 3BC6&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; CMP EAX,ESI&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;; 比较<br>0040132F |. 75 42&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;JNZ SHORT CrackHea.00401373&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;; 不等则完蛋<br>00401331 |. EB 2C&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;JMP SHORT CrackHea.0040135F<br>00401333 |. 4E 6F 77 20 7&gt;&nbsp; &nbsp;&nbsp; &nbsp;ASCII "Now write a keyg"<br>00401343 |. 65 6E 20 61 6&gt;&nbsp; &nbsp;&nbsp; &nbsp;ASCII "en and tut and y"<br>00401353 |. 6F 75 27 72 6&gt;&nbsp; &nbsp;&nbsp; &nbsp;ASCII "ou're done.",0<br>0040135F |&gt; 6A 00&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;PUSH 0&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;; Style = MB_OK|MB_APPLMODAL<br>00401361 |. 68 0F304000&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;PUSH CrackHea.0040300F&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ; Title = "Crudd's Crack Head"<br>00401366 |. 68 33134000&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;PUSH CrackHea.00401333&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ; Text = "Now write a keygen and tut and you're done."<br>0040136B |. FF75 08&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; PUSH DWORD PTR SS:[EBP+8]&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ; hOwner<br>0040136E |. E8 19010000&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;CALL&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;; MessageBoxA<br>从上面的代码，我们很容易看出 00401328 地址处的 CALL CrackHea.004013D2 是关键，必须仔细跟踪。而注册成功则会显示一个对话框，标题是&#8220;Crudd's Crack Head&#8221;，对话框显示的内容是&#8220;Now write a keygen and tut and you're done.&#8221;现在我按一下 F8，准备步进到 00401328 地址处的那条 CALL CrackHea.004013D2 指令后再按 F7 键跟进去。等等，怎么回事？怎么按一下 F8 键跑到这来了：<br>00401474 $- FF25 2C204000&nbsp; &nbsp;&nbsp; &nbsp;JMP DWORD PTR DS:[&nbsp; &nbsp; ; USER32.GetWindowTextA<br>0040147A $- FF25 30204000&nbsp; &nbsp;&nbsp; &nbsp;JMP DWORD PTR DS:[]&nbsp; &nbsp;&nbsp;&nbsp;; USER32.LoadCursorA<br>00401480 $- FF25 1C204000&nbsp; &nbsp;&nbsp; &nbsp;JMP DWORD PTR DS:[]&nbsp; &nbsp;&nbsp; &nbsp; ; USER32.LoadIconA<br>00401486 $- FF25 20204000&nbsp; &nbsp;&nbsp; &nbsp;JMP DWORD PTR DS:[]&nbsp; &nbsp;&nbsp; &nbsp; ; USER32.LoadMenuA<br>0040148C $- FF25 24204000&nbsp; &nbsp;&nbsp; &nbsp;JMP DWORD PTR DS:[]&nbsp; &nbsp;&nbsp;&nbsp;; USER32.MessageBoxA<br>原来是跳到另一个断点了。这个断点我们不需要，按一下 F2 键删掉它吧。删掉 00401474 地址处的断点后，我再按 F8 键，呵，完了，跑到 User32.dll 的领空了。看一下 OllyDBG 的标题栏：&#8220;[CPU - 主线程, 模块 - USER32]，跑到系统领空了，OllyDBG 反汇编窗口中显示代码是这样：<br>77D3213C 6A 0C&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;PUSH 0C<br>77D3213E 68 A021D377&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;PUSH USER32.77D321A0<br>77D32143 E8 7864FEFF&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;CALL USER32.77D185C0<br>怎么办？别急，我们按一下 ALT+F9 组合键，呵，回来了：<br>00401328 |. E8 A5000000&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;CALL CrackHea.004013D2&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;; 关键，要按F7键跟进去<br>0040132D |. 3BC6&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;CMP EAX,ESI&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;; 比较<br>0040132F |. 75 42&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;JNZ SHORT CrackHea.00401373&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ; 不等则完蛋<br>光标停在 00401328 地址处的那条指令上。现在我们按 F7 键跟进：<br>004013D2 /$ 56&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; PUSH ESI&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;; ESI入栈<br>004013D3 |. 33C0&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;XOR EAX,EAX&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;; EAX清零<br>004013D5 |. 8D35 C4334000&nbsp; &nbsp;&nbsp;&nbsp;LEA ESI,DWORD PTR DS:[4033C4]&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;; 把注册码框中的数值送到ESI<br>004013DB |. 33C9&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;XOR ECX,ECX&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;; ECX清零<br>004013DD |. 33D2&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;XOR EDX,EDX&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;; EDX清零<br>004013DF |. 8A06&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;MOV AL,BYTE PTR DS:[ESI]&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;; 把注册码中的每个字符送到AL<br>004013E1 |. 46&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; INC ESI&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ; 指针加1，指向下一个字符<br>004013E2 |. 3C 2D&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; CMP AL,2D&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;; 把取得的字符与16进制值为2D的字符(即&#8220;-&#8221;)比较，这里主要用于判断输入的是不是负数<br>004013E4 |. 75 08&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; JNZ SHORT CrackHea.004013EE&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;; 不等则跳<br>004013E6 |. BA FFFFFFFF&nbsp; &nbsp;&nbsp; &nbsp; MOV EDX,-1&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ; 如果输入的是负数，则把-1送到EDX，即16进制FFFFFFFF<br>004013EB |. 8A06&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;MOV AL,BYTE PTR DS:[ESI]&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;; 取&#8220;-&#8221;号后的第一个字符<br>004013ED |. 46&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; INC ESI&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ; 指针加1，指向再下一个字符<br>004013EE |&gt; EB 0B&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; JMP SHORT CrackHea.004013FB<br>004013F0 |&gt; 2C 30&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; SUB AL,30&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;; 每位字符减16进制的30，因为这里都是数字，如1的ASCII码是&#8220;31H&#8221;，减30H后为1，即我们平时看到的数值<br>004013F2 |. 8D0C89&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;LEA ECX,DWORD PTR DS:[ECX+ECX*4]&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;; 把前面运算后保存在ECX中的结果乘5再送到ECX<br>004013F5 |. 8D0C48&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;LEA ECX,DWORD PTR DS:[EAX+ECX*2]&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;; 每位字符运算后的值与2倍上一位字符运算后值相加后送ECX<br>004013F8 |. 8A06&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;MOV AL,BYTE PTR DS:[ESI]&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;; 取下一个字符<br>004013FA |. 46&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; INC ESI&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ; 指针加1，指向再下一个字符<br>004013FB |&gt; 0AC0&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;or AL,AL<br>004013FD |.^ 75 F1&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;JNZ SHORT CrackHea.004013F0&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;; 上面一条和这一条指令主要是用来判断是否已把用户输入的注册码计算完<br>004013FF |. 8D040A&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;LEA EAX,DWORD PTR DS:[EDX+ECX]&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;; 把EDX中的值与经过上面运算后的ECX中值相加送到EAX<br>00401402 |. 33C2&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;XOR EAX,EDX&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;; 把EAX与EDX异或。如果我们输入的是负数，则此处功能就是把EAX中的值取反<br>00401404 |. 5E&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; POP ESI&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ; ESI出栈。看到这条和下一条指令，我们要考虑一下这个ESI的值是哪里运算得出的呢？<br>00401405 |. 81F6 53757A79&nbsp; &nbsp;&nbsp;&nbsp;XOR ESI,797A7553&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ; 把ESI中的值与797A7553H异或<br>0040140B \. C3&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; RETN<br>这里留下了一个问题：那个 ESI 寄存器中的值是从哪运算出来的？先不管这里，我们接着按 F8 键往下走，来到 0040140B 地址处的那条 RETN 指令（这里可以通过在调试选项的&#8220;命令&#8221;标签中勾选&#8220;使用 RET 代替 RETN&#8221;来更改返回指令的显示方式），再按一下 F8，我们就走出 00401328 地址处的那个 CALL 了。现在我们回到了这里：<br>0040132D |. 3BC6&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; CMP EAX,ESI&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ; 比较<br>0040132F |. 75 42&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;JNZ SHORT CrackHea.00401373&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;; 不等则完蛋<br>光标停在了 0040132D 地址处的那条指令上。根据前面的分析，我们知道 EAX 中存放的是我们输入的注册码经过计算后的值。我们来看一下信息窗口：<br>ESI=E6B5F2F9<br>EAX=FF439EBE<br>左键选择信息窗口中的 ESI=E6B5F2F9，再按右键，在弹出菜单上选&#8220;修改寄存器&#8221;，我们会看到这样一个窗口：<br><img onmousewheel="return bbimg(this)" alt="" hspace=0 src="http://www.955263.com/Article/UploadFiles/200706/20070608082759820.jpg" onload=javascript:resizepic(this) border=0><br>可能你的显示跟我不一样，因为这个 crackme 中已经说了每个机器的序列号不一样。关掉上面的窗口，再对信息窗口中的 EAX=FF439EBE 做同样操作：<br><img onmousewheel="return bbimg(this)" alt="" hspace=0 src="http://www.955263.com/Article/UploadFiles/200706/20070608082759614.jpg" onload=javascript:resizepic(this) border=0><br>由上图我们知道了原来前面分析的对我们输入的注册码进行处理后的结果就是把字符格式转为数字格式。我们原来输入的是字串&#8220;12345666&#8221;，现在转换为了数字 12345666。这下就很清楚了，随便在上面那个修改 ESI 图中显示的有符号或无符号编辑框中复制一个，粘贴到我们调试的程序中的编辑框中试一下：<br><img onmousewheel="return bbimg(this)" alt="" hspace=0 src="http://www.955263.com/Article/UploadFiles/200706/20070608082759438.jpg" onload=javascript:resizepic(this) border=0><br>呵呵，成功了。且慢高兴，这个 crackme 是要求写出注册机的。我们先不要求写注册机，但注册的算法我们要搞清楚。还记得我在前面说到的那个 ESI 寄存器值的问题吗？现在看看我们上面的分析，其实对做注册机来说是没有多少帮助的。要搞清注册算法，必须知道上面那个 ESI 寄存器值是如何产生的，这弄清楚后才能真正清楚这个 crackme 算法。今天就先说到这里。</div>
                        </td>
                    </tr>
                </tbody>
            </table>
            </div>
            </div>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.blogjava.net/wangxinsh55/aggbug/126616.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wangxinsh55/" target="_blank">SIMONE</a> 2007-06-27 16:14 <a href="http://www.blogjava.net/wangxinsh55/archive/2007/06/27/126616.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>BMP文件格式分析</title><link>http://www.blogjava.net/wangxinsh55/archive/2007/03/09/102871.html</link><dc:creator>SIMONE</dc:creator><author>SIMONE</author><pubDate>Fri, 09 Mar 2007 08:55:00 GMT</pubDate><guid>http://www.blogjava.net/wangxinsh55/archive/2007/03/09/102871.html</guid><wfw:comment>http://www.blogjava.net/wangxinsh55/comments/102871.html</wfw:comment><comments>http://www.blogjava.net/wangxinsh55/archive/2007/03/09/102871.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wangxinsh55/comments/commentRss/102871.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wangxinsh55/services/trackbacks/102871.html</trackback:ping><description><![CDATA[
		<div>MP(Bitmap-File)图形文件是Windows采用的图形文件格式，在Windows环境下运行的所有图象处理软件都支持BMP图象文件格式。Windows系统内部各图像绘制操作都是以BMP为基础的。Windows 3.0以前的BMP图文件格式与显示设备有关，因此把这种BMP图象文件格式称为设备相关位图DDB(device-dependent bitmap)文件格式。Windows 3.0以后的BMP图象文件与显示设备无关，因此把这种BMP图象文件格式称为设备无关位图DIB(device-independent bitmap)格式（注：Windows 3.0以后，在系统中仍然存在DDB位图，象BitBlt()这种函数就是基于DDB位图的，只不过如果你想将图像以BMP格式保存到磁盘文件中时，微软极力推荐你以DIB格式保存），目的是为了让Windows能够在任何类型的显示设备上显示所存储的图象。BMP位图文件默认的文件扩展名是BMP或者bmp（有时它也会以.DIB或.RLE作扩展名）。 </div>
		<div>6.1.2 文件结构</div>
		<div>位图文件可看成由4个部分组成：位图文件头(bitmap-file header)、位图信息头(bitmap-information header)、彩色表(color table)和定义位图的字节阵列，它具有如下所示的形式。 </div>
		<div>位图文件的组成 <br />结构名称 <br />符号 </div>
		<div>位图文件头(bitmap-file header) BITMAPFILEHEADER bmfh <br />位图信息头(bitmap-information header) BITMAPINFOHEADER bmih <br />彩色表(color table) RGBQUAD aColors[] <br />图象数据阵列字节 BYTE aBitmapBits[] </div>
		<div>位图文件结构可综合在表6-01中。 </div>
		<div>　 </div>
		<div>图象文件 </div>
		<div>头<br />0000h 文件标识 2 bytes 两字节的内容用来识别位图的类型： <br />‘BM’ ： Windows 3.1x, 95, NT, … </div>
		<div>‘BA’ ：OS/2 Bitmap Array </div>
		<div>‘CI’ ：OS/2 Color Icon </div>
		<div>‘CP’ ：OS/2 Color Pointer </div>
		<div>‘IC’ ： OS/2 Icon </div>
		<div>‘PT’ ：OS/2 Pointer</div>
		<div>注：因为OS/2系统并没有被普及开，所以在编程时，你只需判断第一个标识“BM”就行。</div>
		<div>　 0002h File Size 1 dword 用字节表示的整个文件的大小 <br />　 0006h Reserved 1 dword 保留，必须设置为0 <br />　 000Ah Bitmap Data Offset 1 dword 从文件开始到位图数据开始之间的数据(bitmap data)之间的偏移量 <br />　 000Eh Bitmap Header Size 1 dword 位图信息头(Bitmap Info Header)的长度，用来描述位图的颜色、压缩方法等。下面的长度表示： <br />28h - Windows 3.1x, 95, NT, … </div>
		<div>0Ch - OS/2 1.x </div>
		<div>F0h - OS/2 2.x</div>
		<div>注：在Windows95、98、2000等操作系统中，位图信息头的长度并不一定是28h，因为微软已经制定出了新的BMP文件格式，其中的信息头结构变化比较大，长度加长。所以最好不要直接使用常数28h，而是应该从具体的文件中读取这个值。这样才能确保程序的兼容性。</div>
		<div>　 0012h Width 1 dword 位图的宽度，以象素为单位 <br />　 0016h Height 1 dword 位图的高度，以象素为单位 <br />　 001Ah Planes 1 word 位图的位面数（注：该值将总是1） </div>
		<div>图象信息头 <br />　<br />001Ch Bits Per Pixel 1 word 每个象素的位数 <br />1 - 单色位图（实际上可有两种颜色，缺省情况下是黑色和白色。你可以自己定义这两种颜色） </div>
		<div>4 - 16 色位图 </div>
		<div>8 - 256 色位图 </div>
		<div>16 - 16bit 高彩色位图 </div>
		<div>24 - 24bit 真彩色位图 </div>
		<div>32 - 32bit 增强型真彩色位图</div>
		<div>　 001Eh Compression 1 dword 压缩说明： <br />0 - 不压缩 (使用BI_RGB表示) </div>
		<div>1 - RLE 8-使用8位RLE压缩方式(用BI_RLE8表示) </div>
		<div>2 - RLE 4-使用4位RLE压缩方式(用BI_RLE4表示) </div>
		<div>3 - Bitfields-位域存放方式(用BI_BITFIELDS表示)</div>
		<div>　 0022h Bitmap Data Size 1 dword 用字节数表示的位图数据的大小。该数必须是4的倍数 <br />　 0026h HResolution 1 dword 用象素/米表示的水平分辨率 <br />　 002Ah VResolution 1 dword 用象素/米表示的垂直分辨率 <br />　 002Eh Colors 1 dword 位图使用的颜色数。如8-比特/象素表示为100h或者 256. <br />　 0032h Important Colors 1 dword 指定重要的颜色数。当该域的值等于颜色数时（或者等于0时），表示所有颜色都一样重要 <br />调色板数据 根据BMP版本的不同而不同 Palette N * 4 byte 调色板规范。对于调色板中的每个表项，这4个字节用下述方法来描述RGB的值：  1字节用于蓝色分量 <br />1字节用于绿色分量 <br />1字节用于红色分量 <br />1字节用于填充符(设置为0) </div>
		<div>图象数据 根据BMP版本及调色板尺寸的不同而不同 Bitmap Data xxx bytes 该域的大小取决于压缩方法及图像的尺寸和图像的位深度，它包含所有的位图数据字节，这些数据可能是彩色调色板的索引号，也可能是实际的RGB值，这将根据图像信息头中的位深度值来决定。 </div>
		<div> </div>
		<div> </div>
		<div>
				<br />构件详解</div>
		<div>1. 位图文件头 </div>
		<div>位图文件头包含有关于文件类型、文件大小、存放位置等信息，在Windows 3.0以上版本的位图文件中用BITMAPFILEHEADER结构来定义： </div>
		<div>typedef struct tagBITMAPFILEHEADER { /* bmfh */ </div>
		<div>UINT bfType;<br />DWORD bfSize; <br />UINT bfReserved1; <br />UINT bfReserved2; <br />DWORD bfOffBits;</div>
		<div>} BITMAPFILEHEADER; </div>
		<div>其中： <br />  </div>
		<div>bfType<br />说明文件的类型.（该值必需是0x4D42，也就是字符'BM'。我们不需要判断OS/2的位图标识，这么做现在来看似乎已经没有什么意义了，而且如果要支持OS/2的位图，程序将变得很繁琐。所以，在此只建议你检察'BM'标识） </div>
		<div>bfSize<br />说明文件的大小，用字节为单位</div>
		<div>bfReserved1<br />保留，必须设置为0</div>
		<div>bfReserved2<br />保留，必须设置为0</div>
		<div>bfOffBits<br />说明从文件头开始到实际的图象数据之间的字节的偏移量。这个参数是非常有用的，因为位图信息头和调色板的长度会根据不同情况而变化，所以你可以用这个偏移值迅速的从文件中读取到位数据。</div>
		<div>
				<br />2. 位图信息头 </div>
		<div>位图信息用BITMAPINFO结构来定义，它由位图信息头(bitmap-information header)和彩色表(color table)组成，前者用BITMAPINFOHEADER结构定义，后者用RGBQUAD结构定义。BITMAPINFO结构具有如下形式： </div>
		<div>typedef struct tagBITMAPINFO { /* bmi */ </div>
		<div>BITMAPINFOHEADER bmiHeader;<br />RGBQUAD bmiColors[1];</div>
		<div>} BITMAPINFO; </div>
		<div>其中： <br />  </div>
		<div>bmiHeader<br />说明BITMAPINFOHEADER结构，其中包含了有关位图的尺寸及位格式等信息</div>
		<div>bmiColors<br />说明彩色表RGBQUAD结构的阵列，其中包含索引图像的真实RGB值。</div>
		<div>
				<br />BITMAPINFOHEADER结构包含有位图文件的大小、压缩类型和颜色格式，其结构定义为： </div>
		<div>typedef struct tagBITMAPINFOHEADER { /* bmih */ </div>
		<div>DWORD biSize; <br />LONG biWidth; <br />LONG biHeight; <br />WORD biPlanes; <br />WORD biBitCount; <br />DWORD biCompression; <br />DWORD biSizeImage; <br />LONG biXPelsPerMeter; <br />LONG biYPelsPerMeter; <br />DWORD biClrUsed; <br />DWORD biClrImportant;</div>
		<div>} BITMAPINFOHEADER; </div>
		<div>其中： <br />  </div>
		<div>biSize<br />说明BITMAPINFOHEADER结构所需要的字数。注：这个值并不一定是BITMAPINFOHEADER结构的尺寸，它也可能是sizeof(BITMAPV4HEADER)的值，或是sizeof(BITMAPV5HEADER)的值。这要根据该位图文件的格式版本来决定，不过，就现在的情况来看，绝大多数的BMP图像都是BITMAPINFOHEADER结构的（可能是后两者太新的缘故吧:-）。</div>
		<div>biWidth<br />说明图象的宽度，以象素为单位</div>
		<div>biHeight<br />说明图象的高度，以象素为单位。注：这个值除了用于描述图像的高度之外，它还有另一个用处，就是指明该图像是倒向的位图，还是正向的位图。如果该值是一个正数，说明图像是倒向的，如果该值是一个负数，则说明图像是正向的。大多数的BMP文件都是倒向的位图，也就是时，高度值是一个正数。（注：当高度值是一个负数时（正向图像），图像将不能被压缩（也就是说biCompression成员将不能是BI_RLE8或BI_RLE4）。</div>
		<div>biPlanes<br />为目标设备说明位面数，其值将总是被设为1</div>
		<div>biBitCount<br />说明比特数/象素，其值为1、4、8、16、24、或32</div>
		<div>biCompression<br />说明图象数据压缩的类型。其值可以是下述值之一： <br />BI_RGB：没有压缩； </div>
		<div>BI_RLE8：每个象素8比特的RLE压缩编码，压缩格式由2字节组成(重复象素计数和颜色索引)； </div>
		<div>BI_RLE4：每个象素4比特的RLE压缩编码，压缩格式由2字节组成 </div>
		<div>BI_BITFIELDS：每个象素的比特由指定的掩码决定。</div>
		<div>
				<br />biSizeImage<br />说明图象的大小，以字节为单位。当用BI_RGB格式时，可设置为0 <br />biXPelsPerMeter<br />说明水平分辨率，用象素/米表示 <br />biYPelsPerMeter<br />说明垂直分辨率，用象素/米表示 <br />biClrUsed<br />说明位图实际使用的彩色表中的颜色索引数（设为0的话，则说明使用所有调色板项） <br />biClrImportant<br />说明对图象显示有重要影响的颜色索引的数目，如果是0，表示都重要。</div>
		<div>
				<br />现就BITMAPINFOHEADER结构作如下说明： </div>
		<div>(1) 彩色表的定位 </div>
		<div>应用程序可使用存储在biSize成员中的信息来查找在BITMAPINFO结构中的彩色表，如下所示： </div>
		<div>pColor = ((LPSTR) pBitmapInfo + (WORD) (pBitmapInfo-&gt;bmiHeader.biSize)) </div>
		<div>(2) biBitCount </div>
		<div>biBitCount=1 表示位图最多有两种颜色，缺省情况下是黑色和白色，你也可以自己定义这两种颜色。图像信息头装调色板中将有两个调色板项，称为索引0和索引1。图象数据阵列中的每一位表示一个象素。如果一个位是0，显示时就使用索引0的RGB值，如果位是1，则使用索引1的RGB值。 </div>
		<div>biBitCount=4 表示位图最多有16种颜色。每个象素用4位表示，并用这4位作为彩色表的表项来查找该象素的颜色。例如，如果位图中的第一个字节为0x1F，它表示有两个象素，第一象素的颜色就在彩色表的第2表项中查找，而第二个象素的颜色就在彩色表的第16表项中查找。此时，调色板中缺省情况下会有16个RGB项。对应于索引0到索引15。 </div>
		<div>biBitCount=8 表示位图最多有256种颜色。每个象素用8位表示，并用这8位作为彩色表的表项来查找该象素的颜色。例如，如果位图中的第一个字节为0x1F，这个象素的颜色就在彩色表的第32表项中查找。此时，缺省情况下，调色板中会有256个RGB项，对应于索引0到索引255。 </div>
		<div>biBitCount=16 表示位图最多有216种颜色。每个色素用16位（2个字节）表示。这种格式叫作高彩色，或叫增强型16位色，或64K色。它的情况比较复杂，当biCompression成员的值是BI_RGB时，它没有调色板。16位中，最低的5位表示蓝色分量，中间的5位表示绿色分量，高的5位表示红色分量，一共占用了15位，最高的一位保留，设为0。这种格式也被称作555 16位位图。如果biCompression成员的值是BI_BITFIELDS，那么情况就复杂了，首先是原来调色板的位置被三个DWORD变量占据，称为红、绿、蓝掩码。分别用于描述红、绿、蓝分量在16位中所占的位置。在Windows 95（或98）中，系统可接受两种格式的位域：555和565，在555格式下，红、绿、蓝的掩码分别是：0x7C00、0x03E0、0x001F，而在565格式下，它们则分别为：0xF800、0x07E0、0x001F。你在读取一个像素之后，可以分别用掩码“与”上像素值，从而提取出想要的颜色分量（当然还要再经过适当的左右移操作）。在NT系统中，则没有格式限制，只不过要求掩码之间不能有重叠。（注：这种格式的图像使用起来是比较麻烦的，不过因为它的显示效果接近于真彩，而图像数据又比真彩图像小的多，所以，它更多的被用于游戏软件）。 </div>
		<div>biBitCount=24 表示位图最多有224种颜色。这种位图没有调色板（bmiColors成员尺寸为0），在位数组中，每3个字节代表一个象素，分别对应于颜色R、G、B。 </div>
		<div>biBitCount=32 表示位图最多有232种颜色。这种位图的结构与16位位图结构非常类似，当biCompression成员的值是BI_RGB时，它也没有调色板，32位中有24位用于存放RGB值，顺序是：最高位—保留，红8位、绿8位、蓝8位。这种格式也被成为888 32位图。如果 biCompression成员的值是BI_BITFIELDS时，原来调色板的位置将被三个DWORD变量占据，成为红、绿、蓝掩码，分别用于描述红、绿、蓝分量在32位中所占的位置。在Windows 95(or 98)中，系统只接受888格式，也就是说三个掩码的值将只能是：0xFF0000、0xFF00、0xFF。而在NT系统中，你只要注意使掩码之间不产生重叠就行。（注：这种图像格式比较规整，因为它是DWORD对齐的，所以在内存中进行图像处理时可进行汇编级的代码优化（简单））。 </div>
		<div>(3) ClrUsed </div>
		<div>BITMAPINFOHEADER结构中的成员ClrUsed指定实际使用的颜色数目。如果ClrUsed设置成0，位图使用的颜色数目就等于biBitCount成员中的数目。请注意，如果ClrUsed的值不是可用颜色的最大值或不是0，则在编程时应该注意调色板尺寸的计算，比如在4位位图中，调色板的缺省尺寸应该是16＊sizeof(RGBQUAD)，但是，如果ClrUsed的值不是16或者不是0，那么调色板的尺寸就应该是ClrUsed＊sizeof(RGBQUAD)。 </div>
		<div>(4) 图象数据压缩 </div>
		<div>① BI_RLE8：每个象素为8比特的RLE压缩编码，可使用编码方式和绝对方式中的任何一种进行压缩，这两种方式可在同一幅图中的任何地方使用。 </div>
		<div>编码方式：由2个字节组成，第一个字节指定使用相同颜色的象素数目，第二个字节指定使用的颜色索引。此外，这个字节对中的第一个字节可设置为0，联合使用第二个字节的值表示： </div>
		<div>
				<br />第二个字节的值为0：行的结束。 </div>
		<div>第二个字节的值为1：图象结束。 </div>
		<div>第二个字节的值为2：其后的两个字节表示下一个象素从当前开始的水平和垂直位置的偏移量。 </div>
		<div>
				<br />绝对方式：第一个字节设置为0，而第二个字节设置为0x03～0xFF之间的一个值。在这种方式中，第二个字节表示跟在这个字节后面的字节数，每个字节包含单个象素的颜色索引。压缩数据格式需要字边界(word boundary)对齐。下面的例子是用16进制表示的8-位压缩图象数据： </div>
		<div>03 04 05 06 00 03 45 56 67 00 02 78 00 02 05 01 02 78 00 00 09 1E 00 01 <br />这些压缩数据可解释为 ： </div>
		<div>压缩数据  <br />扩展数据 </div>
		<div>03 04 04 04 04  <br />05 06 06 06 06 06 06  <br />00 03 45 56 67 00 45 56 67  <br />02 78 78 78  <br />00 02 05 01 从当前位置右移5个位置后向下移一行 <br />02 78 78 78  <br />00 00 行结束 <br />09 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E  <br />00 01 RLE编码图象结束  </div>
		<div>② BI_RLE4：每个象素为4比特的RLE压缩编码，同样也可使用编码方式和绝对方式中的任何一种进行压缩，这两种方式也可在同一幅图中的任何地方使用。这两种方式是： </div>
		<div>编码方式：由2个字节组成，第一个字节指定象素数目，第二个字节包含两种颜色索引，一个在高4位，另一个在低4位。第一个象素使用高4位的颜色索引，第二个使用低4位的颜色索引，第3个使用高4位的颜色索引，依此类推。 </div>
		<div>绝对方式：这个字节对中的第一个字节设置为0，第二个字节包含有颜色索引数，其后续字节包含有颜色索引，颜色索引存放在该字节的高、低4位中，一个颜色索引对应一个象素。此外，BI_RLE4也同样联合使用第二个字节中的值表示： </div>
		<div>
				<br />第二个字节的值为0：行的结束。 </div>
		<div>第二个字节的值为1：图象结束。 </div>
		<div>第二个字节的值为2：其后的两个字节表示下一个象素从当前开始的水平和垂直位置的偏移量。 </div>
		<div>
				<br />下面的例子是用16进制数表示的4-位压缩图象数据： </div>
		<div>03 04 05 06 00 06 45 56 67 00 04 78 00 02 05 01 04 78 00 00 09 1E 00 01 </div>
		<div>这些压缩数据可解释为 ： </div>
		<div>压缩数据 <br />扩展数据 </div>
		<div>03 04 0 4 0 <br />05 06 0 6 0 6 0  <br />00 06 45 56 67 00 4 5 5 6 6 7  <br />04 78 7 8 7 8  <br />00 02 05 01 从当前位置右移5个位置后向下移一行 <br />04 78 7 8 7 8  <br />00 00 行结束 <br />09 1E 1 E 1 E 1 E 1 E 1  <br />00 01 RLE图象结束  </div>
		<div>3. 彩色表 </div>
		<div>彩色表包含的元素与位图所具有的颜色数相同，象素的颜色用RGBQUAD结构来定义。对于24-位真彩色图象就不使用彩色表（同样也包括16位、和32位位图），因为位图中的RGB值就代表了每个象素的颜色。彩色表中的颜色按颜色的重要性排序，这可以辅助显示驱动程序为不能显示足够多颜色数的显示设备显示彩色图象。RGBQUAD结构描述由R、G、B相对强度组成的颜色，定义如下： </div>
		<div>typedef struct tagRGBQUAD { /* rgbq */ </div>
		<div>BYTE rgbBlue; <br />BYTE rgbGreen; <br />BYTE rgbRed; <br />BYTE rgbReserved;</div>
		<div>} RGBQUAD; </div>
		<div>其中： <br />  </div>
		<div>rgbBlue<br />指定蓝色强度</div>
		<div>rgbGreen<br />指定绿色强度</div>
		<div>rgbRed<br />指定红色强度</div>
		<div>rgbReserved<br />保留，设置为0</div>
		<div>
				<br />4. 位图数据 </div>
		<div>紧跟在彩色表之后的是图象数据字节阵列。图象的每一扫描行由表示图象象素的连续的字节组成，每一行的字节数取决于图象的颜色数目和用象素表示的图象宽度。扫描行是由底向上存储的，这就是说，阵列中的第一个字节表示位图左下角的象素，而最后一个字节表示位图右上角的象素。（只针对与倒向DIB，如果是正向DIB，则扫描行是由顶向下存储的），倒向DIB的原点在图像的左下角，而正向DIB的原点在图像的左上角。同时，每一扫描行的字节数必需是4的整倍数，也就是DWORD对齐的。如果你想确保图像的扫描行DWORD对齐，可使用下面的代码：</div>
		<div>(((width*biBitCount)+31)&gt;&gt;5)&lt;&lt;2</div>
<img src ="http://www.blogjava.net/wangxinsh55/aggbug/102871.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wangxinsh55/" target="_blank">SIMONE</a> 2007-03-09 16:55 <a href="http://www.blogjava.net/wangxinsh55/archive/2007/03/09/102871.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在VC中自建操作BMP位图文件的类</title><link>http://www.blogjava.net/wangxinsh55/archive/2007/03/09/102865.html</link><dc:creator>SIMONE</dc:creator><author>SIMONE</author><pubDate>Fri, 09 Mar 2007 08:32:00 GMT</pubDate><guid>http://www.blogjava.net/wangxinsh55/archive/2007/03/09/102865.html</guid><wfw:comment>http://www.blogjava.net/wangxinsh55/comments/102865.html</wfw:comment><comments>http://www.blogjava.net/wangxinsh55/archive/2007/03/09/102865.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wangxinsh55/comments/commentRss/102865.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wangxinsh55/services/trackbacks/102865.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 在														VC														中自建操作														BMP														位图文件的类																																																														 								...&nbsp;&nbsp;<a href='http://www.blogjava.net/wangxinsh55/archive/2007/03/09/102865.html'>阅读全文</a><img src ="http://www.blogjava.net/wangxinsh55/aggbug/102865.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wangxinsh55/" target="_blank">SIMONE</a> 2007-03-09 16:32 <a href="http://www.blogjava.net/wangxinsh55/archive/2007/03/09/102865.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Visual C++中DDB与DIB位图编程全攻略 </title><link>http://www.blogjava.net/wangxinsh55/archive/2007/03/09/102849.html</link><dc:creator>SIMONE</dc:creator><author>SIMONE</author><pubDate>Fri, 09 Mar 2007 07:58:00 GMT</pubDate><guid>http://www.blogjava.net/wangxinsh55/archive/2007/03/09/102849.html</guid><wfw:comment>http://www.blogjava.net/wangxinsh55/comments/102849.html</wfw:comment><comments>http://www.blogjava.net/wangxinsh55/archive/2007/03/09/102849.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wangxinsh55/comments/commentRss/102849.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wangxinsh55/services/trackbacks/102849.html</trackback:ping><description><![CDATA[
		<div class="postText">
				<p align="center">
						<strong>Visual C++中DDB与DIB位图编程全攻略</strong>
						<br />来源: 天极网</p>
				<p align="left">
						<strong>1. 基本概念<br /><br /></strong>先来用通俗的语句讲解位图和调色板的概念。<br />我们知道，自然界中的所有颜色都可以由红、绿、蓝(R，G，B)三基色组合而成。针对含有红、绿、蓝色成分的多少，可以对其分别分成0～255个等级，而红、绿、蓝的不同组合共有256×256×256种，因此约能表示1600万种颜色。对于人眼而言，这已经是"真彩色"了。<br /><br />对每个像素进行了（R，G，B）量化的图像就是位图，其在计算机中对应文件的扩展名一般为.bmp。既然用R，G，B的量化值就可以直接记录一张位图的所有像素，那我们需要调色板干什么呢？<br /><br />首先，我们可以计算完全利用（R，G，B）组合来存储一个800×600的位图所需要的空间为：<br /><br />800×600×3 = 1440000（字节）＝ 1.37M（字节）<br /><br />惊人的大！因此，调色板横空出世了，它的功能在于缓解位图文件存储空间过大的问题。<br /><br />假设一个位图为16色，其像素总数为800×600。我们只需要用4个bit就可以存储这个位图的每个像素在16种颜色中所处的等级，然后调色板提供了这16种等级对应的（R，G，B）值，这样，存储这个16色位图只需要：<br /><br />800×600×4/8 = 240000（字节）= 0.22 M（字节）<br /><br />额外的存储R，G，B表的开销（即调色板Palette，也称为颜色查找表LUT）仅仅为16×3＝48字节。<br /><br />存储空间被大为减少！<br /><br />常见的位图有单色、16色、256色、16位及24位真彩色5种，对于前三者（即不大于256色）都可以调色板方式进行存储，而对16位及24位真彩色以调色板进行存储是不划算的，它们直接按照R，G，B分量进行存储。<br /><br />在此基础上我们来分析DDB位图（Device-dependent bitmap，与设备相关的位图）与DIB位图（Device-independent bitmap，与设备无关的位图）的概念以及二者的区别。<br /><br />DDB依赖于具体设备，它只能存在于内存中（视频内存或系统内存），其颜色模式必须与特定的输出设备相一致，使用系统调色板。一般只能载入色彩较简单的DDB位图，对于颜色较丰富的位图，需使用DIB才能长期保存。<br /><br />DIB不依赖于具体设备，可以用来永久性地保存图象。DIB一般是以*.BMP文件的形式保存在磁盘中的，有时也会保存在*.DIB文件中。 DIB位图的特点是将颜色信息储存在位图文件自身的颜色表中，应用程序要根据此颜色表为DIB创建逻辑调色板。因此，在输出一幅DIB位图之前，程序应该将其逻辑调色板选入到相关的设备上下文并实现到系统调色板中。<br /><br /><strong>2. 例程简述<br /></strong><br />本文后续的讲解都基于这样的一个例子工程，它是一个基于对话框的MFC应用程序，包括2个父菜单：<br /><br />（1） DDB位图<br /><br />DDB位图父菜单又包括两个子菜单：<br /><br />a. ID：IDM_LOADDDBPIC caption：加载<br /><br />单击事件：加载资源中的DDB位图并显示之<br /><br />b. ID：IDM_MARK_DDBPIC caption：标记<br /><br />单击事件：在DIB位图中透明地添加天极网logo<br /><br />（2） DIB位图<br /><br />DIB位图父菜单又包括两个子菜单：<br /><br />a. ID：IDM_OPENDIBPIC caption：打开<br /><br />单击事件：弹出文件对话框，打开.bmp位图文件，并显示<br /><br />b. ID：IDM_MARK_DIBPIC caption：标记<br /><br />单击事件：在DIB位图中透明地添加天极网logo<br /><br />工程中还包含下列位图资源：<br /><br />（1）IDB_LOADED_BITMAP：要加载的位图资源<br /><br />（2）IDB_YESKY_BITMAP：天极网logo<br /><br />后续篇章将集中在对4个子菜单单击事件消息处理函数的讲解，下面的代码是整个对话框类CBitMapExampleDlg的消息映射：<br /><br /><code>BEGIN_MESSAGE_MAP(CBitMapExampleDlg, CDialog)<br />//{{AFX_MSG_MAP(CBitMapExampleDlg)<br />ON_WM_SYSCOMMAND()<br />ON_WM_PAINT()<br />ON_WM_QUERYDRAGICON()<br />ON_COMMAND(IDM_LOADDDBPIC, OnLoadddbpic)<br />ON_COMMAND(IDM_MARK_DDBPIC, OnMarkDdbpic)<br />ON_COMMAND(IDM_OPENDIBPIC, OnOpendibpic)<br />ON_COMMAND(IDM_MARK_DIBPIC,OnMarkDibpic) //}}AFX_MSG_MAP<br />END_MESSAGE_MAP()</code></p>
				<p> </p>
				<p>
						<strong>3. DDB位图编程<br /><br /></strong>先看DDB加载按钮的单击事件代码：<br /><br /><code>void CBitMapExampleDlg::OnLoadddbpic() <br />{<br />1:　CBitmap bmpDraw;<br />2: bmpDraw.LoadBitmap( IDB_LOADED_BITMAP );//装入要加载的DDB位图 <br />3: BITMAP bmpInfo;<br />4: bmpDraw.GetBitmap( &amp;bmpInfo ); //获取要加载DDB位图的尺寸 <br />5: CDC memDC;//定义一个兼容DC<br />6: CClientDC dc( this );<br />7: memDC.CreateCompatibleDC( &amp;dc );//创建兼容DC<br />8: CBitmap* pbmpOld = memDC.SelectObject( &amp;bmpDraw );//保存原有DDB，并选入新DDB入DC<br /><br />9: dc.BitBlt( 0, 0, bmpInfo.bmWidth, bmpInfo.bmHeight, &amp;memDC, 0, 0, SRCCOPY );<br /><br />10:　memDC.SelectObject( pbmpOld );//选入原DDB<br />}</code><br />上述代码将产生如图1所示的效果，位图被安置在对话框（0,0）坐标开始的位置上。</p>
				<p>
						<img alt="" src="http://cimg.163.com/catchpic/F/F8/F8F94958CE8F80443F1BDF1E43BFEA87.jpg" border="0" />
				</p>
				<p>图1 加载DDB位图资源</p>
				<p> </p>
				<p>
						<br />我们来逐行解析上述代码是怎样产生图1的效果的。<br /><br />第1、2行定义了一个CBitmap对象，并调用其成员函数LoadBitmap加载工程中的位图资源IDB_LOADED_BITMAP。第3、4行定义了BITMAP结构体的实例并调用CBitmap的成员函数GetBitmap获得位图信息，BITMAP结构体定义在<wingdi.h></wingdi.h>头文件中，其形式为：<br /><br /><code>/* Bitmap Header Definition */<br />typedef struct tagBITMAP<br />{<br />LONG bmType; //必需为0<br />LONG bmWidth; //位图的宽度(以像素为单位)<br />LONG bmHeight; //位图的高度(以像素为单位)<br />LONG bmWidthBytes; //每一扫描行所需的字节数，应是偶数<br />WORD bmPlanes; //色平面数<br />WORD bmBitsPixel; //色平面的颜色位数<br />LPVOID bmBits; //指向存储像素阵列的数组<br />} BITMAP, *PBITMAP, NEAR *NPBITMAP, FAR *LPBITMAP;</code><br />第5~8行的作用是：构建一个CDC对象，调用CDC::CreateCompatibleDC创建一个兼容的内存设备上下文，接着调用CDC::SelectObject将DDB选入内存设备上下文中。<br /><br />第9行调用函数CDC::BitBlt绘制位图，CDC::BitBlt的原型为：<br /><br /><code>CDC::BitBlt(int x, int y, int nWidth, int nHeight, CDC *pSrcDC, int xSrc, int ySrc, DWORD dwRop)</code><br />CDC::BitBlt执行的操作为将源DC中位图复制到目的DC中。其中前四个参数为目的区域的坐标（x,y）及长度和宽度（Width, nHeight），第五个参数是源DC指针，接下来的参数是源DC中的起始坐标，最后一个参数为光栅操作的类型。<br /><br />第10行调用CDC::SelectObject把原来的DDB选入到内存设备上下文中并使新DDB脱离出来。<br /><br />与CDC::BitBlt对应的还有另一个函数CDC::StretchBlt，它具有缩放功能，其原型为：<br /><br /><code>BOOL CDC::StretchBlt(int x, int y, int nWidth, int nHeight, CDC *pSrcDC, int<br />xSrc, int ySrc, int nSrcWidth, int nSrcHeight, DWORD dwRop);</code><br />该函数把位图从源矩形拷贝到目的矩形中，如果源和目的矩形尺寸不同，那么将缩放位图的功能以适应目的矩形的大小。函数的大部分参数与BitBlt的相同，但多了两个参数nSrcWidth和nSrcHeight用来指定源矩形的宽和高。<br /><br />如果我们将函数CBitMapExampleDlg::OnLoadddbpic() 中的第9行改为：<br /><br /><code>CRect clientRect;<br />GetClientRect(&amp;clientRect); //获得对话框窗口的大小<br />dc.StretchBlt(0, 0, clientRect.right, clientRect.bottom, &amp;memDC, 0, 0,<br />bmpInfo.bmWidth, bmpInfo.bmHeight, SRCCOPY);</code><br />则单击加载按钮后的对话框如图2所示，位图被拉伸至整个对话框的范围。<br /></p>
				<p>
						<br />
				</p>
				<p align="center">
						<img alt="" src="http://cimg.163.com/catchpic/4/4F/4F91CC4A105C69E77279A52F4FB2A9EE.jpg" border="0" />
				</p>
				<p align="center">
						<br />图2 拉伸位图<br /></p>
				<p>CDC::BitBlt和dc.StretchBlt函数中的dwRop参数较为有用，它定义光栅操作的类型。请看"DDB位图"父菜单下"标记"子菜单单击事件的消息处理函数代码：<br /><br /><code>void CBitMapExampleDlg::OnMarkDdbpic()<br />{<br />CBitmap bmpDraw;<br />bmpDraw.LoadBitmap(IDB_YESKY_BITMAP); //装入天极网logo DDB位图资源<br />BITMAP bmpInfo;<br />bmpDraw.GetBitmap(&amp;bmpInfo); //获取天极网logo位图的尺寸 <br /><br />CDC memDC; //定义一个兼容DC<br />CClientDC dc(this);<br />memDC.CreateCompatibleDC(&amp;dc); //创建DC<br /><br />CBitmap *pbmpOld = memDC.SelectObject(&amp;bmpDraw);<br />//保存原有DDB，并选入天极网logo位图入DC<br />dc.BitBlt(0, 0, bmpInfo.bmWidth, bmpInfo.bmHeight, &amp;memDC, 0, 0, SRCAND);<br />memDC.SelectObject(pbmpOld); //选入原DDB <br />}</code><br />单击该按钮后，将产生如图3的效果，天极网的logo被透明地添加到了位图中！<br /></p>
				<p>
						<br />
				</p>
				<p align="center">
						<img alt="" src="http://cimg.163.com/catchpic/A/AE/AE56DF89FB6B969070E26137BC6C5041.jpg" border="0" />
				</p>
				<p align="center">
						<br />图3 在DDB位图中加入天极网logo<br /></p>
				<p>能产生这个效果的原因在于我们在代码行：<br /><br /><code>dc.BitBlt ( 0, 0, bmpInfo.bmWidth, bmpInfo.bmHeight, &amp;memDC, 0, 0, SRCAND );</code></p>
				<p>
						<code>中使用了参数SRCAND（不同于先前代码中SRCCOPY，它仅仅意味着复制源位图到目的位图），它的含义为源和目的间进行AND操作。我们不知道天极网的编辑同志是怎么为文章中的图片加logo的，有可能他们就使用了具有自动AND功能的图像加logo批处理软件。的确，我们可以利用例程中的原理写一个批处理软件，一次对一堆图片自动添加logo。<br /><br />参数dwRop除了可以为SRCAND和SRCCOPY外，还可以有如下取值：<br /><br />BLACKNESS：输出区域为黑色<br /><br />DSTINVERT：反转目的位图 <br /><br />MERGECOPY：用与操作把图案(Pattern)与源位图融合起来 <br /><br />MERGEPAINT：用或操作把反转的源位图与目的位图融合起来 <br /><br />NOTSRCCOPY：把源位图反转然后拷贝到目的地 <br /><br />NOTSRCERASE：用或操作融合源和目的位图，然后再反转 <br /><br />PATCOPY：把图案拷贝到目的位图中 <br /><br />PATINVERT：用异或操作把图案与目的位图相融合 <br /><br />PATPAINT：用或操作融合图案和反转的源位图，然后用或操作把结果与目的位图融合 <br /><br />SRCERASE：先反转目的位图，再用与操作将其与源位图融合 <br /><br />SRCINVERT：用异或操作融合源位图和目的位图 <br /><br />SRCPAINT：用或操作融合源位图和目的位图 <br /><br />WHITENESS：输出区域为白色<br /><br />合理利用这些取值将帮助我们制作出特定要求的图像处理软件。<br /><br />从上述实例我们可以看出，在VC中使用CBitmap类，必须将位图放入工程的资源中，并使用类 CBitmap的成员函数LoadBitmap加载之，再通过CDC类的成员函数BitBlt进行DC拷贝等操作达到显示的目的。CBitmap有显示的不足：<br /><br />（1） 位图需要放入工程资源中，这将导致工程的可执行文件变大；<br /><br />（2） 因为位图需放入工程资源中，而资源中不能无穷无尽地包含位图，应用程序无法自适应地选取其它位图，能使用的位图十分有限的；<br /><br />（3） 类CBitmap只是DDB位图操作API的封装，不能独立于平台。<br /><br />DIB位图则可以解决上述问题，其特点是以.BMP位图文件格式存储独立于平台的图像数据，下面我们来详细分析。<br /><strong>4. DIB位图编程<br /><br /></strong>4.1位图文件格式<br /><br />先来分析DIB位图文件的格式。位图文件分为四部分： <br /><br />（1）位图文件头BITMAPFILEHEADER<br /><br />位图文件头BITMAPFILEHEADER是一个结构体，长度为14字节，定义为：<br /><br /><code>typedef struct tagBITMAPFILEHEADER<br />{<br />WORD bfType; //文件类型，必须是0x424D，即字符串"BM"<br />DWORD bfSize; //文件大小，包括BITMAPFILEHEADER的14个字节<br />WORD bfReserved1; //保留字<br />WORD bfReserved2; //保留字<br />DWORD bfOffBits; //从文件头到实际的位图数据的偏移字节数<br />} BITMAPFILEHEADER;</code><br />（2）位图信息头BITMAPINFOHEADER<br /><br />位图信息头BITMAPINFOHEADER也是一个结构体，长度为40字节，定义为：<br /><br /><code>typedef struct tagBITMAPINFOHEADER<br />{<br />DWORD biSize; //本结构的长度，为40<br />LONG biWidth; //图象的宽度，单位是象素<br />LONG biHeight; //图象的高度，单位是象素<br />WORD biPlanes; //必须是1<br />WORD biBitCount;<br />//表示颜色时要用到的位数，1(单色), 4(16色), 8(256色), 24(真彩色)<br />DWORD biCompression;<br />//指定位图是否压缩，有效的值为BI_RGB，BI_RLE8，BI_RLE4，BI_BITFIELDS等，BI_RGB表示不压缩<br />DWORD biSizeImage;<br />//实际的位图数据占用的字节数，即 biSizeImage=biWidth’ × biHeight，biWidth’是biWidth 按照4的整倍数调整后的结果 <br />LONG biXPelsPerMeter; //目标设备的水平分辨率，单位是每米的象素个数<br />LONG biYPelsPerMeter; //目标设备的垂直分辨率，单位是每米的象素个数<br />DWORD biClrUsed; //位图实际用到的颜色数，0表示颜色数为2biBitCount<br />DWORD biClrImportant; //位图中重要的颜色数，0表示所有颜色都重要<br />} BITMAPINFOHEADER;</code><br />（3）调色板Palette<br /><br />调色板Palette针对的是需要调色板的位图，即单色、16色和256色位图。对于不以调色板方式存储的位图，则无此项信息。调色板是一个数组，共有biClrUsed个元素(如果该值为0，则有2biBitCount个元素)。数组中每个元素是一个RGBQUAD结构体，长度为4个字节，定义为：<br /><br /><code>typedef struct tagRGBQUAD<br />{<br />BYTE rgbBlue; //蓝色分量<br />BYTE rgbGreen; //绿色分量<br />BYTE rgbRed; //红色分量<br />BYTE rgbReserved; //保留值<br />} RGBQUAD;</code></code>
				</p>
				<p>
						<code>
								<code>（4）实际的位图数据ImageDate<br /><br />对于用到调色板的位图，实际的图象数据ImageDate为该象素的颜色在调色板中的索引值；对于真彩色图，图象数据则为实际的R、G、B值：<br /><br />a.单色位图：用1bit就可以表示象素的颜色索引值；<br /><br />b.16色位图：用4bit可以表示象素的颜色索引值；<br /><br />c. 256色位图：1个字节表示1个象素的颜色索引值； <br /><br />d.真彩色：3个字节表示1个象素的颜色R，G，B值。<br /><br />此外，位图数据每一行的字节数必须为4的整倍数，如果不是，则需要补齐。奇怪的是，位图文件中的数据是从下到上（而不是从上到下）、从左到右方式存储的。<br />4.2位图的显示<br /><br />Visual C++ MFC中没有提供一个专门的类来处理DIB位图，因此，为了方便地使用位图文件，我们有必要派生一个CDib类。类的源代码如下：<br /><br />(1) CDib类的声明<br /><br /><code>// DIB.h：类CDib声明头文件<br />#ifndef __DIB_H__<br />#define __DIB_H__<br />#include <wingdi.h></wingdi.h><br />class CDib<br />{<br />public:<br />CDib();<br />~CDib();<br /><br />BOOL Load( const char * );<br />BOOL Save( const char * );<br />BOOL Draw( CDC *, int nX = 0, int nY = 0, int nWidth = -1, int nHeight = -1, int mode = SRCCOPY);<br />BOOL SetPalette( CDC * );<br /><br />private:<br />CPalette m_Palette;<br />unsigned char *m_pDib, *m_pDibBits;<br />DWORD m_dwDibSize;<br />BITMAPINFOHEADER *m_pBIH;<br />RGBQUAD *m_pPalette;<br />int m_nPaletteEntries;<br />};<br />#endif<br /></code><br />(2) CDib类的实现<br /><br /><code>// DIB.cpp：类CDib实现文件<br />#include "stdafx.h"<br />#include "DIB.h"<br /><br />CDib::CDib()<br />{<br />m_pDib = NULL;<br />}<br /><br />CDib::~CDib()<br />{<br />// 如果位图已经被加载，释放内存<br />if (m_pDib != NULL)<br />delete []m_pDib;<br />}</code><br />下面这个函数非常重要，其功能为加载位图，类似于CBitmap类的LoadBitmap函数：<br /><br /><code>BOOL CDib::Load(const char *pszFilename)<br />{<br />CFile cf;<br /><br />// 打开位图文件<br />if (!cf.Open(pszFilename, CFile::modeRead))<br />return (FALSE);<br /><br />// 获得位图文件大小，并减去BITMAPFILEHEADER的长度<br />DWORD dwDibSize;<br />dwDibSize = cf.GetLength() - sizeof(BITMAPFILEHEADER);<br /><br />// 为DIB位图分配内存<br />unsigned char *pDib;<br />pDib = new unsigned char[dwDibSize];<br />if (pDib == NULL)<br />return (FALSE);<br /><br />BITMAPFILEHEADER BFH;<br /><br />// 读取位图文件数据<br />try<br />{<br />// 文件格式是否正确有效<br />if ( cf.Read(&amp;BFH, sizeof(BITMAPFILEHEADER)) != sizeof(BITMAPFILEHEADER) ||<br />BFH.bfType != ’MB’ || cf.Read(pDib, dwDibSize) != dwDibSize)<br />{<br />delete []pDib;<br />return (FALSE);<br />}<br />}<br />catch (CFileException *e)<br />{<br />e-&gt;Delete();<br />delete []pDib;<br />return (FALSE);<br />}<br /><br />// delete先前加载的位图<br />if (m_pDib != NULL) <br />delete m_pDib;<br /><br />// 将临时Dib数据指针和Dib大小变量赋给类成员变量<br />m_pDib = pDib;<br />m_dwDibSize = dwDibSize;<br /><br />// 为相应类成员变量赋BITMAPINFOHEADER和调色板指针<br />m_pBIH = (BITMAPINFOHEADER*)m_pDib;<br />m_pPalette = (RGBQUAD*) &amp;m_pDib[sizeof(BITMAPINFOHEADER)];<br /><br />// 计算调色板中实际颜色数量<br />m_nPaletteEntries = 1 &lt;&lt; m_pBIH-&gt;biBitCount;<br />if (m_pBIH-&gt;biBitCount &gt;8)<br />m_nPaletteEntries = 0;<br />else if (m_pBIH-&gt;biClrUsed != 0)<br />m_nPaletteEntries = m_pBIH-&gt;biClrUsed;<br /><br />// 为相应类成员变量赋image data指针<br />m_pDibBits = &amp;m_pDib[sizeof(BITMAPINFOHEADER) + m_nPaletteEntries * sizeof (RGBQUAD)];<br /><br />// delete先前的调色板<br />if (m_Palette.GetSafeHandle() != NULL)<br />m_Palette.DeleteObject();<br /><br />// 如果位图中存在调色板，创建LOGPALETTE 及CPalette<br />if (m_nPaletteEntries != 0)<br />{<br />LOGPALETTE *pLogPal = (LOGPALETTE*)new char[sizeof(LOGPALETTE) + m_nPaletteEntries *sizeof(PALETTEENTRY)];<br /><br />if (pLogPal != NULL)<br />{<br />pLogPal-&gt;palVersion = 0x300;<br />pLogPal-&gt;palNumEntries = m_nPaletteEntries;<br /><br />for (int i = 0; i &lt; m_nPaletteEntries; i++)<br />{<br />pLogPal-&gt;palPalEntry[i].peRed = m_pPalette[i].rgbRed;<br />pLogPal-&gt;palPalEntry[i].peGreen = m_pPalette[i].rgbGreen;<br />pLogPal-&gt;palPalEntry[i].peBlue = m_pPalette[i].rgbBlue;<br />}<br /><br />//创建CPalette并释放LOGPALETTE的内存<br />m_Palette.CreatePalette(pLogPal);<br />delete []pLogPal;<br />}<br />}<br /><br />return (TRUE);<br />}<br /><br />//函数功能：保存位图入BMP文件<br />BOOL CDib::Save(const char *pszFilename)<br />{<br />if (m_pDib == NULL)<br />return (FALSE);<br /><br />CFile cf;<br />if (!cf.Open(pszFilename, CFile::modeCreate | CFile::modeWrite))<br />return (FALSE);<br /><br />try<br />{<br />BITMAPFILEHEADER BFH;<br />memset(&amp;BFH, 0, sizeof(BITMAPFILEHEADER));<br />BFH.bfType = ’MB’;<br />BFH.bfSize = sizeof(BITMAPFILEHEADER) + m_dwDibSize;<br />BFH.bfOffBits = sizeof(BITMAPFILEHEADER) + <br />sizeof(BITMAPINFOHEADER) + m_nPaletteEntries *sizeof(RGBQUAD);<br /><br />cf.Write(&amp;BFH, sizeof(BITMAPFILEHEADER));<br />cf.Write(m_pDib, m_dwDibSize);<br />}<br />catch (CFileException *e)<br />{<br />e-&gt;Delete();<br />return (FALSE);<br />}<br />return (TRUE);<br />}</code><br />下面这个函数也非常重要，其功能为在pDC指向的CDC中绘制位图，起点坐标为(nX,nY)，绘制宽度和高度为nWidth、nHeight，最后一个参数是光栅模式：<br /><br /><code>BOOL CDib::Draw(CDC *pDC, int nX, int nY, int nWidth, int nHeight, int mode)<br />{<br />if (m_pDib == NULL)<br />return (FALSE);<br /><br />// 获取位图宽度和高度赋值<br />if (nWidth == - 1)<br />nWidth = m_pBIH-&gt;biWidth;<br />if (nHeight == - 1)<br />nHeight = m_pBIH-&gt;biHeight;<br /><br />// 绘制位图<br />StretchDIBits(pDC-&gt;m_hDC, nX, nY, nWidth, nHeight, 0, 0, m_pBIH-&gt;biWidth, m_pBIH-&gt;biHeight, m_pDibBits, (BITMAPINFO*)m_pBIH, BI_RGB, mode);<br /><br />return (TRUE);<br />}<br /><br />//函数功能：设置调色板<br />BOOL CDib::SetPalette(CDC *pDC)<br />{<br />if (m_pDib == NULL)<br />return (FALSE);<br /><br />// 检查当前是否有一个调色板句柄，对于大于256色的位图，为NULL<br />if (m_Palette.GetSafeHandle() == NULL)<br />return (TRUE);<br /><br />// 选择调色板，接着实施之，最后恢复老的调色板<br />CPalette *pOldPalette;<br />pOldPalette = pDC-&gt;SelectPalette(&amp;m_Palette, FALSE);<br />pDC-&gt;RealizePalette();<br />pDC-&gt;SelectPalette(pOldPalette, FALSE);<br /><br />return (TRUE);<br />}</code><br />从整个CDib类的代码中我们可以看出，DIB位图的显示需遵循如下步骤：<br /><br />（1）读取位图，本类中使用pDib = new unsigned char[dwDibSize]为位图中的信息分配内存，另一种方法是调用API函数CreateDIBSection，譬如：<br /><br /><code>m_hBitmap = ::CreateDIBSection(pDC-&gt;GetSafeHdc(), <br />(LPBITMAPINFO) m_lpBMPHdr, DIB_RGB_COLORS,<br />(LPVOID*) &amp;m_lpDIBits, NULL, 0);</code><br />m_hBitmap定义为：<br /><br /><code>HBITMAP m_hBitmap;</code><br />（2）根据读取的位图信息，计算出调色板大小，然后创建调色板；<br /><br />（3）调用CDib::SetPalette( CDC *pDC )设置调色板，需要用到CDC::SelectPalette及CDC::RealizePalette两个函数；<br /><br />（4）调用CDib::Draw(CDC *pDC, int nX, int nY, int nWidth, int nHeight, int mode)函数绘制位图。在此函数中，真正发挥显示位图作用的是对StretchDIBits API函数的调用。StretchDIBits函数具有缩放功能，其最后一个参数也是光栅操作的模式。<br /><br />下面给出DIB位图的打开及显示并在其中加入天极网logo的函数源代码。"DIB位图"父菜单下"打开"子菜单的单击事件消息处理函数为（其功能为打开位图并显示之）： <br /><br /><code>void CBitMapExampleDlg::OnOpendibpic()<br />{<br />// 弹出文件对话框，让用户选择位图文件<br />CFileDialog fileDialog(TRUE, "*.BMP", NULL, NULL,"位图文件(*.BMP)|*.bmp;*.BMP|");<br />if (IDOK == fileDialog.DoModal())<br />{<br />// 加载位图并显示之<br />CDib dib;<br />if (dib.Load(fileDialog.GetPathName()))<br />{<br />CClientDC dc(this);<br />dib.SetPalette(&amp;dc);<br />dib.Draw(&amp;dc);<br />}<br />}<br />}</code><br />"DIB位图"父菜单下"标记"子菜单的单击事件消息处理函数为（其功能为给位图加上天极网logo）：<br /><br /><code>void CBitMapExampleDlg::OnMarkDibpic()<br />{<br />// 弹出文件对话框，让用户选择标记logo<br />CFileDialog fileDialog(TRUE, "*.BMP", NULL, NULL, "标记位图文件(*.BMP)|*.bmp;*.BMP|");<br />if (IDOK == fileDialog.DoModal())<br />{<br />// 加载标记logo位图并与目标位图相与<br />CDib dib;<br />if (dib.Load(fileDialog.GetPathName()))<br />{<br />CClientDC dc(this);<br />dib.SetPalette(&amp;dc);<br />dib.Draw(&amp;dc, 0, 0, - 1, - 1, SRCAND);<br />}<br />}<br />}</code><br />图4显示了DIB位图加载天极网logo后的效果，要好于图3中加天极网logo后的DDB位图。图4显示的是真彩色位图相互与的结果，而图3中的图像颜色被减少了。<br /><br /></code>
						</code>
				</p>
				<p align="center">
						<img alt="" src="http://cimg.163.com/catchpic/E/E6/E683A3F6D12F0BFB25E9FC01B42FAC52.jpg" border="0" />
				</p>
				<p align="center">
						<br />图4 在DIB位图中加入天极网logo<br /></p>
				<strong>
				</strong>
				<p>
						<strong>5. 结束语<br /></strong>
						<br />本文介绍了位图及调色板的概念，并讲解了DDB位图与DIB位图的区别。在此基础上，本文以实例讲解了DDB位图和DIB位图的操作方式。DDB位图的处理相对比较简单，对于DIB位图，我们需要定义一个MFC所没有的新类CDib，它屏蔽位图信息的读取及调色板创建的技术细节，应用程序可以方便地使用之。<br /><br />本文中的所有程序在Visual C++6.0及Windows XP平台上调试通过。 </p>
				<br />
				<br />
				<p id="TBPingURL">Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1518334</p>
				<br />
		</div>
<img src ="http://www.blogjava.net/wangxinsh55/aggbug/102849.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wangxinsh55/" target="_blank">SIMONE</a> 2007-03-09 15:58 <a href="http://www.blogjava.net/wangxinsh55/archive/2007/03/09/102849.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>基于internet的ActiveX 控件开发使用方法</title><link>http://www.blogjava.net/wangxinsh55/archive/2007/03/08/102643.html</link><dc:creator>SIMONE</dc:creator><author>SIMONE</author><pubDate>Thu, 08 Mar 2007 09:26:00 GMT</pubDate><guid>http://www.blogjava.net/wangxinsh55/archive/2007/03/08/102643.html</guid><wfw:comment>http://www.blogjava.net/wangxinsh55/comments/102643.html</wfw:comment><comments>http://www.blogjava.net/wangxinsh55/archive/2007/03/08/102643.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wangxinsh55/comments/commentRss/102643.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wangxinsh55/services/trackbacks/102643.html</trackback:ping><description><![CDATA[    ActiveX控件是一个自包含代码的组件，它有自己的永久状态，并且ActiveX控件与包容器之间通过COM接口进行通信，所以通常ActiveX控件具有普遍的适应性，换句话说，如果ActiveX控件在一个包容器程序中可以运行，那么通常它也可以运行在另一个包容器程序中。由于ActiveX控件是一种可独立发布的组件程序，又是一个永久对象，这些特性使得它非常适合于Internet环境。
<div style="TEXT-INDENT: 21pt">从包容器IE而言。IE需要考虑一些与Internet环境有关的问题，如许可证检查，自动下载、远程数据获取等。</div><div style="TEXT-INDENT: 21pt">另一方面从ActiveX控件本身来看，用于桌面环境的ActiveX控件，为了获得更强的交互性能，通常它要实现各种ActiveX控件所要求的接口，所以组件规模通常很大。为了使程序代码量尽可能地小，Microsoft Visual C++提供了ATL模板库作为这类ActiveX控件的基本开发工具。</div><div style="TEXT-INDENT: 21pt">IE要求ActiveX控件必须支持自注册，以便在用户浏览包含ActiveX控件的WEB页面时可以动态地注册控件。所以作为ActiveX控件的DLL程序必须引出DllRegisterServer和DllUnregisterServer函数。</div><p></p><table style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; MARGIN-LEFT: 6.75pt; BORDER-LEFT: medium none; WIDTH: 432pt; MARGIN-RIGHT: 6.75pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse" cellspacing="0" cellpadding="0" width="576" align="left" border="1"><tbody><tr style="HEIGHT: 24pt"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 135pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 24pt" valign="top" width="180"><div>接口</div></td><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 297pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 24pt" valign="top" width="396"><div>功能与说明</div></td></tr><tr style="HEIGHT: 24pt"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 135pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 24pt" valign="top" width="180"><div>IOleObject</div></td><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 297pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 24pt" valign="top" width="396"><div>如果控件要与包容器程序的站点对象进行通信，则须实现此接口</div></td></tr><tr style="HEIGHT: 24pt"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 135pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 24pt" valign="top" width="180"><div>IOleInPlaceObject</div><div>IOleInPlaceActiveObject</div></td><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 297pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 24pt" valign="top" width="396"><div>如果控件支持实地激活特性，则须实现此接口</div></td></tr><tr style="HEIGHT: 24pt"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 135pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 24pt" valign="top" width="180"><div>IOleControl</div></td><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 297pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 24pt" valign="top" width="396"><div>如果控件支持快捷键，或者访问包容器的环境属性，或者控件要求包容器处理事件，则须实现此接口。</div></td></tr><tr style="HEIGHT: 24pt"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 135pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 24pt" valign="top" width="180"><div>IDataObject</div></td><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 297pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 24pt" valign="top" width="396"><div>如果控件提供数据对象的特性，则实现此接口</div></td></tr><tr style="HEIGHT: 24pt"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 135pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 24pt" valign="top" width="180"><div>IViewObject2</div></td><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 297pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 24pt" valign="top" width="396"><div>如果控件在非实地激活状态下也需要显示信息，则实现此接口</div></td></tr><tr style="HEIGHT: 24pt"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 135pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 24pt" valign="top" width="180"><div>IDispatch</div></td><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 297pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 24pt" valign="top" width="396"><div>如果控件有自定义的属性和方法，则实现此接口</div></td></tr><tr style="HEIGHT: 24pt"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 135pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 24pt" valign="top" width="180"><div>IConnectionPointContainer</div></td><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 297pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 24pt" valign="top" width="396"><div>如果控件对象支持一个或多个出接口，则实现此接口。</div></td></tr><tr style="HEIGHT: 24pt"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 135pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 24pt" valign="top" width="180"><div>IProvideClassInfo[2]</div></td><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 297pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 24pt" valign="top" width="396"><div>如果控件对象要直接通过GetClassInfo成员函数提供对象类型信息，则实现此接口</div></td></tr><tr style="HEIGHT: 24pt"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 135pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 24pt" valign="top" width="180"><div>ISpecifyPropertyPages</div></td><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 297pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 24pt" valign="top" width="396"><div>如果控件对象支持属性页，则实现此接口</div></td></tr><tr style="HEIGHT: 24pt"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 135pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 24pt" valign="top" width="180"><div>IPersistStream[Init]</div><div>IPersisStorage</div><div>或其它永久接口</div></td><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 297pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 24pt" valign="top" width="396"><div>如果控件对象支持永久特性的，则至少实现一个永久接口</div></td></tr></tbody></table><div style="TEXT-INDENT: 21pt">从理论上讲，IE只要求被嵌入的对象(ActiveX控件以对象的形式被嵌入在页面中)实现IUnKnown接口，但实际上为了实现一定的功能，并更好地与IE程序进行交互，ActiveX控件还必须实现其它一些接口。对应的接口与功能对应关系如下：</div><div style="TEXT-INDENT: 21pt">一般的包容器程序通常只负责ActiveX控件的创建、运行和释放，但IE要做的事情更多一些。首先，当客户机上不存在ActiveX控件组件程序时，它必须根据HTML页面中指示的地点把组件程序下载过来，并注册在客户机上，然后再创建对象，此过程在后台自动完成，不需要执行其它命令。其次，当IE把组件程序下载到本地之后，它在创建控件对象时可以进行许可证检查，以保证系统安全性，用户也可拒绝对象或执行初始化。</div><div>ActiveX控件包装</div><div style="TEXT-INDENT: 21.75pt">通过CLSID指定ActiveX控件的类型，如果要在IE中正确显示包含ActiveX控件的页面，则必须事先在机器上注册相应的ActiveX控件，否则相应的ActiveX就不能显示。在Internet上使用时，页面设计者不能期望浏览器用户找到你指定的ActiveX控件并注册到客户机上，为了解次这个问题，我们可以用“codebase”属性指定控件的代码位置：</div><div style="TEXT-INDENT: 21.75pt">&lt;OBJECT CLASSID=”CLSID：2885EE05-A26B-11d1-B49B-00c04F98EFE0”</div><div style="TEXT-INDENT: 21.75pt"><span>         Codebase=</span>“<a href="http://webserver/Pollgon.dll">http://webserver/Pollgon.dll</a>”</div><div><span>     ALIGN=”CENTER” WIDTH=200 HEIGHT=200 ID=”PolyCtl”&gt; &lt;/OBJECT&gt;</span></div><div>以上指定了Polygong控件程序“<a href="http://webserver/Pollgon.dll">http://webserver/Pollgon.dll</a>”，当浏览器碰到这样的描述时，它会把codebase指定的程序下载到本地(在系统目录的“Downloaded Program Files”子目录下)，然后调用组件程序的自注册入口函数注册到当前系统中，以后的创建都在本地进行，不再涉及codebase属性。</div><div> </div><div>因为ActiveX控件的程序代码需要在Internet上传输，所以使用压缩技术传输程序代码非常有意义。另一方面，如果ActiveX控件还调用到其它的DLL程序模块，那么IE也必须把这些程序下载到本地来，为此，Microsoft采用了惯用的CAB压缩方法，它把ActiveX控件程序以及相关的其它文件放到同一个CAB文件中，然后在codebase属性中指定CAB文件的URL路径。</div><div> </div><div><strong><span style="COLOR: blue">IE</span></strong><strong><span style="COLOR: blue">对</span><span style="COLOR: blue">CAB</span></strong><strong><span style="COLOR: blue">文件的处理过程如下：</span></strong></div><div style="MARGIN-LEFT: 18pt; TEXT-INDENT: -18pt"><span>1． </span>IE在解析“OBJECT”标记时，它继续查找codebase属性。</div><div style="MARGIN-LEFT: 18pt; TEXT-INDENT: -18pt"><span>2． </span>如果找到了codebase属性，并且codebase指定了ActiveX控件的CAB文件，那么IE定位到CAB文件。</div><div style="MARGIN-LEFT: 18pt; TEXT-INDENT: -18pt"><span>3． </span>IE把CAB文件中的有关文件解压出来，并放到客户机的临时目录(系统目录的“Downloaded Program Files”子目录下)中。</div><div style="MARGIN-LEFT: 18pt; TEXT-INDENT: -18pt"><span>4． </span>IE注册有关的文件。</div><div style="MARGIN-LEFT: 18pt; TEXT-INDENT: -18pt"><span>5． </span>IE调用COM API函数创建ActiveX控件对象。</div><div><span style="COLOR: blue">CAB</span><span style="COLOR: blue">文件包含了</span><span style="COLOR: blue">ActiveX</span><span style="COLOR: blue">控件注册和运行时所需要的必要信息。通常一个</span><span style="COLOR: blue">CAB</span><span style="COLOR: blue">文件包含一个</span><span style="COLOR: blue">INF</span><span style="COLOR: blue">文件，</span><span style="COLOR: blue">INF</span><span style="COLOR: blue">文件是一个文本文件，它描述了</span><span style="COLOR: blue">CAB</span><span style="COLOR: blue">文件的所有细节信息。如以下的</span><span style="COLOR: blue">INF</span><span style="COLOR: blue">文件例子。</span></div><div><span style="COLOR: blue">[version]</span></div><div style="TEXT-INDENT: 21.75pt"><span style="COLOR: blue">Signature=”$CHICAGE$”</span></div><div style="TEXT-INDENT: 21.75pt"><span style="COLOR: blue">AdvancedINF=2.0</span></div><div><span style="COLOR: blue">[Add.Code]</span></div><div style="TEXT-INDENT: 21.75pt"><span style="COLOR: blue">MyCtrl.ocx = MyCtrl.ocx</span></div><div style="TEXT-INDENT: 21.75pt"><span style="COLOR: blue">Msvcrt.dll = msvcrt.dll</span></div><div style="TEXT-INDENT: 21.75pt"><span style="COLOR: blue">Mfc42.dll = mfc42.dll</span></div><div style="TEXT-INDENT: 21.75pt"><span style="COLOR: blue">Olepro32.dll = olepro32.dll</span></div><div><span style="COLOR: blue">[MyCtrl.ocx]</span></div><div style="TEXT-INDENT: 21.75pt"><span style="COLOR: blue">File-win32-x86=thiscab</span></div><div style="TEXT-INDENT: 21.75pt"><span style="COLOR: blue">Clsid = {2885EE05-A26B-11d1-B49B-00c04F98EFE0}</span></div><div style="TEXT-INDENT: 21.75pt"><span style="COLOR: blue">FileVersion = 1,0,0,0</span></div><div style="TEXT-INDENT: 21.75pt"><span style="COLOR: blue">RegisterServer = yes</span></div><div style="TEXT-INDENT: 21.75pt"> </div><div><span style="COLOR: blue">[msvcrt.dll]</span></div><div style="TEXT-INDENT: 21.75pt"><span style="COLOR: blue">FileVersion = 4,20,0,6164</span></div><div style="TEXT-INDENT: 21.75pt"><span style="COLOR: blue">Hook = mfc42installer</span></div><div><span style="COLOR: blue">[mfc42.dll]</span></div><div style="TEXT-INDENT: 21.75pt"><span style="COLOR: blue">FileVersion=4,2,0,6256</span></div><div style="TEXT-INDENT: 21.75pt"><span style="COLOR: blue">Hook=mfc42installer</span></div><div><span style="COLOR: blue">[olepro32.dll]</span></div><div style="TEXT-INDENT: 21.75pt"><span style="COLOR: blue">FileVersion = 4,2,0,6068</span></div><div style="TEXT-INDENT: 21.75pt"><span style="COLOR: blue">Hook = mfc42installer</span></div><div><span style="COLOR: blue">[mfc42installer]</span></div><div style="TEXT-INDENT: 21.75pt"><span style="COLOR: blue">File-win32-x86=http://activex.microsoft.com/controls/vc/mfc42.cab</span></div><div style="TEXT-INDENT: 21.75pt"><span style="COLOR: blue">Run = %EXTRACT_DIR% \mfc42.exe</span></div><div> </div><div>[Version]给出了INF文件的基本版本信息，“signature=”一行说明此INF文件可适用于32位</div><div style="MARGIN-LEFT: 36.75pt">Windows操作系统； “AdvancedINF=”一行要求IE必须装入2.0版以上的Advpack.dll模块才能解析此INF文件。</div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt">[Add.Code]是INF文件的主体部分，它列出了所有ActiveX控件需要的文件以及每个文件所对应的部分名称。MyCtrl控件是MFC开发的，所以它需要用到MFC的动态连接库文件，所以，这一部分中列出了MyCtrl控件所必须的四个文件：MyCtrl.ocx、msvcrt.dll、mfc42.dll和olepro32.dll。</div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt">[MyCtrl.ocx]部分描述了MyCtrl控件组件程序MyCtrl.ocx的信息，包括此程序所在的CAB文件位置(thiscab指自身)以及MyCtrl控件的CLSID信息。”FileVersion=”指明了MyCtrl.ocx的版本；”RegisterServer=yes”说明此文件在使用之前要预先被注册。</div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt">[msvcrt.dll] [mfc42.dll] [olepro32.dll]部分分别说明了相应的文件版本，并把进一步信息指向最后的[mfc42installer]部分。</div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt">[mfc42installer]部分中，“file-win32-x86=”指明了MFC42.CAB文件的URL路径，”run=”指明了运行的命令</div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"> </div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span style="COLOR: blue">可用</span><span style="COLOR: blue">Cabarc.exe</span><span style="COLOR: blue">工具生成</span><span style="COLOR: blue">CAB</span><span style="COLOR: blue">文件，如：</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span style="COLOR: red">Cabarc.exe N MyCtrl.cab MyCtrl.ocx MyCtrl.inf</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"> </div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span style="COLOR: blue">用</span><span style="COLOR: blue">ATL</span><span style="COLOR: blue">产生的</span><span style="COLOR: blue">Activex</span><span style="COLOR: blue">控件所对应的</span><span style="COLOR: blue">INF</span><span style="COLOR: blue">文件如下：</span><span style="COLOR: blue">(</span><span style="COLOR: blue">可作为</span><span style="COLOR: blue">ATL</span><span style="COLOR: blue">开发的样板</span><span style="COLOR: blue">)</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span style="COLOR: blue">[version]</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span style="COLOR: blue">    Signature=”$CHICAGE$”</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span style="COLOR: blue">    AdvancedINF = 2.0</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span style="COLOR: blue">[Add.Code]</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span style="COLOR: blue">    Polygon.dll = polygon.dll</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span style="COLOR: blue">    Atl.dll = atl.dll</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span style="COLOR: blue">[atl.dll]</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span style="COLOR: blue">    File-win32-x86=thiscab</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span style="COLOR: blue">    FileVersion = 3,00,0,8166</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span style="COLOR: blue">    DestDir = 11</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span style="COLOR: blue">    RegisterServer=yes</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"> </div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt">[Polygon.dll]</div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span>    File-win32-x86=thiscab</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span>    Clsid = {2885EE05-A26B-11d1-B49B-00c04F98EFE0}</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span>    FileVersion=1,0,0,1</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span>    RegisterServer=yes</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"> </div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt">这里新出现的标记为“DestDir=”，它是指文件下载过来之后存放的目标目录，“11”表示</div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt">Windows的系统目录(“Windows\System” 或 “WInnt\System32”)；“10”指windows目录</div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt">(windows或winnt)。根据INF文件生成的CAB文件命令为：</div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt">Cabarc.exe N Polygon.cab  atl.dll Polygon.dll Polygon.inf</div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"> </div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt">CAB文件也支持数字签名，我们在利用cabarc.exe工具生成CAB文件时可以用 –s命令行</div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt">参数预留数签名空间，然后运行实用工具<span style="COLOR: red">SIGNCODE</span>，加入数字签名即可。</div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"> </div><div style="MARGIN-LEFT: 36.9pt; TEXT-INDENT: -36.9pt"><strong><span style="COLOR: blue">许可证管理</span></strong></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span style="COLOR: blue">    </span><span style="COLOR: black">除了免费发行的</span><span style="COLOR: black">ActiveX</span><span style="COLOR: black">之外，大多数</span><span style="COLOR: black">ActiveX</span><span style="COLOR: black">控件都支持<strong>设计时刻</strong>和<strong>运行时刻</strong>的许可</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span style="COLOR: black">证检查。设计时刻许可证检查可以保证程序员在创建就用系统或者</span><span style="COLOR: black">WEB</span><span style="COLOR: black">页面时使用合法的</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span style="COLOR: black">ActiveX</span><span style="COLOR: black">控件；运行时刻许可证检查可以保证用户运行一个包含合法的控件的就用系统或者</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span style="COLOR: black">显示一个包含合法控件的</span><span style="COLOR: black">Web</span><span style="COLOR: black">页面。</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span style="COLOR: black">    </span><span style="COLOR: black">由于许可证检查是在</span><span style="COLOR: black">ActiveX</span><span style="COLOR: black">控件被创建时进行的，所以不管是设计时刻许可检查还是</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span style="COLOR: black">运行时刻许可检查，实现的关键在于</span><span style="COLOR: black">ActiveX</span><span style="COLOR: black">控件的类厂对象。如果</span><span style="COLOR: black">ActiveX</span><span style="COLOR: black">控件支持许可</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span style="COLOR: black">检查，那么其类厂对象必须支持</span><span style="COLOR: black">IClassFactory2</span><span style="COLOR: black">接口。此接口是</span><span style="COLOR: black">IClassFactory</span><span style="COLOR: black">的扩展。</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span style="COLOR: black">    </span><span style="COLOR: black">许可证是一段文本信息，它可以存放在单独的文件中，也可以存放在组件程序二进制代</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span style="COLOR: black">码中或者就用程序中。但作为</span><span style="COLOR: black">ActiveX</span><span style="COLOR: black">控件组件程序，它只需实现</span><span style="COLOR: black">IClassFactory2</span><span style="COLOR: black">接口的三</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span style="COLOR: black">个成员函数，对于许可证的管理和用法取决于包容器程序。不同类型的包容器程序对许可证</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span style="COLOR: black">的处理有所不同，并且设计时刻与运行时刻对许可证的处理也有所不同。</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -15.75pt"><span style="COLOR: black">在创建包含</span><span style="COLOR: black">ActiveX</span><span style="COLOR: black">控件的应用程序时，包容器</span><span style="COLOR: black">(</span><span style="COLOR: black">如</span><span style="COLOR: black">VB</span><span style="COLOR: black">或其它开发工具</span><span style="COLOR: black">)</span><span style="COLOR: black">调用</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span style="COLOR: black">IClassFactory2::GetLicInfo</span><span style="COLOR: black">和</span><span style="COLOR: black">RequestLicKey</span><span style="COLOR: black">函数获取许可证，然后嵌入到应用程序的二进制</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span style="COLOR: black">代码中。在运行应用程序时刻，作为包容器程序的应用程序在创建</span><span style="COLOR: black">ActiveX</span><span style="COLOR: black">控件时，调用</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span style="COLOR: black">IClassFactory2::CreateInstanceLic</span><span style="COLOR: black">函数创建控件对象，与设计时刻不同的是，它在</span><span style="COLOR: black">bstrKey</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -15.75pt"><span style="COLOR: black">参数中传递有效的许可证字符串</span><span style="COLOR: black">(</span><span style="COLOR: black">内嵌在程序中</span><span style="COLOR: black">)</span><span style="COLOR: black">而不是</span><span style="COLOR: black">NULL</span><span style="COLOR: black">。所以这样的程序可以</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span style="COLOR: black">在任何一机器上运行，而不用担心许可问题</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span style="COLOR: black">    </span><span style="COLOR: black">对于</span><span style="COLOR: black">IE</span><span style="COLOR: black">这样的包容器程序，它也调用</span><span style="COLOR: black">IClassFactory2</span><span style="COLOR: black">的成员函数进行许可检查，但由于</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span style="COLOR: black">ActiveX</span><span style="COLOR: black">控件被包含在</span><span style="COLOR: black">Web</span><span style="COLOR: black">页面上，任何浏览器都可以访问到</span><span style="COLOR: black">Web</span><span style="COLOR: black">页面，并且</span><span style="COLOR: black">IE</span><span style="COLOR: black">在创建控</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span style="COLOR: black">件之前已经把程序代码下载到本地，所以</span><span style="COLOR: black">IE</span><span style="COLOR: black">需要其它的方式把许可证隐藏起来，以避免许</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span style="COLOR: black">可证被非法滥用。</span><span style="COLOR: black">Microsoft</span><span style="COLOR: black">为此引进了许可证包文件</span><span style="COLOR: black">(license package file</span><span style="COLOR: black">，文件名后缀为</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span style="COLOR: black">LPK)</span><span style="COLOR: black">。</span></div><div style="MARGIN-LEFT: 36.75pt; TEXT-INDENT: -36.75pt"><span style="COLOR: black">    </span><span style="COLOR: black">许可证文件格式共分四部分：</span></div><div style="MARGIN-LEFT: 18pt; TEXT-INDENT: -18pt"><span style="COLOR: black">1． </span><span style="COLOR: black">LPK</span><span style="COLOR: black">文件头。包含信息“</span><span style="COLOR: black">LPK License Package</span><span style="COLOR: black">”。</span></div><div style="MARGIN-LEFT: 18pt; TEXT-INDENT: -18pt"><span style="COLOR: black">2． </span><span style="COLOR: black">版权信息。对版权的解释以及对非法拷贝的警告。</span></div><div style="MARGIN-LEFT: 18pt; TEXT-INDENT: -18pt"><span style="COLOR: black">3． </span><span style="COLOR: black">LPK</span><span style="COLOR: black">文件的版本和</span><span style="COLOR: black">GUID</span><span style="COLOR: black">。文本信息，也标识了真正许可证数据的开始。</span></div><div style="MARGIN-LEFT: 18pt; TEXT-INDENT: -18pt"><span style="COLOR: black">4． </span><span style="COLOR: black">用</span><span style="COLOR: black">UUENCODED(Base64)</span><span style="COLOR: black">码对许可证进行编码。包含多个对象</span><span style="COLOR: black">(CLSID)</span><span style="COLOR: black">与相应的许可证</span><span style="COLOR: black">(UNICODE</span><span style="COLOR: black">字符串</span><span style="COLOR: black">)</span><span style="COLOR: black">。</span></div><div><span style="COLOR: black">IE</span><span style="COLOR: black">包含一个许可证管理器组件，它负责解析</span><span style="COLOR: black">LPK</span><span style="COLOR: black">文件，并提取出每个</span><span style="COLOR: black">CLSID</span><span style="COLOR: black">的许可证。当</span></div><div><span style="COLOR: black">IE</span><span style="COLOR: black">要显示</span><span style="COLOR: black">ActiveX</span><span style="COLOR: black">控件时，它通过许可证管理器组件从</span><span style="COLOR: black">LPK</span><span style="COLOR: black">文件提取出许可证，然后调用</span><span style="COLOR: black">IClassFactory2::CreateInstanceLic</span><span style="COLOR: black">函数创建</span><span style="COLOR: black">ActiveX</span><span style="COLOR: black">控件对象，如果它指定的许可与</span><span style="COLOR: black">ActiveX</span><span style="COLOR: black">控件内部的许可证相匹配，那么</span><span style="COLOR: black">ActiveX</span><span style="COLOR: black">控件就会被创建并在页面上显示。</span></div><div><span style="COLOR: black">创建许可证包文件可以使用</span><span style="COLOR: black">Microsoft</span><span style="COLOR: black">提供的实用工具</span><span style="COLOR: black">LPK_TOOL.EXE</span><span style="COLOR: black">。为所指定的</span><span style="COLOR: black">ActiveX</span><span style="COLOR: black">控件生成相应的</span><span style="COLOR: black">LPK</span><span style="COLOR: black">文件。一旦生成了</span><span style="COLOR: black">LPK</span><span style="COLOR: black">文件，并可在</span><span style="COLOR: black">WEB</span><span style="COLOR: black">页中使用，如：</span></div><div><span style="COLOR: black">&lt;OBJECT CLASSID=”clsid:</span> 2885EE05-A26B-11d1-B49B-00c04F98EFE0”&gt;</div><div>&lt;PARAM NAME=”LPKPATH” VALUE=”MyPage.lpk”&gt;</div><div>&lt;/OBJECT&gt;</div><div>LPK文件针对整个WEB页面，而不是单个ActiveX控件，所以每个WEB页面只能使用一个LPK文件。</div><div> </div><div><span style="COLOR: blue">WEB</span><span style="COLOR: blue">页面中</span><span style="COLOR: blue">ActiveX</span><span style="COLOR: blue">控件的初始化</span></div><div style="TEXT-INDENT: 21pt"><span style="COLOR: black">在“</span><span style="COLOR: black">OBJECT</span><span style="COLOR: black">”标记的“</span><span style="COLOR: black">DATA</span><span style="COLOR: black">”属性中指定包含属性数据文件的</span><span style="COLOR: black">URL</span><span style="COLOR: black">文件名。如果</span><span style="COLOR: black">DATA</span><span style="COLOR: black">属性包含</span><span style="COLOR: black">URL</span><span style="COLOR: black">文件名，那么包容器就创建一个</span><span style="COLOR: black">URL</span><span style="COLOR: black">名字对象，并且调用</span><span style="COLOR: black">ActiveX</span><span style="COLOR: black">控件</span><span style="COLOR: black">IPersistMoniker</span><span style="COLOR: black">接口的</span><span style="COLOR: black">Load</span><span style="COLOR: black">成员函数执行初始化。</span><span style="COLOR: black">ActiveX</span><span style="COLOR: black">控件调用名字对象的</span><span style="COLOR: black">IMoniker::BindToStorage</span><span style="COLOR: black">函数以便获取属性数据并进行初始化。</span></div><div style="TEXT-INDENT: 21.75pt"><span style="COLOR: black">如果</span><span style="COLOR: black">ActiveX</span><span style="COLOR: black">不支持</span><span style="COLOR: black">IPersistMoniker</span><span style="COLOR: black">接口，则</span><span style="COLOR: black">IE</span><span style="COLOR: black">会请求其它的永久接口，然后获取属性数据，必要时打包到流对象中，再调用永久接口的</span><span style="COLOR: black">LOAD</span><span style="COLOR: black">成员函数。于是</span><span style="COLOR: black">ActiveX</span><span style="COLOR: black">控件就可通过流对象得到属性数据。</span></div><div style="TEXT-INDENT: 21.75pt"><span style="COLOR: black">以下</span><span style="COLOR: black">HTML</span><span style="COLOR: black">说明在</span><span style="COLOR: black">Web</span><span style="COLOR: black">页面中为</span><span style="COLOR: black">ActiveX</span><span style="COLOR: black">控件指定属性数据的方法</span></div><div style="TEXT-INDENT: 21.75pt"><span style="COLOR: black">&lt;OBJECT CLASSID=”</span> 2885EE05-A26B-11d1-B49B-00c04F98EFE0”</div><div style="TEXT-INDENT: 21.75pt"><span>         CODEBASE=”<a href="http://webserver/MyCtrl.cab">http://webServer/MyCtrl.cab</a>”</span></div><div style="TEXT-INDENT: 21.75pt"><span>         Data=”<a href="http://webserver/Mydata.dat">http://webServer/Mydata.dat</a>”</span></div><div style="TEXT-INDENT: 21.75pt"><span>         ID=”MyCtrl”&gt;&lt;/OBJECT&gt;</span></div><div style="TEXT-INDENT: 21pt">有时候ActiveX控件的属性数据量很小，可以只用一些数值或字符串就把属性表达出来。这种情况下，使用流方式的属性表达式就没有必要，可以在HTML中使用“</div><div>PARAM”关键字对ActiveX控件的属性进行赋值。如:</div><div style="TEXT-INDENT: 21.75pt"><span style="COLOR: black">&lt;OBJECT CLASSID=”</span> 2885EE05-A26B-11d1-B49B-00c04F98EFE0”</div><div style="TEXT-INDENT: 21.75pt"><span>         CODEBASE=”<a href="http://webserver/MyCtrl">http://webServer/MyCtrl</a>.dll”&gt;</span></div><div style="TEXT-INDENT: 21.75pt"><span>         &lt;PARAM NAME = “Sides” VALUE = 5&gt;</span></div><div style="TEXT-INDENT: 21.75pt">&lt;/OBJECT&gt;</div><div> </div><img src ="http://www.blogjava.net/wangxinsh55/aggbug/102643.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wangxinsh55/" target="_blank">SIMONE</a> 2007-03-08 17:26 <a href="http://www.blogjava.net/wangxinsh55/archive/2007/03/08/102643.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>外挂开发中的封包技术</title><link>http://www.blogjava.net/wangxinsh55/archive/2007/03/08/102641.html</link><dc:creator>SIMONE</dc:creator><author>SIMONE</author><pubDate>Thu, 08 Mar 2007 09:23:00 GMT</pubDate><guid>http://www.blogjava.net/wangxinsh55/archive/2007/03/08/102641.html</guid><wfw:comment>http://www.blogjava.net/wangxinsh55/comments/102641.html</wfw:comment><comments>http://www.blogjava.net/wangxinsh55/archive/2007/03/08/102641.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wangxinsh55/comments/commentRss/102641.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wangxinsh55/services/trackbacks/102641.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 通过对动作模拟技术的介绍，我们对游戏外挂有了一定程度上的认识，也学会了使用动作模拟技术来实现简单的动作模拟型游戏外挂的制作。这种动作模拟型游戏外挂有一定的局限性，它仅仅只能解决使用计算机代替人力完成那么有规律、繁琐而无聊的游戏动作。但是，随着网络游戏的盛行和复杂度的增加，很多游戏要求将客户端动作信息及时反馈回服务器，通过服务器对这些动作信息进行有效认证后，再向客户端发送下一步游戏动作信息，这样动作...&nbsp;&nbsp;<a href='http://www.blogjava.net/wangxinsh55/archive/2007/03/08/102641.html'>阅读全文</a><img src ="http://www.blogjava.net/wangxinsh55/aggbug/102641.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wangxinsh55/" target="_blank">SIMONE</a> 2007-03-08 17:23 <a href="http://www.blogjava.net/wangxinsh55/archive/2007/03/08/102641.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转: __cdecl __fastcall与 __stdcall</title><link>http://www.blogjava.net/wangxinsh55/archive/2007/03/08/102592.html</link><dc:creator>SIMONE</dc:creator><author>SIMONE</author><pubDate>Thu, 08 Mar 2007 06:34:00 GMT</pubDate><guid>http://www.blogjava.net/wangxinsh55/archive/2007/03/08/102592.html</guid><wfw:comment>http://www.blogjava.net/wangxinsh55/comments/102592.html</wfw:comment><comments>http://www.blogjava.net/wangxinsh55/archive/2007/03/08/102592.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wangxinsh55/comments/commentRss/102592.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wangxinsh55/services/trackbacks/102592.html</trackback:ping><description><![CDATA[__cdecl __fastcall与 __stdcall <br /><br />调用约定： <br />__cdecl __fastcall与 __stdcall，三者都是调用约定(Calling convention)，它决定以下内容：1)函数参数的压栈顺序，2)由调用者还是被调用者把参数弹出栈，3)以及产生函数修饰名的方法。 <br /><br />1、__stdcall调用约定：函数的参数自右向左通过栈传递，被调用的函数在返回前清理传送参数的内存栈， <br /><br />2、_cdecl是C和C＋＋程序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码，所以产生的可执行文件大小会比调用_stdcall函数的大。函数采用从右到左的压栈方式。注意：对于可变参数的成员函数，始终使用__cdecl的转换方式。 <br /><br />3、__fastcall调用约定：它是通过寄存器来传送参数的（实际上，它用ECX和EDX传送前两个双字（DWORD）或更小的参数，剩下的参数仍旧自右向左压栈传送，被调用的函数在返回前清理传送参数的内存栈）。 <br /><br />4、thiscall仅仅应用于"C++"成员函数。this指针存放于CX寄存器，参数从右到左压。thiscall不是关键词，因此不能被程序员指定。 <br /><br />5、 naked call采用1-4的调用约定时，如果必要的话，进入函数时编译器会产生代码来保存ESI，EDI，EBX，EBP寄存器，退出函数时则产生代码恢复这些 寄存器的内容。naked call不产生这样的代码。naked call不是类型修饰符，故必须和_declspec共同使用。 <br /><br />调用约定可以通过工程设置：Setting...\C/C++ \Code Generation项进行选择，缺省状态为__cdecl。 <br /><br />名字修饰约定： <br /><br />1、修饰名(Decoration name)："C"或者"C++"函数在内部（编译和链接）通过修饰名识别 <br />2、C编译时函数名修饰约定规则： <br />__stdcall调用约定在输出函数名前加上一个下划线前缀，后面加上一个"@"符号和其参数的字节数，格式为_functionname@number,例如 ：function(int a, int b)，其修饰名为：_function@8 <br />__cdecl调用约定仅在输出函数名前加上一个下划线前缀，格式为_functionname。 <br />__fastcall调用约定在输出函数名前加上一个"@"符号，后面也是一个"@"符号和其参数的字节数，格式为@functionname@number。 <br /><br />3、C++编译时函数名修饰约定规则： <br />__stdcall调用约定： <br />1)、以"?"标识函数名的开始，后跟函数名； <br />2)、函数名后面以"@@YG"标识参数表的开始，后跟参数表； <br />3)、参数表以代号表示： <br />X--void ， <br />D--char， <br />E--unsigned char， <br />F--short， <br />H--int， <br />I--unsigned int， <br />J--long， <br />K--unsigned long， <br />M--float， <br />N--double， <br />_N--bool， <br />PA--表示指针，后面的代号表明指针类型，如果相同类型的指针连续出现，以"0"代替，一个"0"代表一次重复； <br />4)、参数表的第一项为该函数的返回值类型，其后依次为参数的数据类型,指针标识在其所指数据类型前； <br />5)、参数表后以"@Z"标识整个名字的结束，如果该函数无参数，则以"Z"标识结束。 <br />其格式为"?functionname@@YG*****@Z"或"?functionname@@YG*XZ"，例如 <br />int Test1(char *var1,unsigned long)-----“?Test1@@YGHPADK@Z” <br />void Test2() -----“?Test2@@YGXXZ” <br /><br />__cdecl调用约定： <br />规则同上面的_stdcall调用约定，只是参数表的开始标识由上面的"@@YG"变为"@@YA"。 <br />__fastcall调用约定： <br />规则同上面的_stdcall调用约定，只是参数表的开始标识由上面的"@@YG"变为"@@YI"。 <br />VC++对函数的省缺声明是"__cedcl",将只能被C/C++调用. <br /><br /><br />注意： <br />1、_beginthread需要__cdecl的线程函数地址，_beginthreadex和CreateThread需要__stdcall的线程函数地址。 <br /><br />2、一般WIN32的函数都是__stdcall。而且在Windef.h中有如下的定义： <br />#define CALLBACK __stdcall <br />#define WINAPI　 __stdcall <br /><br />3、extern "C" _declspec(dllexport) int __cdecl Add(int a, int b); <br />typedef int (__cdecl*FunPointer)(int a, int b); <br />修饰符的书写顺序如上。 <br /><br />4、 extern "C"的作用：如果Add(int a, int b)是在c语言编译器编译，而在c++文件使用，则需要在c++文件中声明：extern "C" Add(int a, int b)，因为c编译器和c++编译器对函数名的解释不一样（c++编译器解释函数名的时候要考虑函数参数，这样是了方便函数重载，而在c语言中不存在函数重 载的问题），使用extern "C"，实质就是告诉c++编译器，该函数是c库里面的函数。如果不使用extern "C"则会出现链接错误。 <br />一般象如下使用： <br />#ifdef _cplusplus <br />#define EXTERN_C extern "C" <br />#else <br />#define EXTERN_C extern <br />#endif <br /><br />#ifdef _cplusplus <br />extern "C"{ <br />#endif <br />EXTERN_C int func(int a, int b); <br />#ifdef _cplusplus <br />} <br />#endif <br /><br />5、MFC提供了一些宏，可以使用AFX_EXT_CLASS来代替__declspec(DLLexport)，并修饰类名，从而导出类，AFX_API_EXPORT来修饰函数，AFX_DATA_EXPORT来修饰变量 <br />AFX_CLASS_IMPORT：__declspec(DLLexport) <br />AFX_API_IMPORT：__declspec(DLLexport) <br />AFX_DATA_IMPORT：__declspec(DLLexport) <br />AFX_CLASS_EXPORT：__declspec(DLLexport) <br />AFX_API_EXPORT：__declspec(DLLexport) <br />AFX_DATA_EXPORT：__declspec(DLLexport) <br />AFX_EXT_CLASS：#ifdef _AFXEXT <br />AFX_CLASS_EXPORT <br />#else <br />AFX_CLASS_IMPORT <br /><br />6、 DLLMain负责初始化(Initialization)和结束(Termination)工作，每当一个新的进程或者该进程的新的线程访问DLL时， 或者访问DLL的每一个进程或者线程不再使用DLL或者结束时，都会调用DLLMain。但是，使用TerminateProcess或 TerminateThread结束进程或者线程，不会调用DLLMain。 <br /><br />7、一个DLL在内存中只有一个实例 <br />DLL程序和调用其输出函数的程序的关系： <br />1)、DLL与进程、线程之间的关系 <br />DLL模块被映射到调用它的进程的虚拟地址空间。 <br />DLL使用的内存从调用进程的虚拟地址空间分配，只能被该进程的线程所访问。 <br />DLL的句柄可以被调用进程使用；调用进程的句柄可以被DLL使用。 <br />DLLDLL可以有自己的数据段，但没有自己的堆栈，使用调用进程的栈，与调用它的应用程序相同的堆栈模式。 <br /><br />2)、关于共享数据段 <br />DLL 定义的全局变量可以被调用进程访问；DLL可以访问调用进程的全局数据。使用同一DLL的每一个进程都有自己的DLL全局变量实例。如果多个线程并发访问 同一变量，则需要使用同步机制；对一个DLL的变量，如果希望每个使用DLL的线程都有自己的值，则应该使用线程局部存储(TLS，Thread Local Strorage)。   <br /><br /><p id="TBPingURL">Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1427956</p><img src ="http://www.blogjava.net/wangxinsh55/aggbug/102592.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wangxinsh55/" target="_blank">SIMONE</a> 2007-03-08 14:34 <a href="http://www.blogjava.net/wangxinsh55/archive/2007/03/08/102592.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>游戏外挂开发资料帖http://hack.gameres.com/showthread.asp?threadid=63098</title><link>http://www.blogjava.net/wangxinsh55/archive/2007/03/05/101917.html</link><dc:creator>SIMONE</dc:creator><author>SIMONE</author><pubDate>Mon, 05 Mar 2007 06:52:00 GMT</pubDate><guid>http://www.blogjava.net/wangxinsh55/archive/2007/03/05/101917.html</guid><wfw:comment>http://www.blogjava.net/wangxinsh55/comments/101917.html</wfw:comment><comments>http://www.blogjava.net/wangxinsh55/archive/2007/03/05/101917.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wangxinsh55/comments/commentRss/101917.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wangxinsh55/services/trackbacks/101917.html</trackback:ping><description><![CDATA[ <a href="http://hack.gameres.com/showthread.asp?threadid=63098">http://hack.gameres.com/showthread.asp?threadid=63098</a><br />本人实在晚上无聊,决定搜索相关的学习资料.工具等提供给大家学习. 
<p></p><p><br /></p><table cellspacing="1" cellpadding="4" width="80%" align="center"><tbody><tr><td class="code"><pre><br />2007.1.31更新<br />1.<a href="http://hack.gameres.com/showthread.asp?threadid=71224" target="_blank"><font color="#9c0000">IO模拟(VB)</font></a><br /><font color="#cc6600">--------------------------------------------------------------------</font><br />2006.9.3更新<br />1.<a href="http://chendi.51.net/creation/xiyou/WDF%20De-Encoder.rar" target="_blank"><font color="#9c0000">大话西游II资源工具</font></a><br />2.<a href="http://chendi.51.net/creation/xiyou/XY2TOOL_SRC.rar" target="_blank"><font color="#9c0000">天下无D外挂源代码</font></a><br />3.<a href="http://www.77169.com/Soft/HTML/26109.html" target="_blank"><font color="#9c0000">大陆天堂2木马源代码</font></a><br />4.<a href="http://www.work009.com/download.asp?id=25" target="_blank"><font color="#9c0000">传奇网吧杀手源代码</font></a><br />5.<a href="http://www.77169.com/Soft/HTML/25940.html" target="_blank"><font color="#9c0000">传奇幽灵源代码</font></a><br />6.<a href="http://www.77169.com/Soft/HTML/25634.html" target="_blank"><font color="#9c0000">热血江湖辅助工具源代码</font></a><br />7.<a href="http://www.77169.com/Soft/HTML/25468.html" target="_blank"><font color="#9c0000">决战自动补血源代码</font></a><br />8.<a href="http://down3.tomore.com/down/datanew/2004112410391913640.rar" target="_blank"><font color="#9c0000">完整的天翼脱机脱机外挂（含控件）源代码</font></a><br />9.<a href="http://down3.tomore.com/down/datanew/2005011117081513866.rar" target="_blank"><font color="#9c0000">千年新版外挂源代码+详细分析文档</font></a><br />10.<a href="http://down3.tomore.com/down/datanew/200406190943474874.rar" target="_blank"><font color="#9c0000">防大补贴传奇3外挂源程序</font></a><br />11.<a href="http://down3.tomore.com/down/datanew/2004062821022022343.exe" target="_blank"><font color="#9c0000">神迹脱机外挂源代码 </font></a><br />12.<a href="http://down3.tomore.com/down/datanew/2004112519590627775.rar" target="_blank"><font color="#9c0000">对对碰外挂源码</font></a><br /><font color="#cc6600">--------------------------------------------------------------------</font><br />2006.9.2更新<br />1.<a href="http://www.easyde.net/recode/UploadFile/2006-6/200662414373874489.rar" target="_blank"><font color="#9c0000">搜神记游戏外挂源代码</font></a><br />2.<a href="http://www.71wg.com/soft/3813.htm" target="_blank"><font color="#9c0000">冒险岛无敌外挂源代码</font></a><br />3.<a href="http://www.71wg.com/soft/3784.htm" target="_blank"><font color="#9c0000">占星奇缘源代码</font></a><br />4.<a href="http://www.71wg.com/soft/3463.htm" target="_blank"><font color="#9c0000">传奇世界SGL文件破解-工具+原代码</font></a><br />5.<a href="http://www.wgqy.com/down/zxqydm.rar" target="_blank"><font color="#9c0000">占星情缘脱机外挂源代码</font></a> <a href="http://www.eqsf.net/Software/Catalog287/5300.html" target="_blank"><font color="#9c0000">下载地址二</font></a> <a href="http://www.71wg.com/soft/3784.htm" target="_blank"><font color="#9c0000">下载地址三</font></a><br /><font color="#cc6600">--------------------------------------------------------------------</font><br />2006.9.1更新<br />1.<a href="http://www.hack58.net/Soft/html/15/30/2006/200603255196.htm" target="_blank"><font color="#9c0000">WPE.Pro0.9F</font></a><br />2.<a href="http://www.dxqsoft.com/download/WSE06b1.zip" target="_blank"><font color="#9c0000">WinSock Expert</font></a><br />3.<a href="http://hack.gameres.com/showthread.asp?threadid=54479" target="_blank"><font color="#9c0000">樱花封包助手VER：0.2 Beta Build 0406</font></a><br /><font color="#cc6600">--------------------------------------------------------------------</font><br />2006.8.30更新<br />1.<a href="http://bbs.gameres.com/showthread.asp?threadid=17349" target="_blank"><font color="#9c0000">VB做外挂的一些知识</font></a><br />2.<a href="http://soft.hackbase.com/43/20050503/6638.html" target="_blank"><font color="#9c0000">风蓝搜集的外挂代码</font></a><br />3.<a href="http://soft.hackbase.com/37/20040918/5114.html" target="_blank"><font color="#9c0000">5dai教程8.0破解版</font></a><br />4.<a href="http://soft.hackbase.com/53/20060115/8761.html" target="_blank"><font color="#9c0000">眼镜外挂Ebook</font></a><br />5.<a href="http://down.youxiwg.com/yt2.rar" target="_blank"><font color="#9c0000">倚天II拍档代码</font></a><br />6.<a href="http://bbs.pediy.com/upload/2005/8/files/tj2assrob.rar" target="_blank"><font color="#9c0000">天骄2助理代码+分析文档</font></a><br />7.<a href="http://www.eexe.net/default.asp?CateID=15&amp;page=1" target="_blank"><font color="#9c0000">外挂学习搜集NB的BLOG(推荐)</font></a><br />8.<a href="http://www.ttee.com/newsoft/Article/Catalog28/45.html" target="_blank"><font color="#9c0000">A算法详解</font></a><br />9.<a href="http://www.77169.com/soft/html/19821.html" target="_blank"><font color="#9c0000">连连看外挂代码</font></a><br />10.<a href="http://dx2.3800hk.com/news/w64/116938.html" target="_blank"><font color="#9c0000">编写QQ外挂插件的原理和方法</font></a><br />您有啥好的意见或者建议请论坛短信交流<img src="http://hack.gameres.com/image/emot/em16.gif" border="0" /></pre></td></tr></tbody></table><br /> <br /><img src ="http://www.blogjava.net/wangxinsh55/aggbug/101917.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wangxinsh55/" target="_blank">SIMONE</a> 2007-03-05 14:52 <a href="http://www.blogjava.net/wangxinsh55/archive/2007/03/05/101917.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用MFC开发ActiveX控件</title><link>http://www.blogjava.net/wangxinsh55/archive/2007/02/28/101152.html</link><dc:creator>SIMONE</dc:creator><author>SIMONE</author><pubDate>Wed, 28 Feb 2007 07:33:00 GMT</pubDate><guid>http://www.blogjava.net/wangxinsh55/archive/2007/02/28/101152.html</guid><wfw:comment>http://www.blogjava.net/wangxinsh55/comments/101152.html</wfw:comment><comments>http://www.blogjava.net/wangxinsh55/archive/2007/02/28/101152.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wangxinsh55/comments/commentRss/101152.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wangxinsh55/services/trackbacks/101152.html</trackback:ping><description><![CDATA[　　<b>摘要：</b> 本文对COM组件中的ActiveX控件的MFC开发方法进行了介绍，讲述了用户自定义和库存属性、方法以及事件的添加方法和属性页的制作过程。使读者能够掌握基本的MFC ActiveX开发方法。<br /><br />　　<b>关键词：</b> MFC；ActiveX控件；COM<br /><br />　　<b>阅读目录：</b><br /><br />　　<a href="http://www.yesky.com/SoftChannel/72342371928702976/20040306/1774498.shtml" target="_blank">一、前言</a><br />　　<a href="http://www.yesky.com/SoftChannel/72342371928702976/20040306/1774498_1.shtml" target="_blank">二、建立工程框架</a><br />　　<a href="http://www.yesky.com/SoftChannel/72342371928702976/20040306/1774498_2.shtml" target="_blank">三、属性、方法以及事件的添加</a><br />　　<a href="http://www.yesky.com/SoftChannel/72342371928702976/20040306/1774498_3.shtml" target="_blank">四、实现属性表</a><br />　　<a href="http://www.yesky.com/SoftChannel/72342371928702976/20040306/1774498_4.shtml" target="_blank">五、在包容程序中使用ActiveX控件</a><br />　　<a href="http://www.yesky.com/SoftChannel/72342371928702976/20040306/1774498_4.shtml" target="_blank">六、小结</a><br /><br /><br />　　<b>前言</b><br /><br />　　ActiveX控件是一种实现了一系列特定接口而使其在使用和外观上更象一个控件的COM组件。ActiveX控件这种技术涉及到了几乎所有的COM和OLE的技术精华，如可链接对象、统一数据传输、OLE文档、属性页、永久存储以及OLE自动化等。<br /><br />　　ActiveX控件作为基本的界面单元，必须拥有自己的属性和方法以适合不同特点的程序和向包容器程序提供功能服务，其属性和方法均由自动化服务的IDispatch接口来支持。除了属性和方法外，ActiveX控件还具有区别于自动化服务的一种特性--事件。事件指的是从控件发送给其包容程序的一种通知。与窗口控件通过发送消息通知其拥有者类似，ActiveX控件是通过触发事件来通知其包容器的。事件的触发通常是通过控件包容器提供的IDispatch接口来调用自动化对象的方法来实现的。在设计ActiveX控件时就应当考虑控件可能会发生哪些事件以及包容器程序将会对其中的哪些事件感兴趣并将这些事件包含进来。与自动化服务不同，ActiveX控件的方法、属性和事件均有自定义（custom）和库存（stock）两种不同的类型。自定义的方法和属性也就是是普通的自动化方法和属性，自定义事件则是自己选取名字和Dispatch ID的事件。而所谓的库存方法、属性和事件则是使用了ActiveX控件规定了名字和Dispatch ID的"标准"方法、属性和事件。<br /><br />　　ActiveX控件可以使COM组件从外观和使用上能与普通的窗口控件一样，而且还提供了类似于设置Windows标准控件属性的属性页，使其能够在包容器程序的设计阶段对ActiveX控件的属性进行可视化设置。ActiveX控件提供的这些功能使得对其的使用将是非常方便的。本文下面即以MFC为工具对ActiveX控件的开发进行介绍。<br />　　<b>建立工程框架</b><br /><br />　　通过"MFC ActiveX ControlWizard"向导可以非常容易的建立一个MFC ActiveX控件工程框架。按照默认的选项将建立如图1所示的工程结构：<br /><br /><img onerror="this.src='http://www.yesky.com/image20010518/113233.jpg';" hspace="3" src="http://www.yesky.com/image20010518/113233.jpg" align="center" vspace="1" border="1" /><br />图1 使用缺省选项建立的ActiveX控件工程结构<br /><br />　　其中，_DSample68和_DSample68Events这两个接口将为客户程序提供本控件的属性、方法以及可能响应的事件。全局函数DllRegisterServer（）和DllUnregisterServer（）分别用于控件在注册表的注册和注销，一般不需要对其进行改动。<br /><br />　　应用程序类从COleControlModule继承。而COleControlModule有是从CWinApp派生，提供了初始化控件模块的功能。CSample68PropPage的基类是COlePropertyPage，CDialog类的派生类，主要负责对属性页中对图形界面下用户控件属性的显示。控件类CSample68Ctrl类是这几个类中比较重要的一个类，大部分实质性工作都在该类完成，其基类为COleControl，从CWnd和CCmdTarget继承，因此能够为控件对象提供与MFC窗口对象相同的功能同时也提供了一系列事件触发函数和一个分发映射表，使ActiveX控件能够同包容器程序有效地进行交互。该类的派生类将可以在满足特定的条件时向控件的包容器发送消息或是触发事件，以通知包容器程序在控件内有一些重要的事件发生。分发映射表是其中很重要的一个部分，负责向包容器程序暴露控件提供的方法和属性。图2展示了COleControl类在控件与包容器通信中所起的作用。可以看出，ActiveX控件与其包容器之间的所有通信过程都是由COleControl来完成的：<br /><br /><img onerror="this.src='http://www.yesky.com/image20010518/113232.jpg';" hspace="3" src="http://www.yesky.com/image20010518/113232.jpg" align="center" vspace="1" border="1" /><br />图2 COleControl在ActiveX控件与包容器通信中的作用<br /><br />　　控件类对基类COleControl的OnDraw（）函数进行了重载，向导生成了如下缺省代码，其作用是在控件的客户区绘制一个椭圆。在编程过程中通常要对其进行替换：<br /><br /><table width="100%" bgcolor="#ffffff"><tbody><tr><td>void CSample68Ctrl::OnDraw(<br />CDC* pdc, const CRect&amp; rcBounds, const CRect&amp; rcInvalid)<br />{<br />　// TODO: Replace the following code with your own drawing code.<br />　pdc-&gt;FillRect(rcBounds, CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH)));<br />　pdc-&gt;Ellipse(rcBounds);<br />}</td></tr></tbody></table><br /><img onerror="this.src='http://www.yesky.com/image20010518/113234.jpg';" hspace="3" src="http://www.yesky.com/image20010518/113234.jpg" align="center" vspace="1" border="1" /><br />图3 插入ActiveX控件<br /><br /><img onerror="this.src='http://www.yesky.com/image20010518/113236.jpg';" hspace="3" src="http://www.yesky.com/image20010518/113236.jpg" align="center" vspace="1" border="1" /><br />图4 插入的待测试控件<br /><br />　　对向导生成的代码进行编译后，将产生扩展名为ocx的ActiveX控件。ActiveX控件并不能独立运行，只能在包容器程序中才能够运行。通常，为了调试方便而多使用VC++附带的ActiveX Control Test Container工具以在测试阶段对ActiveX控件进行调试。在测试工具的客户区点击鼠标右键，并选中弹出菜单的"Insert New Control…"菜单项，将弹出图3所示的对话框，左侧的列表框中列出了当前系统中所有注册的ActiveX控件，选中要测试的控件并将其插入到测试程序即可通过"Control"菜单下的各菜单项对控件的方法、属性以及事件等进行测试。在位于下方的分割视图中将跟踪显示出调试记录（参见图4）。<br />　　<b>属性、方法以及事件的添加</b><br /><br /><img onerror="this.src='http://www.yesky.com/image20010518/113237.jpg';" hspace="3" src="http://www.yesky.com/image20010518/113237.jpg" align="center" vspace="1" border="1" /><br />图5 属性的添加<br /><br /><img onerror="this.src='http://www.yesky.com/image20010518/113239.jpg';" hspace="3" src="http://www.yesky.com/image20010518/113239.jpg" align="center" vspace="1" border="1" /><br />图6 方法的添加<br /><br />　　对ActiveX控件属性、方法和事件的添加均有库存和自定义两种。其中对属性和方法的添加在MFC ClassWizard对话框的Automation页中通过按钮"Add Property…"和"Add Method…"弹出如图5和图6所示的添加属性和添加方法的对话框来完成。对于库存属性和方法，可以直接从External name组合框的下拉列表中选取，Implementation项将自动设置为Stock。对于自定义属性和方法的添加与在自动化对象中为接口添加属性和方法的过程一样，ClassWizard将在.odl文件和控件类生成相应的代码，下面给出的是在控件类中实现的部分分发映射代码：<br /><br /><table width="100%" bgcolor="#ffffff"><tbody><tr><td>……<br />// Dispatch maps<br />//{{AFX_DISPATCH(CSample68Ctrl)<br />CString m_message;<br />afx_msg void OnMessageChanged();<br />afx_msg short GetXPos();<br />afx_msg void SetXPos(short nNewValue);<br />afx_msg short GetYPos();<br />afx_msg void SetYPos(short nNewValue);<br />afx_msg short MessageLen();<br />//}}AFX_DISPATCH<br />DECLARE_DISPATCH_MAP()<br />// Dispatch and event IDs<br />public:<br />enum {<br />//{{AFX_DISP_ID(CSample68Ctrl)<br />dispidMessage = 1L,<br />dispidXPos = 2L,<br />dispidYPos = 3L,<br />dispidMessageLen = 4L,<br />//}}AFX_DISP_ID<br />};<br />……<br />BEGIN_DISPATCH_MAP(CSample68Ctrl, COleControl)<br />//{{AFX_DISPATCH_MAP(CSample68Ctrl)<br />DISP_PROPERTY_NOTIFY(CSample68Ctrl, "Message", m_message, OnMessageChanged, VT_BSTR)<br />DISP_PROPERTY_EX(CSample68Ctrl, "XPos", GetXPos, SetXPos, VT_I2)<br />DISP_PROPERTY_EX(CSample68Ctrl, "YPos", GetYPos, SetYPos, VT_I2)<br />DISP_FUNCTION(CSample68Ctrl, "MessageLen", MessageLen, VT_I2, VTS_NONE)<br />DISP_STOCKPROP_BACKCOLOR()<br />DISP_STOCKPROP_CAPTION()<br />DISP_STOCKPROP_FORECOLOR()<br />//}}AFX_DISPATCH_MAP<br />END_DISPATCH_MAP()<br />……</td></tr></tbody></table><br />　　在这里共添加了一个自定义方法MessageLen（）和三种库存属性BackColor、Caption和ForeColor（分别表示控件的背景色、标题和前台色）、两个以Get/Set方式获取的自定义属性XPos、YPos和一个以成员变量方式实现的自定义属性Message。这几个自定义属性分别表示要显示字符串的x、y坐标和要显示的内容。对于采取Get/Set方式获取的属性，应当在控件类中为其添加相应的成员函数，并修改其Get、Set成员函数的实现过程：<br /><br /><table width="100%" bgcolor="#ffffff"><tbody><tr><td>short m_nYPos;<br />short m_nXPos;<br />……<br />short CSample68Ctrl::GetXPos() <br />{<br />　return m_nXPos;<br />}<br />void CSample68Ctrl::SetXPos(short nNewValue) <br />{<br />　m_nXPos = nNewValue;<br />　SetModifiedFlag();<br />}<br />short CSample68Ctrl::GetYPos() <br />{<br />　return m_nYPos;<br />}<br />void CSample68Ctrl::SetYPos(short nNewValue) <br />{<br />　m_nYPos = nNewValue;<br />　SetModifiedFlag();<br />}</td></tr></tbody></table><br />　　对于以成员变量方式创建的属性Message，向导还为其生成了一个消息响应函数：<br /><br /><table width="100%" bgcolor="#ffffff"><tbody><tr><td>void CSample68Ctrl::OnMessageChanged() <br />{<br />SetModifiedFlag();<br />}</td></tr></tbody></table><br />　　只要该属性的值被更改，OnMessageChanged（）函数即会被调用。<br /><br />　　为了使上述属性设置如背景色、前景色等能够与控件实际联系起来，需要替换控件类OnDraw（）函数中由向导生成的那部分代码。例如，下面这段代码即以前面添加的属性设置作为参数值，在控件中显示一串字符：<br /><br /><table width="100%" bgcolor="#ffffff"><tbody><tr><td>// 用背景色设置画刷<br />CBrush Brush(TranslateColor(GetBackColor()));<br />// 用前台色设置字体颜色<br />pdc-&gt;SetTextColor(TranslateColor(GetForeColor()));<br />// 绘制背景<br />pdc-&gt;FillRect(rcBounds, &amp;Brush);<br />// 设置字体背景透明<br />pdc-&gt;SetBkMode(TRANSPARENT);<br />// 显示字符<br />pdc-&gt;TextOut(m_nXPos, m_nYPos, m_message);</td></tr></tbody></table><br />　　为了使属性设置更改后，其效果能够立即在控件上显示出来，应当在与属性设置相关的函数实现中调用InvalidateControl（）以更新控件的显示。<br /><br />　　可以编译程序并在ActiveX Control Test Container工具中对其进行测试。在插入控件后，通过"Invoke Methods…"菜单项弹出如图7所示的对话框。在Method Name组合框中可以选择要测试的属性和方法。其中，对于属性的测试分别有ProgGet和ProgSet的说明以指出是对属性值的获取与设置。在Parameter编辑框中输入要设置的参数及其对应的参数类型，点击SetValue按钮将把该参数值添加到参数列表框，最后点击Invoke按钮将在控件应用设置的属性并执行指定的方法。对于有返回值的方法，其执行结果将在Return编辑框中显示。如果出现了异常操作，在Exception编辑框中将会显示出相应的异常错误信息。图8给出了经过属性设置的控件界面。<br /><br /><img onerror="this.src='http://www.yesky.com/image20010518/113241.jpg';" hspace="3" src="http://www.yesky.com/image20010518/113241.jpg" align="center" vspace="1" border="1" /><br />图7 对属性、方法的测试<br /><br /><img onerror="this.src='http://www.yesky.com/image20010518/113242.jpg';" hspace="3" src="http://www.yesky.com/image20010518/113242.jpg" align="center" vspace="1" border="1" /><br />图8 设置了属性后的控件<br /><br />　　对于控件属性的添加，在MFC ClassWizard对话框的ActiveX Events页中通过"Add Event…"按钮弹出如图9所示的"Add Event"事件添加对话框。与方法、属性的添加类似，在External name组合框中可以输入要添加的自定义事件名称，也可以从下拉列表选择库存事件。Implementation项将根据所要添加的事件类型而自动设置Stock或Custom选项。ActiveX控件将通过添加的事件来通知容器程序有特定的事件发生，库存事件多为键盘、鼠标事件，将由COleControl自动进行处理。对于自定义事件，则只是在.odl文件和控件类中添加了事件映射表等必要的代码（代码附下），至于应当在何种条件下触发该事件须由开发人员自行编写代码。<br /><br /><img onerror="this.src='http://www.yesky.com/image20010518/113243.jpg';" hspace="3" src="http://www.yesky.com/image20010518/113243.jpg" align="center" vspace="1" border="1" /><br />图9 事件的添加<br /><br /><table width="100%" bgcolor="#ffffff"><tbody><tr><td>dispinterface _DSample68Events<br />{<br />　properties:<br />　　// Event interface has no properties<br />　methods:<br />　　// NOTE - ClassWizard will maintain event information here.<br />　　// Use extreme caution when editing this section.<br />　　//{{AFX_ODL_EVENT(CSample68Ctrl)<br />　　[id(1)] void MsgOut();<br />　　//}}AFX_ODL_EVENT<br />};<br />……<br />// Event maps<br />//{{AFX_EVENT(CSample68Ctrl)<br />void FireMsgOut()<br />{FireEvent(eventidMsgOut,EVENT_PARAM(VTS_NONE));}<br />//}}AFX_EVENT<br />DECLARE_EVENT_MAP()<br />// Dispatch and event IDs<br />public:<br />enum {<br />　//{{AFX_DISP_ID(CSample68Ctrl)<br />　……<br />　eventidMsgOut = 1L,<br />　//}}AFX_DISP_ID<br />};<br />……<br />BEGIN_EVENT_MAP(CSample68Ctrl, COleControl)<br />//{{AFX_EVENT_MAP(CSample68Ctrl)<br />EVENT_CUSTOM("MsgOut", FireMsgOut, VTS_NONE)<br />//}}AFX_EVENT_MAP<br />END_EVENT_MAP()</td></tr></tbody></table><br />　　上述代码添加了一个MsgOut的自定义事件，可以在通过调用FireMsgOut（）来激发。下面对Message属性的OnMessageChanged（）消息响应函数进行修改，每当Message属性内容被更改都会调用该函数，在该函数中调用此前添加的MessageLen（）方法以确定更改后的Message属性的字符串长度，在长度大于10时调用FireMsgOut（）触发MsgOut事件：<br /><br /><table width="100%" bgcolor="#ffffff"><tbody><tr><td>void CSample68Ctrl::OnMessageChanged() <br />{<br />　InvalidateControl();<br />　if (MessageLen() &gt;= 10)<br />　　FireMsgOut();<br />　　SetModifiedFlag();<br />}</td></tr></tbody></table><br /><img onerror="this.src='http://www.yesky.com/image20010518/113244.jpg';" hspace="3" src="http://www.yesky.com/image20010518/113244.jpg" align="center" vspace="1" border="1" /><br />图10 选择要记录的事件<br /><br />　　在用ActiveX Control Test Container对刚添加的事件进行测试时，首先通过"Control"菜单下的"Logging…"菜单项弹出如图10所示的对话框，并从"Events"属性页中选中要跟踪记录的事件。当通过Invoke Methods对话框设置Message属性的内容超过10个字符后，位于程序框架下方的分割视图将记录控件所触发的MsgOut事件（如图11所示）。<br /><br /><img onerror="this.src='http://www.yesky.com/image20010518/113245.jpg';" hspace="3" src="http://www.yesky.com/image20010518/113245.jpg" align="center" vspace="1" border="1" /><br />图11 对事件的测试<br />　　<b>实现属性表</b><br /><br />　　属性表是ActiveX控件所特有的一种技术，可以在包容器程序处于设计阶段时为其提供一个可视化的人机交互界面，并可以通过其对控件的自定义属性和库存属性进行设置。在用向导生成程序框架的同时即已经生成了一个空的用于管理自定义属性的属性页。在代码上通过控件类实现文件中的属性页ID表对其进行维护：<br /><br /><table width="100%" bgcolor="#ffffff"><tbody><tr><td>BEGIN_PROPPAGEIDS(CSample68Ctrl, 1)<br />PROPPAGEID(CSample68PropPage::guid)<br />END_PROPPAGEIDS(CSample68Ctrl)</td></tr></tbody></table><br />　　这里的CSample68PropPage类是从COlePropertyPage派生出来的，而COlePropertyPage的基类又是CDialog，因此不难发现CSample68PropPage与通常的对话框类是比较相似的。可以象处理对话框一样在资源视图中为缺省的属性页添加与自定义属性相关的交互用控件，并通过ClassWizard将这些控件与类成员变量建立绑定关系。但是有一点不同，就是在绑定成员变量时还要与控件中的相应属性建立起对应关系。如图12所示，在Optional property name组合框中输入自定义属性名或是直接从下拉列表选择库存属性名，ClassWizard向导将在属性页类的DoDataExchange（）函数中添加控件、变量和属性的绑定代码：<br /><br /><table width="100%" bgcolor="#ffffff"><tbody><tr><td>void CSample68PropPage::DoDataExchange(CDataExchange* pDX)<br />{<br />　//{{AFX_DATA_MAP(CSample68PropPage)<br />　DDP_Text(pDX, IDC_MESSAGE, m_sMessage, _T("Message") );<br />　DDX_Text(pDX, IDC_MESSAGE, m_sMessage);<br />　DDP_Text(pDX, IDC_TITLE, m_sCaption, _T("Caption") );<br />　DDX_Text(pDX, IDC_TITLE, m_sCaption);<br />　DDP_Text(pDX, IDC_XPOS, m_nXPos, _T("XPos") );<br />　DDX_Text(pDX, IDC_XPOS, m_nXPos);<br />　DDP_Text(pDX, IDC_YPOS, m_nYPos, _T("YPos") );<br />　DDX_Text(pDX, IDC_YPOS, m_nYPos);<br />//}}AFX_DATA_MAP<br />DDP_PostProcessing(pDX);<br />}</td></tr></tbody></table><br /><img onerror="this.src='http://www.yesky.com/image20010518/113246.jpg';" hspace="3" src="http://www.yesky.com/image20010518/113246.jpg" align="center" vspace="1" border="1" /><br />图12 成员变量、控件与属性的绑定<br /><br />　　这里只是在向导生成的缺省属性页中实现了自定义属性的可视化设置。虽然也可以用相同的方法为库存属性进行设置，但是更多的还是采用添加库存属性页ID的方法来直接使用库存属性页来对其进行维护。例如，对于库存属性BackColor和ForeColor，可以通过ID号为CLSID_CcolorPropPage的库存属性页来进行设置，在将其添加到属性页ID表的同时一定要注意修改BEGIN_PROPPAGEIDS（）宏的属性页计数，否则将会引起系统的崩溃：<br /><br /><table width="100%" bgcolor="#ffffff"><tbody><tr><td>BEGIN_PROPPAGEIDS(CSample68Ctrl, 2)<br />PROPPAGEID(CSample68PropPage::guid)<br />PROPPAGEID(CLSID_CColorPropPage)<br />END_PROPPAGEIDS(CSample68Ctrl)</td></tr></tbody></table><br />　　继续在ActiveX Control Test Container中测试控件，将其插入后选择"Edit"菜单的"Properties…"菜单项，将弹出入图13所示的属性表。该属性表共有三个属性页，其中第一个属性页为刚才编辑的自定义属性页，第二个属性页（如图14所示）即为CLSID_CcolorPropPage所指定的颜色属性页（为库存属性页），最后一个属性页则是向导自动添加的扩展属性页。在属性表中设置了相应的属性后，点击"应用"按钮即可让控件使用新的属性。这与在"Invoke Methods"对话框中所完成的功能一样，但显然要方便的多。而且在包容器程序的设计阶段，也是通过该属性表来完成控件与客户的属性设置交互的。<br /><br /><img onerror="this.src='http://www.yesky.com/image20010518/113247.jpg';" hspace="3" src="http://www.yesky.com/image20010518/113247.jpg" align="center" vspace="1" border="1" /><br />图13 控件的属性表<br /><br /><img onerror="this.src='http://www.yesky.com/image20010518/113248.jpg';" hspace="3" src="http://www.yesky.com/image20010518/113248.jpg" align="center" vspace="1" border="1" /><br />图14 颜色属性页<br />　　<b>在包容程序中使用ActiveX控件</b><br /><br />　　对于ActiveX控件的包容器程序，并不需要象使用OLE文档服务器或ActiveX文档服务器对象那样编写特定的包容器程序框架，直接将控件添加到工程并在对话框上创建即可对其进行使用。<br /><br />　　通过"Project"菜单下的"Add To Project"菜单项弹出的"Components and Controls…"子菜单项打开一个"Components and Controls Gallery"对话框，进入到Registered ActiveX Controls目录下，选取前面创建的ActiveX控件，并将其添加到工程。向导将会在工程中添加一个关于此ActiveX控件的包装类，并在"Controls"工具栏中添加一个表示此控件的图标。可以象使用其他的标准控件一样将其放置到对话框资源中，并修改其缺省属性。除此之外，还可以在程序中通过对控件包装类成员函数的使用来动态更改控件的属性设置。例如，下面这段代码通过包装类对象m_ctrlTest在程序运行期间动态设置了控件的XPos、YPos 以及Message属性：<br /><br /><table width="100%" bgcolor="#ffffff"><tbody><tr><td>// 更新显示<br />UpdateData();<br />// 动态更改控件的Message属性<br />m_ctrlTest.SetMessage(m_sInput);<br />// 设置显示坐标<br />m_ctrlTest.SetXPos(10);<br />m_ctrlTest.SetYPos(10); </td></tr></tbody></table><br /><img onerror="this.src='http://www.yesky.com/image20010518/113249.jpg';" hspace="3" src="http://www.yesky.com/image20010518/113249.jpg" align="center" vspace="1" border="1" /><br />图15 添加事件响应函数<br /><br />　　在资源视图中用鼠标右键点击放置于对话框上的ActiveX控件，并从弹出菜单中选择"Events…"菜单项，将弹出如图15所示的对话框，在左边的列表框中显示了控件提供的事件，双击事件将在包容器程序中添加相应的事件处理函数和事件映射表，并可以在响应控件发出的事件后进行相应的处理：<br /><br /><table width="100%" bgcolor="#ffffff"><tbody><tr><td>BEGIN_EVENTSINK_MAP(CSample69Dlg, CDialog)<br />//{{AFX_EVENTSINK_MAP(CSample69Dlg)<br />ON_EVENT(CSample69Dlg, IDC_SAMPLE68CTRL1, 1 /* MsgOut */, OnMsgOutSample68ctrl1, VTS_NONE)<br />//}}AFX_EVENTSINK_MAP<br />END_EVENTSINK_MAP()<br />……<br />void CSample69Dlg::OnMsgOutSample68ctrl1() <br />{<br />　// 得到输入字符数<br />　int nNum = m_ctrlTest.MessageLen();<br />　// 回显信息<br />　m_sInput.Format("输入字符太多，共输入了%d个字符", nNum); <br />　// 显示信息<br />　UpdateData(FALSE);<br />}</td></tr></tbody></table><br />　　从上述对ActiveX控件的使用过程可以看出其与标准控件的使用并没有什么太大的区别，通过包装类使得在客户程序中对控件属性、方法的使用可以象使用普通MFC类一样简单。另外，在控件的包装类中还提供有Create（）方法，使在程序运行期间也能够动态创建控件。<br /><br />　　<b>小结</b><br /><br />　　尽管ActiveX控件从技术上集成了COM和OLE的许多精华技术，但由于MFC对ActiveX控件提供了强大的支持，使得对ActiveX控件的开发成为一件非常容易的事情。但要深刻理解ActiveX控件技术，还要对一些基础技术有一个基本的概念，本文的目的并不在于介绍如何编写一个ActiveX控件，而是通过对控件的创建过程的分析而使读者能够对ActiveX控件的开发有一个新的认识。本文所述代码在Windows 2000 Professional下由Microsoft Visual C++ 6.0编译通过。<br /><img src ="http://www.blogjava.net/wangxinsh55/aggbug/101152.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wangxinsh55/" target="_blank">SIMONE</a> 2007-02-28 15:33 <a href="http://www.blogjava.net/wangxinsh55/archive/2007/02/28/101152.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>平时积累的有关MSDN 里ActiveX开发技术的一些链接.</title><link>http://www.blogjava.net/wangxinsh55/archive/2007/02/28/101150.html</link><dc:creator>SIMONE</dc:creator><author>SIMONE</author><pubDate>Wed, 28 Feb 2007 07:18:00 GMT</pubDate><guid>http://www.blogjava.net/wangxinsh55/archive/2007/02/28/101150.html</guid><wfw:comment>http://www.blogjava.net/wangxinsh55/comments/101150.html</wfw:comment><comments>http://www.blogjava.net/wangxinsh55/archive/2007/02/28/101150.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wangxinsh55/comments/commentRss/101150.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wangxinsh55/services/trackbacks/101150.html</trackback:ping><description><![CDATA[1. ActiveX 基础知识中文专题:<br />http://msdn.microsoft.com/library/chs/default.asp?url=/library/CHS/vccore/html/_core_activex_control_containers.3a_.using_controls_in_a_non.2d.dialog_container.asp<br /><br />2. BHO 编程指南：<br />ms-help://MS.MSDNQTR.2003FEB.2052/dnwebgen/html/bho.htm<br /><br />3. 定制浏览器向导：<br />ms-help://MS.MSDNQTR.2003FEB.2052/progie/workshop/browser/hosting/wbcustomization.htm<br /><br />4. 使用 ATL AXHost 承载 ActiveX 控件<br />ms-help://MS.MSDNQTR.2003FEB.2052/vccore/html/vclrfHostingActiveXControlsUsingATLAXHost.htm<br /><br />5. The Shell Drag/Drop Helper Object<br />ms-help://MS.MSDNQTR.2003FEB.2052/dnwui/html/ddhelp_pt1.htm<br /><br />6. Using ATL 3.0 to Host the Video Control<br />ms-help://MS.MSDNQTR.2003FEB.2052/directshow_sp1/htm/hostingthevideocontrolinanatlbasedapplication.htm<br /><br />7. HOWTO: Dynamically Add ActiveX Controls to ATL Composite Control<br />ms-help://MS.MSDNQTR.2003FEB.2052/enu_kbvisualc/en-us/visualc/Q218442.htm<br /><br />8. HOWTO: Control the Context Menu in an ATL HTML Control<br />ms-help://MS.MSDNQTR.2003FEB.2052/enu_kbie_dev/en-us/ie_dev/Q274202.htm<br /><br />11.WebBrowser 不可见时不触发 DocumentComplete BUG：<br />http://support.microsoft.com/kb/q259935/<br /><br />12.重新发布 Visual C++ ActiveX 控件<br />http://msdn.microsoft.com/library/chs/default.asp?url=/library/CHS/vccore/html/_core_regular_dlls_dynamically_linked_to_mfc.3a_.overview.asp<br /><br />13.事件<br />ms-help://MS.MSDNQTR.2003FEB.2052/progie/workshop/browser/mshtml/tutorials/sink.htm<br /><br />14. 升级现有的 ActiveX 控件<br />ms-help://MS.MSDNQTR.2003FEB.2052/vccore/html/_core_Upgrading_an_Existing_ActiveX_Control_to_be_Used_on_the_Internet.htm<br /><br />15.PRB: TranslateAccelerator() Not Called for ActiveX Controls<br />ms-help://MS.MSDNQTR.2003FEB.2052/enu_kbvisualc/en-us/visualc/Q183167.htm<br /><br />16.FIX: ActiveX Control Events Are Not Fired in ATL Dialog<br />ms-help://MS.MSDNQTR.2003FEB.2052/enu_kbvisualc/en-us/visualc/Q190530.htm<br /><br />17. Creating Custom Explorer Bars, Tool Bands, and Desk Bands<br />ms-help://MS.MSDNQTR.2003FEB.2052/shellcc/platform/shell/programmersguide/shell_adv/bands.htm<br />23 Designing Secure ActiveX Controls<br />ms-help://MS.MSDNQTR.2003FEB.2052/activex/workshop/components/activex/security.htm<br /><br />24. Packaging ActiveX Controls<br />ms-help://MS.MSDNQTR.2003FEB.2052/activex/workshop/components/activex/packaging.htm<br /><br />25.COM Objects Overviews and Tutorials<br />ms-help://MS.MSDNQTR.2003FEB.2052/icom/workshop/components/com/comobj.htm<br /><br />26.The Windows Internet (WinINet) application programming interface (API)<br />ms-help://MS.MSDNQTR.2003FEB.2052/wininet/wininet/about_wininet.htm<br /><br />27.<br />ms-help://MS.MSDNQTR.2003FEB.2052/enu_kbvisualc/en-us/visualc/Q150204.htm<br /><br />28.HOWTO: Call a Script Method from an ActiveX Script Host<br />ms-help://MS.MSDNQTR.2003FEB.2052/enu_kbvisualc/en-us/visualc/Q222966.htm<br /><br />29. HOWTO: Add Toolbars and Tooltips to ActiveX Controls<br />ms-help://MS.MSDNQTR.2003FEB.2052/enu_kbvisualc/en-us/visualc/Q194294.htm<br /><br />30.HOWTO: Draw ActiveX Controls with Child Controls in Design Time<br />ms-help://MS.MSDNQTR.2003FEB.2052/enu_kbvisualc/en-us/visualc/Q198732.htm<br /><br />31.HOWTO: Adding Tooltips to ATL ActiveX Controls<br />ms-help://MS.MSDNQTR.2003FEB.2052/enu_kbvisualc/en-us/visualc/Q201540.htm<br /><br />32. PRB: MFC ActiveX Control BLOB Props Missing While Printing in Internet Explorer<br />ms-help://MS.MSDNQTR.2003FEB.2052/enu_kbie_dev/en-us/ie_dev/Q200932.htm<br /><br />33.HOWTO: Enable ActiveX Control Event Handling on a Web Page<br />ms-help://MS.MSDNQTR.2003FEB.2052/enu_kbie_dev/en-us/ie_dev/Q200839.htm<br /><br />34.PRB: ActiveX Control Window Is Not Created Until Visible in Internet Explorer<br />ms-help://MS.MSDNQTR.2003FEB.2052/enu_kbie_dev/en-us/ie_dev/Q195188.htm<br /><br />35. HOWTO: Detect IE's STOP Button Click in ActiveX Control<br />ms-help://MS.MSDNQTR.2003FEB.2052/enu_kbvisualc/en-us/visualc/Q167956.htm<br /><br />36.HOWTO: Renaming an ActiveX Control After its Project Is Created<br />ms-help://MS.MSDNQTR.2003FEB.2052/enu_kbvisualc/en-us/visualc/Q185695.htm<br /><br />37.HOWTO: Troubleshoot ActiveX Control Crashes in Internet Explorer<br />ms-help://MS.MSDNQTR.2003FEB.2052/enu_kbie_dev/en-us/ie_dev/Q247845.htm<br /><br />38.SAMPLE: AXSH.EXE Demonstrates Implementing ActiveX Script Hosts<br />http://download.microsoft.com/download/vc60pro/sample3/1/WIN98/EN-US/Axsh.exe<br /><br />39.OLE 线程模型的说明和工作方式<br />http://support.microsoft.com/kb/q150777/<br /><br />40.The COM Programmer's Cookbook<br />ms-help://MS.MSDNQTR.2003FEB.2052/dncomg/html/msdn_com_co.htm<br /><br />41. 利用晚期绑定的威力编写您自己的 COM 调用传输<br />ms-help://MS.MSDNQTR.2003FEB.2052/dntaloc/html/comleverage.htm<br /><br /><br />44. ActiveX Controls on the Internet <br />http://msdn2.microsoft.com/en-us/library/d0d6f721.aspx<br /><br /><br />46. IContextMenu<br />ms-help://MS.MSDNQTR.2003FEB.2052/shellcc/platform/shell/programmersguide/shell_int/shell_int_extending/extensionhandlers/contextmenuhandlers.htm<br /><br /><img src ="http://www.blogjava.net/wangxinsh55/aggbug/101150.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wangxinsh55/" target="_blank">SIMONE</a> 2007-02-28 15:18 <a href="http://www.blogjava.net/wangxinsh55/archive/2007/02/28/101150.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>