??xml version="1.0" encoding="utf-8" standalone="yes"?> 我用UltraEdit打开生成的xml文gQUltraEdit把它识别为ASCII格式的文本文Ӟ而通过二进制查看,也确实如此,我看了下帮助文Q也没具体提及到q个问题。但后来我仔l整理了一下思\后,发觉q个其实不算什么问题,分析如下?/p>
文上就说明了,TinyXML是完全支持UTF-8的,可以试load一个UTF-8格式的xml文gQ完全没有问题,那现在问题是load了一个UTF-8文g后,文g中的字符Ԍ在TiXmlDocument对象中,到底是什么格式?TinyXML在load它之后会不会直接把UTF-8格式转ؓUnicode格式Q——不会,Z么?因ؓTinyXML在编写的时候是把自己定位ؓ一个比较单U的C++ LibQ它没有使用MWindows相关的APIQ包括很重要的两个编码{换函数WideCharToMultiByte和MultiByteToWideCharQ它都没用,对不同^収ͼ~码转换函数可能是不同的Q因此TinyXML不会画蛇添地提供这个{换,也就是说Q如果你的xml文gQUTF-8格式Q中如果有中文的话,用TinyXML加蝲q来之后Q依旧是UTF-8的,不经q{换直接print出来的话Q就是ؕ码了Q所以要l过一个UTF-8到Unicode的{换,转换代码我这里就不写了。再说一下,TinyXML声称对UTF-8的支持表C它认识UTF-8的xml文gQ但q不表示它自动对UTF-8的文本进行{换?/p>
好,再回C存的问题上,其实你差不多惛_了,应该怎么做,因ؓ整个TinyXML其实q不计较内存中的文本I竟是什么内容,它都可以把文本当做是UTF-8Q当然了Q实际上把文本{为UTF-8然后交给TinyXML的工作是我们的程序去完成Q,那我们可不可以说保存的xml文g其实已l是UTF-8格式的?——答案是肯定的。而UTF-8的格式标志其实就是文件头的三个字节:0xEFQ?xBBQ?xBF。有了这个头Q用UltraEdit打开q个xmlQUltraEdit把它认为是一个UTF-8的文本文件了?br> q个改动非常单,E微览下TinyXML的代码就知道怎么改了Q我下蝲的TinyXML?.6.1版本的(我没记错的话Q,在tinyxml.cpp文g中搜?#8220;useMicrosoftBOM”Q把“useMicrosoftBOM = false;”q个语句改ؓ“useMicrosoftBOM = true;”可以了Q好像一共才三处地方Q十分简单,代码一看就懂,不多说了?/p>
写这个程序的时候其实我是想要这么一U加密方法:假如明文只有一个字节,那加密出来的密文应该也只有一个字节。而实际上别h实现好的AES法加密出来的密文通常都要带上一串随机数Q不太符合我的要求?/p>
没有什么includeQ没有什么LIBQ更没有什么DLLQ代码再单不q,但我保证它很有效QOKQ不多说了,看代码: 写完了,q行l果是如下: 241 206 235 249 4 160 190 73 253 196 48 49 50 51 52 53 54 55 56 57 上面q段是密文,看v来是不是完全没有原文的样子了Q你试试看把TEST_LEN改ؓ1Q只加密一个字节看Q这也是允许的,代码UCQ移植性很好,拿去别处用也方便。但对安全性要求高的应用可能不能用这U方法了Q至于怎么h安全,我也不知道,密码学方面的东西我最x了?br>
1Q最最最要注意的是Q垃圑֛收机?br />
C#没有deleteQ其托管资源不需要deleteQ也不能昑ּdeleteQ完全由.net的垃圑֛收器来管理?br />
E微有点ȝ的是非托资源,比如打开的文件句柄,创徏的位囑֏柄等Q这些是非托资源,如果一直不昑ּ回收Q那有可能会来多Q那怎么办?q方面的问题在绝大多数的C#的书c上都有讲述Q有时候看得h晕头转向的,其实我们只要遵@q么一U规范,可以应对不?00%Q也?9%的情况了Q?br />
cM没有非托资源的情况下,啥都不用,也不用实现析构函数?br />如果cM有非托管资源Q则应该实现析构函数Qƈ且在析构函数中释放资源?br />如果cM有非托管资源Qƈ且想让类使用者提前释放掉q些非托资源的话,那就实现一个Dispose的方法来释放非托资源,再加上这么一行代码GC.SuppressFinalize(this);以此来告?net的垃圑֛收器Q不需要再调用一ơ析构函数来释放非托资源了?br />
2Q?引用Qstruct/class
C#的struct和class差别很大Q不像C++那样几乎没什么差别,struct适用于创建小的结构,它用的是g递,而用class的对象用的是引用传递?br />
|存在于栈区,而引用,则存在于堆区?br />
structQ存在于栈区Q可用new初始化(也可以不用)Q没有默认构造函敎ͼ但可以添加构造函敎ͼ也可以不dQ,没析构函敎ͼ不能被ѝ?br />classQ存在于堆区Q必ȝnew初始化,有默认构造函敎ͼ有析构函敎ͼ当然能被l承啦?br />
3Q注意装和拆箱
值和引用有时候得互相转换Q比如当一个函数需要引用参敎ͼ而你却把g了过去,那么存在这么一个隐式{换的q程了,q个q程会有额外的性能开销Q所以要量避免。D{为引用叫“装箱”Q反q来引用转ؓ值叫“拆箱”。装可以是隐式的,但拆׃定是昑ּ的,如:
int i = 123;
object o = i; //装箱Q隐式)
int j = (int) o; //拆箱Q显式)
4Q其它一些语法差?br />
4.1 虚方法必d上override修饰Q否则会有warning?br />4.2 没有了C++构造函C?#8220;初始化列?#8221;?br />4.3 布尔和整型是两种不同cd?br />4.4 switch语句的行为和C++的略有不同?br />
E序每次L先触到WM_SIZE的这个断点,N我理解一直有误?于是我创Z一个hello worldE序Q调试了半天Q又发觉没有q个问题QWM_CREATEL在WM_SIZE之前Q我试在别处重现问题,无果……现在Q你再仔l看一下上图,你发现问题了么?
我想l过我这么提C,你应该知道了Q原因就是WM_CREATEq个断点前一行的SetWindowLongq个调用Q这个调用会引v一个WM_SIZE事gQ所以这制造了WM_SIZE在WM_CREATE之前q个假象。处理这个问题很单,把SetWindowLongq行拿掉Q然后把WS_NONAVDONEBUTTONq个属性交lCreateWindowQ如下图Q?br>
q样好了,我想Z提个问题Q我在设计一个程序的时候,也碰C“布局”?#8220;加蝲”的先后问题,因ؓ布局可能需要重新调整加载的资源Q而加载资源又需要参考布局的尺寸,׃功能上的原因Q我必须把两者分开Q所以存在我前面说的q个先鸡先蛋的难题。最后我是这么弄的,做一?#8220;资源是否已经加蝲”的标志,然后L?#8220;布局”Q布局的时候检查这个标志,如果没有资源的加载,那就只更改尺寸参数而不调整资源Q这Lh没什么问题了Q从逻辑上来说我的设计确实是WM_SIZE优先于WM_CREATE……
Ƣ迎对这些方面的设计问题留言讨论?
This compiler switch doesn't do anything very profound. It really only sets a flag in the executable file that indicates how the program is to be loaded an run. If an executable is flagged as a Console Application and is started from Windows, the Windows operating system creates a Command Prompt window that launches the program and displays any console output from the program. If the console application is started from within the Command Prompt window, the MS-DOS promptdoesn't return until the program terminateds. If the executable is flagged as a Windows Application, no Command Prompt window is created. Any console output from the program goes into the bit bucket. If you start such a program from the Command Prompt window, the MS-DOS prompt appears again right after the program is launched. The point is this: nothing bad happens if you compile a Windows Forms application as a console application!
作者之前提到的~译选项?target:exe?target:winexeQ前者生成控制台E序Q后者生成WindowsE序Q这是C#的编译器Q而VC++的连接器的选项却有些不同,看这张图Q?br>
如果你在应用E序向导里指定的是一个WindowsE序Q而你后来却在q个q接器选项里选择ConsoleQ那会怎么样呢Q——会q接p|Q因为VC++q接器认为ConsoleE序和WindowsE序的入口函数是不同Q这L单的一改它会找不到入口函数Q所以连接失败?br>
但不这P通过Charles Petzold的这D|qͼ我们对ConsoleE序和WindowsE序的认识应该是没什么问题了Q两者其实ƈ没有什么根本不同,只是WindowsҎPE文g中的标识Q用E微不同的方法来q行q两U程序而已?br>
BTWQWindowsE序可以创徏自己的控制台Q参考AllocConsole{APIQ,控制台程序也可以创徏H口?/p>
{
int a=1;
int b=0;
int c=a/b;
}
catch()
{
printf("Ignore that error and continue to work!\n");
}
{
int a=1;
int b=0;
int c=a/b;
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
printf("Ignore that error and continue to work!\n");
}
{
int* p = NULL;
*p = 10;
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
printf("Exception[%0X] occured!\n", GetExceptionCode());
}
{
BOOL rtn = Foo1();
if(!rtn)
__leave;
rtn = Foo2();
if(!rtn)
__leave;
//Blah Blah Blah
}
__finally
{
//Do some job to release the resources.
}
INT iLen = lstrlen(p);
TCHAR szBuff[10];
lstrcpy(szBuff, p);
使用新的安全字符串函C后,上面的都不是问题了,关于安全字符串函敎ͼ技术细节还是很多的Q但我们不用那么多Q我们只需要关心我们用得最多的几个可以了?br>
旧函?/td>
新函敎ͼ字符Q?/td>
新函敎ͼ字节Q?/td>
lstrlen
StringCchLength
StringCbLength
lstrcpy
StringCchCopy
StringCbCopy
lstrcat
StringCchCat
StringCbCat
wsprintf
StringCchPrintf
StringCbPrintf
你会发现Q一个旧的函数对应了两个新的函数Q理解这个ƈ不难Q因为新的函数大多需要你指定一?#8220;最镉K?#8221;Q那q个长度I竟指的是字W数目还是字节长度呢Q如果指的是字符数目的话Q那qCchpd的,否则qCbpd的。好我们改一下上面的代码Q?br>
size_t iLen;
//We want to get the character number
StringCchLength(p, STRSAFE_MAX_CCH, &iLen);
TCHAR *p = TEXT("more than 10 characters.");
TCHAR szBuff[BUFFER_SIZE];
StringCchCopy(szBuff, BUFFER_SIZE, p);
//szBuff is "more than" now, it always ends with a '\0'
最后,别忘了用的时候包含strsafe.h头文件?
#define MYTRACE DbgStrOut
#else
#define MYTRACE
#endif
// My debug output function
void DbgStrOut(const TCHAR *fmt, );
)
{
TCHAR szOutStr[512];
va_list ap;
va_start(ap, fmt);
vswprintf(szOutStr, fmt, ap);
va_end(ap);
OutputDebugString(szOutStr);
}
using namespace std;
//假设pData指向了内存中你想要{变ؓ的数据
//iLen是数据长?/span>
strstreambuf buff(pData, iLen);
istream xmlStream(&buff);
//xmlStream也就是你要的了
xmlStream>>xmldoc;
//加密和解密,其实q两个函数完全相同的Q简单v见嘛Q另外我居然允许密码为空Q也是简单v?/span>
void JiangEncode(unsigned char *pBuff, int iBuffLen, char *pKey=NULL, int iKeyLen=0);
void JiangDecode(unsigned char *pBuff, int iBuffLen, char *pKey=NULL, int iKeyLen=0);
//q个main函数是用来测试的
#define TEST_LEN 100
int main(int argc, char* argv[])
{
unsigned char totest[TEST_LEN];
memcpy(totest, "012345678901234567890123456789012345678901234567890123456789\
0123456789012345678901234567890123456789", 100);
JiangEncode(totest, TEST_LEN, "J~xye", 6);
int i;
for(i=0; i<TEST_LEN; i++)
{
if((i%10)==0)
printf("\n");
printf("%d ", totest[i]);
}
printf("\n");
JiangDecode(totest, TEST_LEN, "J~xye", 6);
for(i=0; i<TEST_LEN; i++)
{
if((i%10)==0)
printf("\n");
printf("%d ", totest[i]);
}
return 0;
}
//交换两个BYTE
void Swap2Byte(unsigned char* v1, unsigned char* v2)
{
*v1 ^= *v2;
*v2 ^= *v1;
*v1 ^= *v2;
}
void GetMyCypher(const char* pKey, int iKeyLen, unsigned char* pMyCypher)
{
//原始加密索引
const unsigned char cypherOrg[256] =
{
1, 3, 5, 15, 17, 51, 85, 255, 26, 46, 114, 150, 161, 248, 19, 53,
95, 225, 56, 72, 216, 115, 149, 164, 247, 2, 6, 10, 30, 34, 102, 170,
229, 52, 92, 228, 55, 89, 235, 38, 106, 190, 217, 112, 144, 171, 230, 49,
83, 245, 4, 12, 20, 60, 68, 204, 79, 209, 104, 184, 211, 110, 178, 205,
76, 212, 103, 169, 224, 59, 77, 215, 98, 166, 241, 8, 24, 40, 120, 136,
131, 158, 185, 208, 107, 189, 220, 127, 129, 152, 179, 206, 73, 219, 118, 154,
181, 196, 87, 249, 16, 48, 80, 240, 11, 29, 39, 105, 187, 214, 97, 163,
254, 25, 43, 125, 135, 146, 173, 236, 47, 113, 147, 174, 233, 32, 96, 160,
251, 22, 58, 78, 210, 109, 183, 194, 93, 231, 50, 86, 250, 21, 63, 65,
195, 94, 226, 61, 71, 201, 64, 192, 91, 237, 44, 116, 156, 191, 218, 117,
159, 186, 213, 100, 172, 239, 42, 126, 130, 157, 188, 223, 122, 142, 137, 128,
155, 182, 193, 88, 232, 35, 101, 175, 234, 37, 111, 177, 200, 67, 197, 84,
252, 31, 33, 99, 165, 244, 7, 9, 27, 45, 119, 153, 176, 203, 70, 202,
69, 207, 74, 222, 121, 139, 134, 145, 168, 227, 62, 66, 198, 81, 243, 14,
18, 54, 90, 238, 41, 123, 141, 140, 143, 138, 133, 148, 167, 242, 13, 23,
57, 75, 221, 124, 132, 151, 162, 253, 28, 36, 108, 180, 199, 82, 246, 1
};
memcpy(pMyCypher, cypherOrg, 256);
//Ҏ密码调整加密索引
int i, j;
for(i=0; i<iKeyLen; i++)
{
for(j=0; j<256; j++)
{
int iSwitchIndex = (pMyCypher[j] * pKey[i])%255;
if(iSwitchIndex!=j)
Swap2Byte(&(pMyCypher[j]), &(pMyCypher[iSwitchIndex]));
}
}
}
void JiangCode(unsigned char* pBuff, int iBuffLen, char* pKey, int iKeyLen)
{
unsigned char cypher[256];
GetMyCypher(pKey, iKeyLen, cypher);
int iIndex=0;
while (iIndex<iBuffLen)
{
//其实……也就一个异或操作,所以加密和解密的过E完全一?/span>
pBuff[iIndex] ^= cypher[iIndex%256];
++iIndex;
}
}
void JiangEncode(unsigned char* pBuff, int iBuffLen, char* pKey, int iKeyLen)
{
JiangCode(pBuff, iBuffLen, pKey, iKeyLen);
}
void JiangDecode(unsigned char* pBuff, int iBuffLen, char* pKey, int iKeyLen)
{
JiangCode(pBuff, iBuffLen, pKey, iKeyLen);
}
160 238 105 243 18 190 10 97 26 66
182 218 99 73 40 204 208 123 196 46
76 208 117 159 38 66 234 117 142 52
66 63 135 193 60 49 228 15 12 58
131 252 1 171 96 72 254 198 171 201
110 242 27 189 199 108 136 12 177 215
88 66 156 183 221 142 27 45 61 221
122 158 39 174 123 112 156 241 77 227
112 148 57 67 225 30 47 49 83 94
48 49 50 51 52 53 54 55 56 57
48 49 50 51 52 53 54 55 56 57
48 49 50 51 52 53 54 55 56 57
48 49 50 51 52 53 54 55 56 57
48 49 50 51 52 53 54 55 56 57
48 49 50 51 52 53 54 55 56 57
48 49 50 51 52 53 54 55 56 57
48 49 50 51 52 53 54 55 56 57
48 49 50 51 52 53 54 55 56 57 Press any key to continue
Q博主:此ؓ转蝲文章Q出处已l记不清Q内Ҏ些泛泛而谈Q仅供参考)
一、客h/服务器模?/span>
在TCP/IP|络中两个进E间的相互作用的L模式是客h/服务器模?Client/Server model)。该模式的徏立基于以下两点:
1、非对等作用Q?br>2、通信完全是异步的?br>客户?服务器模式在操作q程中采取的是主动请C方式?/p>
服务器方要先启动QƈҎL提供相应服务Q过E如下:
1、打开一通信通道q告知本C机,它愿意在某一个公认地址上接收客戯求?br>2、等待客戯求到达该端口?br>3、接收到重复服务hQ处理该hq发送应{信受?br>4、返回第二步Q等待另一客户h
5、关闭服务器?/p>
然后dhQ?br>1、打开一通信通道Qƈq接到服务器所在主机的特定端口?br>2、向服务器发送服务请求报文,{待q接收应{;l箋提出h……
3、请求结束后关闭通信通道q终止?/p>
二、基本套接字
Z更好说明套接字编E原理,l出几个基本的套接字Q在以后的篇q中会给出更详细的用说明?/p>
1、创建套接字——socket()
功能Q用前创徏一个新的套接字
格式QSOCKET PASCAL FAR socket(int af,int type,int procotol);
af: 通信发生的区?br>type: 要徏立的套接字类?br>procotol: 使用的特定协?
2、指定本地地址——bind()
功能Q将套接字地址与所创徏的套接字可pv来?br>格式Qint PASCAL FAR bind(SOCKET s,const struct sockaddr FAR * name,int namelen);
s: 是由socket()调用q回的ƈ且未作连接的套接字描q符Q套接字P?br>其它Q没有错误,bind()q回0Q否则SOCKET_ERROR
地址l构说明Q?/p>
3、徏立套接字q接——connect()和accept()
功能Q共同完成连接工?br>格式Qint PASCAL FAR connect(SOCKET s,const struct sockaddr FAR * name,int namelen);
格式QSOCKET PASCAL FAR accept(SOCKET s,struct sockaddr FAR * name,int FAR * addrlen);
(参数同上)
4、监听连接——listen()
功能Q用于面向连接服务器Q表明它愿意接收q接?br>格式Qint PASCAL FAR listen(SOCKET s, int backlog);
5、数据传输——send()与recv()
功能Q数据的发送与接收
格式Qint PASCAL FAR send(SOCKET s,const char FAR * buf,int len,int flags);
格式Qint PASCAL FAR recv(SOCKET s,const char FAR * buf,int len,int flags);
buf:指向存有传输数据的缓冲区的指针?
6、多路复用——select()
功能Q用来检一个或多个套接字状态?br>格式Qint PASCAL FAR select(int nfds,fd_set FAR * readfds,fd_set FAR * writefds,
fd_set FAR * exceptfds,const struct timeval FAR * timeout);
readfds:指向要做L的指针
writefds:指向要做写检的指针
exceptfds:指向要检是否出错的指针
timeout:最大等待时?/p>
7、关闭套接字——closesocket()
功能Q关闭套接字s
格式QBOOL PASCAL FAR closesocket(SOCKET s);
三、典型过E图
2.1 面向q接的套接字的系l调用时序图
2.2 无连接协议的套接字调用时序图
2.3 面向q接的应用程序流E图
四、WinSock?/strong>
Windows Sockets 是从 Berkeley Sockets 扩展而来的,其在l承 Berkeley Sockets 的基上,又进行了新的扩充。这些扩充主要是提供了一些异步函敎ͼq增加了W合WINDOWS消息驱动Ҏ的|络事g异步选择机制?/p>
Windows Sockets׃部分l成Q开发组件和q行lg?br>开发组ӞWindows Sockets 实现文档、应用程序接?API)引入库和一些头文g?br>q行lgQWindows Sockets 应用E序接口的动态链接库(WINSOCK.DLL)?
五、WinSock主要扩充说明
1、异步选择机制
Windows Sockets 的异步选择函数提供了消息机制的|络事g选择Q当使用它登记网l事件发生时Q应用程序相应窗口函数将收到一个消息,消息中指CZ发生的网l事Ӟ以及与事件相关的一些信息。Windows Sockets 提供了一个异步选择函数 WSAAsyncSelect()Q用它来注册应用E序感兴的|络事gQ当q些事g发生Ӟ应用E序相应的窗口函数将收到一个消息?/p>
函数l构如下Q?br>int PASCAL FAR WSAAsyncSelect(SOCKET s,HWND hWnd,unsigned int wMsg,long lEvent);
hWndQ窗口句?br>wMsgQ需要发送的消息
lEventQ事Ӟ以下Z件的内容Q?/p>
事g值含义:
FD_READ 期望在套接字上收到数据(卌准备好)时接到通知
FD_WRITE 期望在套接字上可发送数据(卛_准备好)时接到通知
FD_OOB 期望在套接字上有带外数据到达时接到通知
FD_ACCEPT 期望在套接字上有外来q接时接到通知
FD_CONNECT 期望在套接字q接建立完成时接到通知
FD_CLOSE 期望在套接字关闭时接到通知
例如Q我们要在套接字d备好或写准备好时接到通知Q语句如下:
rc=WSAAsyncSelect(s,hWnd,wMsg,FD_READ|FD_WRITE);
如果我们需要注销对套接字|络事g的消息发送,只要?lEvent 讄?
2、异步请求函?/strong>
?Berkeley Sockets 中请求服务是d的,WINDOWS SICKETS 除了支持q一cd数外Q还增加了相应的异步h函数——WSAAsyncGetXByY?
3、阻塞处理方?/strong> WINDOWS 是非抢先的多d环境Q即若一个程序不d攑ּ其控制权Q别的程序就不能执行。因此在设计Windows Sockets E序Ӟ管pȝ支持d操作Q但q是反对E序员用该操作。但׃ SUN 公司下的 Berkeley Sockets 的套接字默认操作是阻塞的QWINDOWS 作ؓUL?SOCKETS 也不可避免对q个操作支持?/p>
在Windows Sockets 实现中,对于不能立即完成的阻塞操作做如下处理QDLL初始?#8594;循环操作。在循环中,它发送Q?WINDOWS 消息Qƈ查这?Windows Sockets 调用是否完成Q在必要Ӟ它可以放弃CPU让其它应用程序执行(当然使用线E的CPU׃会有q个ȝ了^_^Q。我们可以调?WSACancelBlockingCall() 函数取消此阻塞操作?/p>
?Windows Sockets 中,有一个默认的d处理例程 BlockingHook() 单地获取q发?WINDOWS 消息。如果要对复杂程序进行处理,Windows Sockets 中还?WSASetBlockingHook() 提供用户安装自己的阻塞处理例E能力;与该函数相对应的则是 WSAUnhookBlockingHook()Q它用于删除先前安装的Q何阻塞处理例E,q新安装默认的处理例程。请注意Q设计自qd处理例程Ӟ除了函数 WSACancelBlockingHook() 之外Q它不能使用其它?Windows Sockets API 函数。在处理例程中调?WSACancelBlockingHook()函数取消处于阻塞的操作Q它结束阻塞@环?/p>
4、出错处?/strong> Windows Sockets Z和以后多U程环境QWINDOWS/UNIXQ兼容,它提供了两个出错处理函数来获取和讄当前U程的最q错误号。(WSAGetLastEror()和WSASetLastError()Q?/p>
5、启动与l止 使用函数 WSAStartup() ?WSACleanup() 启动和终止套接字?/p>
六、Windows Sockets|络E序设计核心 我们l于可以开始真正的 Windows Sockets |络E序设计了。不q我们还是先看一看每?Windows Sockets |络E序都要涉及的内宏V让我们一步步慢慢走?/p>
1、启动与l止 在所有Windows Sockets函数中,只有启动函数WSAStartup()和终止函数WSACleanup()是必M用的。启动函数必LW一个用的函数Q而且它允许指?Windows Sockets API 的版本,q获?SOCKETS的特定的一些技术细节。本l构如下Q?/p>
int PASCAL FAR WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData); 其中 wVersionRequested 保证 SOCKETS 可正常运行的 DLL 版本Q如果不支持Q则q回错误信息。我们看一下下面这D代码,看一下如何进?WSAStartup() 的调用: 关闭函数使用ӞM打开q已q接?SOCK_STREAM 套接字被复位Q但那些已由 closesocket() 函数关闭的但仍有未发送数据的套接字不受媄响,未发送的数据仍将被发送。程序运行时可能会多ơ调?WSAStartuo() 函数Q但必须保证每次调用时的 wVersionRequested 的值是相同的?/p>
2、异步请求服?/strong> Windows Sockets 除支?Berkeley Sockets 中同步请求,q增加了了一cd步请求服务函?WSAAsyncGerXByY()。该函数是阻塞请求函数的异步版本。应用程序调用它Ӟ?Windows Sockets DLL 初始化这一操作q返回调用者,此函数返回一个异步句柄,用来标识q个操作。当l果存储在调用者提供的~冲区,q且发送一个消息到应用E序相应H口。常用结构如下: 需要注意的是,׃ Windows 的内存对像可以设|ؓ可移动和可丢弃,因此在操作内存对象是Q必M?WIindows Sockets DLL 对象是可用的? 3、异步数据传?/strong> 使用 send() ?sendto() 函数来发送数据,使用 recv() 或recvfrom() 来接收数据。Windows Sockets 不鼓q户用阻塞方式传输数据,因ؓ那样可能会阻塞整?Windows 环境。下面我们看一个异步数据传输实例?/p>
假设套接?s 在连接徏立后Q已l用了函数 WSAAsyncSelect() 在其上注册了|络事g FD_READ ?FD_WRITEQƈ?wMsg gؓ UM_SOCKQ那么我们可以在 Windows 消息循环中增加如下的分支语句Q?/p>
4、出错处?/strong> Windows 提供了一个函数来获取最q的错误?WSAGetLastError()Q推荐的~写方式如下Q? 一Q快速生成一个DLL 哦,对了Q讲解还是用VC++ 6.0Q简UVC6Q来讲解。在VC6下new一?#8220;Win32 Dynamic-Link Library”的ProjectQ就?#8220;dllTest”吧,注意不要选择“MFC AppWizard(dll)”Q因?#8230;…q不属于本文的内容,其实最重要的是Q我不太懂(^_^Q,然后呢,选择“An empty DLL project”卛_?/p>
接下来就是创Zq两个文Ӟq添加到Project中去Q?/p>
lib.h lib.cpp BuildQ(q么单的E序不会通不q吧Q)你就能看C的生成目录下有个文gQ叫“dllTest.dll”Q这是我们W一个dllQ没几行代码Q简单吧Q但麻雀虽小五脏俱全Q我们用Visual Studio提供的工?#8220;Depends”打开q个文g看看Q如图所C,你能看到我们导出的这个函敎ͼadd?/p>
q个微型的dll和普通程序的不同在于add函数的声明,多了一个__declspec(dllexport)Q这个是关键Q这个修饰符告诉VC6Q这个函数需要导出,而前面的extern "C"的意思是q个函数是以C语言的标准来调用的,如果不加extern "C"的话Q会怎么样呢Q自p试看吧,Build后再用Depends看看生成的dll?br> 二、用这个DLL 用VC6的向导创Z?#8220;Win32 Console Application”的ProjectQ叫“CallDllTest”Q然后选择“"A Hello, Word!"Application”Qؓ什么用ConsoleQ简单呗?/p>
把CallDllTest.cpp的内Ҏ成以下: BuildQ然?lt;Ctrl>+<F5>q行Q就能看到结果了Q打C一?#8220;5”出来Q说明正怺Q有炚w要说明的是你得把前面生的那个DLL和这ơ生成的EXE攑֜同一目录下方可。代码很单,思\是定义函数指针cdQ加载DLLQ获取要调用的函数的指针Q然后调用,q么一个过E?/p>
三、静态链?/strong> 刚才使用LoadLibraryq个API加蝲DLL的方式叫动态链接,现在来介l静态链接,其实静态链接用得还更多Q只不过你不一定注意到而已Q不信的话现在查看一下你刚创建的CallDllTestq个Project的Project SettingsQ如图: 那如何静态用前面生成的那个dllTest.dll呢?回到刚才dllTest.dll的那个生成目录,你会发现一个叫dllTest.lib的文Ӟq就是我前面提到“导入?#8221;Q现在改一下CallDllTest.cpp?br>CallDllTest.cpp dllTest.libq个文g得复制到CallDllTestq个Project的目录下Q否则会报找不到文gQ执行结果如何?跟刚才是一LQ这U方式就叫静态链接,区别是什么?不需要在q行时调用LoadLibrary了,我个得静态链接用得更多一些?/p>
注意看代码: q句话很关键Q__declspec(dllimport)对应了DllTest中的__declspec(dllexport)Q表C函数从dll中导入,那你也许要问了:“我怎么知道q个dll中有q个函数Qƈ且还知道q个函数的参数类型和q回值类型?该不会也是用Dependsȝ吧?”?#8230;…q怎么说呢Q如果这个dll是你写的Q你当然知道啦,但如果这个Dll不是你写的话Q它的作者往往会提供一个头文gQ就好像你要使用LoadLibraryQ你得包?#8220;windows.h”q个头文件一P否则出现符h定义的编译错误,那么我们改一下lib.hq个头文件?/p>
lib.h 在DllTestq个Project中,DLLTEST_EXPORTS是被定义了的Q如图: 所以用dllexportQ而在别的Project中,则用dllimport。在CallDllTest中includeq个lib.hQ就可以了,当然你也可以写得更好Q我q里仅仅是demo?/p>
四、DLL中的main函数 大家都知道C语言的程序是从main开始的Q到了Windows环境下,则换成了WinMainQ但也差不多Q那DLL有没有类似的入口呢?{案是肯定的Q我们来改一下DllTest的lib.cpp?/p>
lib.cpp 你能看到一个叫“DllMain”的函敎ͼ它就是dll的入口,ohQ当然了Q我q篇文章所讲的都是针对Windows操作pȝ的,Linux的可不一样哦Q甚至一般来_Linux的DLL都不?#8220;DLL”?/p>
好了Q你再按照前面的Ҏ去调用下q个DLLQ(记得拯q个dll到相应目录去Q这时候你p看到执行l果中多?#8220;process attach of DLL”?#8220;process detach of DLL”Q这是很显而易见的Q一个进E连接和断开q接到这个dll的时候,DllMain׃被调用,且传递的ul_reason_for_call参数分别是DLL_PROCESS_ATTACH和DLL_PROCESS_DETACHQ那什么时候会?#8220;DLL_THREAD_ATTACH”?#8220;DLL_THREAD_DETACH”Q当库已l加载,创徏新的U程和销毁线E的时候,会分别用THREAD_ATTACH和THREAD_DETACH参数来调DllMain?/p>
五、调用方?/strong> 前面我们导入Q导出函数的时候都加了一?#8220;extern "C"”Q那不加会怎么样呢Q如果再涉及到这几个修饰W:__stdcallQ__cdecl和__fastcallQ那又会怎样呢?我画了两个表Q大家可以比较下Q体会下?/p>
q是三种不同调用方式的比较: q是命名修饰在不同方式下的比较: 如果dll和exe的命名理解不一_有可能出错。通常来说Q我是习惯于?#8220;extern "C"”?#8220;__cdecl”的组合?/p>
六、导出变?/strong> 前面只说了如何导出函敎ͼ那如何导Z个变量呢Q方法类|甚至可以说几乎一P看代码: lib.cpp lib.h q样Q就可以了,和导出函数是没什么差别吧Q同P你也可以用Depends观察生成的DLLQ你会发现iExportIntq个导出W号Q也是我们导出的这个变量了。现在看CallDllTest的代码: CallDllTest.cpp 输出是什么?112?13Q说明成功了。不知道你这个时候有没有惛_一个问题,那就是如果两个进E同时调用dllTest.dllQ而且同时修改和读取iExportIntQ那会不会ؕ掉呢Q要不要做一个互斥锁呢?{案是:不会Q不需要。这得益于Windows内存理的一个底层实现技术,叫Copy-on-writeQ在调用执行“iExportInt++”的时候,其实q真正修改了DLL中的|而是做了一份拷贝,通过内存映射机制Q得程序访问的那个“iExportInt”其实是那份拷贝,而另一q程使用的也是自q拯Q互不干涉?/p>
七、共享内?/strong> 紧接着前面q个问题Q那如果我企N过DLL来让不同q程׃n一D内存,而不是让pȝ执行默认的Copy-on-write操作Q那怎么办呢Q有办法Q这是Microsoft提供的一个方法,个h觉得是个不错的进E间通信的方法,比如两个q程需要交换一大块数据Q而且q一大块数据变化比较频繁Q通过数据库啊Q文件啊Q就昑־有点慢,如果通过socket啊,道啊,显得有些不直接Q还是直接用共享内存来得直接,但很多hq不知道q个功能Q我q里跟大家分享下?/p>
lib.h lib.cpp Build一下,然后用Depends看看输出情况。现在来改CallDllText.cpp。这里有非常要注意的两个地方Q一?#8220;={0}”q个初始化,q是必须的,否则׃n不起作用,Microsoft规定了共享段一定需要先初始化,哪怕只是给W一个元素赋个随便什么值都好,如这个例子,我只是给W一个元素赋g?Q另一处要注意的是“/SECTION:shared,RWS"q个字符Ԍ中间可别要有I格Q否则你同样会发现共享不起作用,我当时调试就很郁P因ؓ我习惯在英文逗号后加个空根{?/p>
CallDllText.cpp Sleep(10000)会让E序堵塞10U钟Q这样就可以q行多个E序的副本,来观察共享的效果?/p>
八、结?/strong> DLL늛的知识面相当q,本文只是入门的文章,介绍了一些比较实用的内容而已Q如果要q一步学习,需要看看《Windows核心~程》这U经典著作,关于DLL的很多内Ҏ都没有提及到Q比如DLL的导出方法其实有好几U,我介l的只是其中一U,但我认ؓ我介l的Ҏ是最好用而且是最单的。我们写E序Q是Z实现某些应用Q而不是ؓ了炫耀某些技术,所以我是偏向于使用成熟Q可靠和易行的方法?br> 除开打印和前面的条g判断外,真的没几行,而且只用了一个非常有限的q代Q效率自然是很高的?/p>
q是表示Q如果生成调试版本,要指C当前文件的名称。__FILE__是一个宏Q在~译器编译过E中l它赋gؓ当前正在~译的文件名U?br>
Windows Sockets Z实现当一个应用程序的套接字调用处于阻塞时Q能够放弃CPU让其它应用程序运行,它在调用处于d时便q入一个叫“HOOK”的例E,此例E负责接收和分配WINDOWS消息Q得其它应用程序仍然能够接收到自己的消息ƈ取得控制权?/p>
WSADATA wsaData; //定义数据信息变量
int err; //定义错误号变?/span>
wVersionRequested = MAKEWORD(1,1);//l版本信息赋?/span>
err = WSAStartup(wVersionRequested, &wsaData);//l错误信息赋?/span>
if(err!=0)
{
return;//告诉用户找不到合适的版本
}
//认 Windows Sockets DLL 支持 1.1 版本
//DLL 版本可以高于 1.1
//pȝq回的版本号始终是最低要求的 1.1Q即应用E序与DLL 中可支持的最低版本号
if(LOBYTE(wsaData.wVersion)!= 1|| HIBYTE(wsaData.wVersion)!=1)
{
WSACleanup();//告诉用户找不到合适的版本
return;
}
//Windows Sockets DLL 被进E接受,可以q入下一步操?/span>
char hostname="rs6000";
taskHnd = WSAAsyncBetHostByName(hWnd,wMsg,hostname,buf,buflen);
switch(lParam)
{
case FD_READ:
len = recv(wParam,lpBuffer,length,0);
break;
case FD_WRITE:
while(send(wParam,lpBuffer,len,0)!=SOCKET_ERROR)
break;
}
break;
if((len==SOCKET_ERROR)&&(WSAGetLastError()==WSAWOULDBLOCK)){}
#define LIB_H
extern "C" int __declspec(dllexport) add(int x,int y);
#endif
int add(int x,int y)
{
return x + y;
}
CallDllTest.cpp
#include "windows.h"
typedef int ( * lpAddFun)(int,int);
int main(int argc, char* argv[])
{
HINSTANCE hDll; //DLL handle
lpAddFun addFun; //Function pointer
hDll = LoadLibrary("dllTest.dll");
if (hDll != NULL)
{
addFun = (lpAddFun)GetProcAddress(hDll,"add");
if(addFun!=NULL)
{
int result = addFun(2,3);
printf("%d",result);
}
FreeLibrary(hDll);
}
return 0;
}
注意我画U线的这个地方,Kernel32.libQuser32.lib和gdi32.lib{,q些lib叫做“导入?#8221;Qƈ不包含真正有效的代码Q真正有效的代码是存攑֜DLL中的Q刚才我们所使用的LoadLibraryq个APIQ其实就是通过Kernel32.lib链接到Kernel32.dll中去的,也就是说LoadLibrary的实现存在于Kernel32.dll中,而我们是通过Kernel32.lib来找到它的,如果q有兴趣的话Q可以用Depends看看Kernel32.dllQ看看里面是否有LoadLibraryQ里面函数很多,别看q了哦Q这可是Windows的核心库之一Q但可能你ƈ没有扑ֈLoadLibraryQ而是扑ֈ了,LoadLibraryA和LoadLibraryWQ这很正常,因ؓ很多需要用到字符串的APIQ都有两个版本,一个是H字W版Q一个是宽字W版Q在E序~译链接的时候,~译器会Ҏ你的选项来跟你选择其中一个,q里暂时不展开了?/p>
#pragma comment(lib, "dllTest.lib")
extern "C" int __declspec(dllimport) add(int x,int y);
int main(int argc, char* argv[])
{
int result = add(2,3);
printf("%d",result);
return 0;
}
#define LIB_H
#ifdef DLLTEST_EXPORTS
extern "C" int __declspec(dllexport) add(int x,int y);
#else
extern "C" int __declspec(dllimport) add(int x,int y);
#endif
#endif
#include "windows.h"
#include "stdio.h"
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
printf("\nprocess attach of DLL");
break;
case DLL_THREAD_ATTACH:
printf("\nthread attach of DLL");
break;
case DLL_THREAD_DETACH:
printf("\nthread detach of DLL");
break;
case DLL_PROCESS_DETACH:
printf("\nprocess detach of DLL");
break;
}
return TRUE;
}
int add(int x,int y)
{
return x + y;
}
#include "windows.h"
#include "stdio.h"
int iExportInt;
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
iExportInt = 112;
break;
}
return TRUE;
}
#define LIB_H
#ifdef DLLTEST_EXPORTS
extern "C" int __declspec(dllexport) iExportInt;
#else
extern "C" int __declspec(dllimport) iExportInt;
#endif
#endif
#include "lib.h"
#pragma comment(lib, "dllTest.lib")
int main(int argc, char* argv[])
{
printf("%d\n", iExportInt);
iExportInt++;
printf("%d\n", iExportInt);
return 0;
}
#define LIB_H
#ifdef DLLTEST_EXPORTS
extern "C" __declspec(dllexport) unsigned char dataChunk[100000];
#else
extern "C" __declspec(dllimport) unsigned char dataChunk[100000];
#endif
#endif
#pragma data_seg("shared")
unsigned char dataChunk[100000]={0};
#pragma data_seg()
#pragma comment(linker, "/SECTION:shared,RWS")
#include "windows.h"
#include "lib.h"
#pragma comment(lib, "dllTest.lib")
int main(int argc, char* argv[])
{
printf("%d\n", dataChunk[0]++);
Sleep(10000);
return 0;
}
作ؓ工程师,l常需要画些图表曲U,那刻度就成了个问题,下面我D两个例子说说q个问题Q?br>数字pd1Q?.12Q?.4Q?.3Q?.16Q?.5Q?.9Q?1Q?2Q?.6
L曲线Q用ExcelQ很单,你很快就弄好了,如图Q?br>
数字pd2Q?234321.1Q?234331.1Q?234343.1Q?234123.1Q?234356.1Q?234567.1Q?234399.1Q?234300.1
同样Q用ExcelL曲线Q如下图Q?br>
注意U坐标的dQ我说的是q个问题Q如果是你,如何来标q个dQ?br>
一般来_我们日常生活中看到的d都是?Q?.5Q?Q?0为间隔的Q如果给Z一个最?.55Q一个最大?.13Q要求画出大U?个刻度,那应该怎么画?如果是我Q大概就dq几个:4.4Q?.6Q?.8Q?.0Q?.2?br>
那这栯不行呢?(5.13-4.55)/5=0.116Q于是画几个dQ?.55Q?.666Q?.782Q?.898Q?.014Q?.13。当然也可以Q但q个法未免太简单,d来的d不好看?br>
那我们如何来描述我上面提到的?Q?.5Q?Q?0为间隔的划分法呢?我一直感觉很困难Q直C天动手去写,q且把它写出来了Q才发现也就那么几行代码Q我把我的代码脓出来与大家分享,我相信我q个法l过E微的修改就能适应所有的坐标d划分?br>
代码实在单,我不写什么注释了Q我觉得q是我写的最_悍的小E序之一?br>
#include <math.h>
void RegulateAll(double dMin, double dMax, int iMaxAxisNum);
int main(int argc, char* argv[])
{
RegulateAll(2, 45, 7);
RegulateAll(0.12, 0.43, 5);
RegulateAll(10001, 10002, 5);
RegulateAll(-12.65, 56.3, 6);
RegulateAll(5243217834.12, 5378234435.12, 5);
RegulateAll(-0.043, 0.099, 8);
RegulateAll(-123431, -67325, 10);
RegulateAll(0.0, 0.0, 5);
return 0;
}
void RegulateAll(double dMin, double dMax, int iMaxAxisNum)
{
if(iMaxAxisNum<1 || dMax<dMin)
return;
double dDelta = dMax - dMin;
if(dDelta<1.0) //Modify this by your requirement.
{
dMax+=(1.0-dDelta)/2.0;
dMin-=(1.0-dDelta)/2.0;
}
dDelta = dMax - dMin;
int iExp = (int)(log(dDelta)/log(10.0))-2;
double dMultiplier = pow(10, iExp);
const double dSolutions[] =
{1, 2, 2.5, 5, 10, 20, 25, 50, 100, 200, 250, 500};
int i;
for(i=0; i<sizeof(dSolutions)/sizeof(double); i++)
{
double dMultiCal = dMultiplier * dSolutions[i];
if(((int)(dDelta/dMultiCal) + 1)<=iMaxAxisNum)
{
break;
}
}
double dInterval = dMultiplier * dSolutions[i];
double dStartPoint = ((int)ceil(dMin/dInterval) - 1) * dInterval;
int iAxisIndex;
for(iAxisIndex=0; 1; iAxisIndex++)
{
printf("%f", dStartPoint+dInterval*iAxisIndex);
if(dStartPoint+dInterval*iAxisIndex>dMax)
break;
printf(" | ");
}
printf("\n");
}
一、预~译头文件说?/span>
所谓头文g预编译,是把一个工E?Project)中用的一些MFC标准头文?如Windows.H、Afxwin.H)预先~译Q以后该工程~译Ӟ不再~译q部分头文gQ仅仅用预~译的结果。这样可以加快编译速度Q节省时间?br>
预编译头文g通过~译stdafx.cpp生成Q以工程名命名,׃预编译的头文件的后缀?#8220;pch”Q所以编译结果文件是projectname.pch?br>
~译器通过一个头文gstdafx.h来用预~译头文件。stdafx.hq个头文件名是可以在project的编译设|里指定的。编译器认ؓQ所有在指o#include "stdafx.h"前的代码都是预编译的Q它跌#include "stdafx. h"指oQ用projectname.pch~译q条指o之后的所有代码?br>
因此Q所有的CPP实现文gW一条语句都是:#include "stdafx.h"?br>
另外Q每一个实现文件CPP都包含了如下语句Q?/p>
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
vc.net默认情况下用预~译?/Yu)Q不明白的在加入?h文g后编译时d现fatal error C1010: 在查N~译头指令时遇到意外的文件结错误。解x法是在在include头文件的地方加上#include "stdafx.h",或者打目属性,扑ֈ“C/C++”文g夹,单击“预编译头”属性页。修?#8220;创徏/使用预编译头”属性ؓ“不用预~译?#8221;?br>
二、C/C++头文件一?/span>
C、传l?C++
#include <assert.h> //讑֮插入?br>#include <ctype.h> //字符处理
#include <errno.h> //定义错误?br>#include <float.h> //点数处?br>#include <fstream.h> //文g输入Q输?br>#include <iomanip.h> //参数化输入/输出
#include <iostream.h> //数据输入/输出
#include <limits.h> //定义各种数据cd最值常?br>#include <locale.h> //定义本地化函?br>#include <math.h> //定义数学函数
#include <stdio.h> //定义输入Q输出函?br>#include <stdlib.h> //定义杂项函数及内存分配函?br>#include <string.h> //字符串处?br>#include <strstrea.h> //Z数组的输入/输出
#include <time.h> //定义关于旉的函?br>#include <wchar.h> //宽字W处理及输入Q输?br>#include <wctype.h> //宽字W分c?br>
标准 C++ Q同上的不再注释Q?br>#include <algorithm> //STL 通用法
#include <bitset> //STL 位集容器
#include <cctype>
#include <cerrno>
#include <clocale>
#include <cmath>
#include <complex> //复数c?br>#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque> //STL 双端队列容器
#include <exception> //异常处理c?br>#include <fstream>
#include <functional> //STL 定义q算函数Q代替运符Q?br>#include <limits>
#include <list> //STL U性列表容?br>#include <map> //STL 映射容器
#include <iomanip>
#include <ios> //基本输入Q输出支?br>#include <iosfwd> //输入Q输出系l用的前置声明
#include <iostream>
#include <istream> //基本输入?br>#include <ostream> //基本输出?br>#include <queue> //STL 队列容器
#include <set> //STL 集合容器
#include <sstream> //Z字符串的?br>#include <stack> //STL 堆栈容器
#include <stdexcept> //标准异常c?br>#include <streambuf> //底层输入Q输出支?br>#include <string> //字符串类
#include <utility> //STL 通用模板c?br>#include <vector> //STL 动态数l容?br>#include <cwchar>
#include <cwctype>
using namespace std;
C99 增加
#include <complex.h> //复数处理
#include <fenv.h> //点环境
#include <inttypes.h> //整数格式转换
#include <stdbool.h> //布尔环境
#include <stdint.h> //整型环境
#include <tgmath.h> //通用cd数学?br>
三、预处理的由?/span>
在C++的历史发展中Q有很多的语a特征Q特别是语言的晦涩之处)来自于C语言Q预处理是其中的一个。C++从C语言那里把C语言预处理器l承q来QC语言预处理器Q被Bjarne博士(C++之父)UCؓCppQ不知道是不是C Program Preprocessor的简U?br>
四、常见的预处理功?/span>
预处理器的主要作用就是把通过预处理的内徏功能对一个资源进行等h换,最常见的预处理有:文g包含Q条件编译、布局控制和宏替换4U?br>
1Q文件包含:#include 是一U最为常见的预处理,主要是做为文件的引用l合源程序正文?br>2Q条件编译:#if,#ifndef,#ifdef,#endif,#undef{也是比较常见的预处理,主要是进行编译时q行有选择的挑选,注释掉一些指定的代码Q以辑ֈ版本控制、防止对文g重复包含的功能?br>3Q布局控制Q?pragmaQ这也是我们应用预处理的一个重要方面,主要功能是ؓ~译E序提供非常规的控制信息?br>4Q宏替换Q?defineQ这是最常见的用法,它可以定义符号常量、函数功能、重新命名、字W串的拼接等各种功能?br>
五、预处理指o
预处理指令的格式如下Q?br>
#directive tokens
#W号应该是这一行的W一个非I字W,一般我们把它放在v始位|。如果指令一行放不下Q可以通过“\”q行控制Q例如:
#define Error if(error) exit(1)
{h?br>
#define Error \
if(error) exit(1)
下面我们看一下常见的预处理指令:
#define 宏定?br>#undef 未定义宏
#include 文本包含
#ifdef 如果宏被定义p行编?br>#ifndef 如果宏未被定义就q行~译
#endif l束~译块的控制
#if 表达式非零就对代码进行编?br>#else 作ؓ其他预处理的剩余选项q行~译
#elif q是一U?else?if的组合选项
#line 改变当前的行数和文g名称
#error 输出一个错误信?br>#pragma 为编译程序提供非常规的控制流信息
下面我们对这些预处理q行一一的说明,考虑到宏的重要性和J琐性,我们把它攑ֈ最后讲?br>
六、文件包含指?br>
q种预处理用方式是最为常见的Q^时我们编写程序都会用刎ͼ最常见的用法是Q?br>#include <iostream> //标准库头文g
#include <iostream.h> //旧式的标准库头文?br>#include "IO.h" //用户自定义的头文?br>#include "../file.h" //UNIX下的父目录下的头文g
#include "/usr/local/file.h" //UNIX下的完整路径
#include "..\file.h" //Dos下的父目录下的头文g
#include "\usr\local\file.h" //Dos下的完整路径
Q其实DOS/Windows的目录分隔也可以用斜杠,Z和Unix兼容Q就l一用斜杠吧Q?br>
q里面有2个地方要注意Q?br>
1、我们用<iostream>q是<iostream.h>?
我们d使用<iostream>Q而不?lt;iostream.h>,Z么呢Q我想你可能q记得我曄l出q几点理由,q里我大致的说一下:
首先Qh格式的头文g早在98q?月䆾p标准委员会抛弃了Q我们应该紧跟标准,以适合时代的发展。其ơ,iostream.h只支持窄字符集,iostream则支持窄/宽字W集。还有,标准对iostream作了很多的改动,接口和实现都有了变化。最后,iostreamlg全部攑օnamespace std中,防止了名字污染?br>
2?lt;io.h>?io.h"的区别?
其实他们唯一的区别就是搜索\径不同:对于#include <io.h> Q编译器从标准库路径开始搜索对?include "io.h" Q编译器从用L工作路径开始搜索?br>
七、编译控制指?/span>
q些指o的主要目的是q行~译时进行有选择的挑选,注释掉一些指定的代码Q以辑ֈ版本控制、防止对文g重复包含的功能。用格式,如下Q?br>
1?ifdef identifier
如果identifierZ个定义了的符Pyour code׃被编译,否则剔除?br>#ifdef identifier
your code
#endif
2?ifndef identifier
如果identifierZ个未定义的符Pyour code׃被编译,否则剔除?br>#ifndef identifier
your code
#endif
3?if expression
如果expression非零Qyour code׃被编译,否则剔除?br>#if expression
your code
#endif
4?ifdef identifier
如果identifierZ个定义了的符Pyour code1׃被编译,否则your code2׃被编译?br>#ifdef identifier
your code1
#else
your code2
#endif
5?else?elif
如果epression1非零Q就~译your code1Q否则,如果expression2非零Q就~译your code2Q否则,q译your code3?br>#if expressin1
your code1
#elif expression2 //呵呵Qelif
your code2
#else
your code3
#enif
其他预编译指令除了上面我们说的集中常用的~译指oQ还?U不太常见的~译指oQ?line?error?pragmaQ我们接下来q单的谈一下?br>
6Q?line
语法如下Q?br>
#line number filename
例如Q?br>
#line 30 a.h
其中Q文件名a.h可以省略不写。这条指令可以改变当前的行号和文件名Q例如上面的q条预处理指令就可以改变当前的行号ؓ30Q文件名是a.h。初看v来似乎没有什么用Q不q,他还是有点用的,那就是用在编译器的编写中Q我们知道编译器对C++源码~译q程中会产生一些中间文Ӟ通过q条指oQ可以保证文件名是固定的Q不会被q些中间文g代替Q有利于q行分析?br>
7Q?error
语法如下Q?br>
#error info
例如Q?br>
#ifndef UNIX
#error This software requires the UNIX OS.
#endif
q条指o主要是给出错误信息,上面的这个例子就是,如果没有在UNIX环境下,׃输出This software requires the UNIX OS.然后诱发~译器终止。所以ȝ来说Q这条指令的目的是在出现其它编译错误之前能够给Z定的信息?br>
8Q?pragma
它是非统一的,他要依靠各个~译器生产者。例如VC++中:
#pragma comment(lib,"dllTest.lib")
导入库dllTest.lib?br>
八、预定义标识W?/span>
Z处理一些有用的信息Q预处理定义了一些预处理标识W,虽然各种~译器的预处理标识符不尽相同Q但是他们都会处理下面的4U:
__FILE__ 正在~译的文件的名字
__LINE__ 正在~译的文件的行号
__DATE__ ~译时刻的日期字W串Q例如: "25 Dec 2000"
__TIME__ ~译时刻的时间字W串Q例如: "12:30:55"
例如Qcout<<"The file is :"<<__FILE__"<<"! The lines is:"<<__LINE__<<endl;
九、预处理何去何从
如何取代#include预处理指令,我们在这里就不再一一讨论了。C++q没有ؓ#include提供替代形式Q但是namespace提供了一U作用域机制Q它能以某种方式支持l合Q利用它可以改善#include的行为方式,但是我们q是无法取代#include?br>
#pragma应该是一个可有可无的预处理指令,按照Bjarne的话_是Q?#8220;#pragma被过分的l常的用于将语言语义的变形隐藏到~译pȝ里,或者被用于提供带有Ҏ语义和笨拙语法的语言扩充?#8221;
对于#ifdefQ我们仍然束手无{,q是我们利用if语句和常量表辑ּQ仍然不以替代它,因ؓ一个if语句的正文必d语法上正,满cL查,即他处在一个绝不会被执行的分支里面?br>
十、预~译头文件的补充说明
q里介绍VC6的预~译功能的用,׃预编译详l用比较的复杂Q这里只介绍几个最重要的预~译指o: /Yu, /Yc,/Yx,/Fp。其它的详细资料可以参?
MSDN -> Visual Studio 6.0 Document -> Visual C++ 6.0 Document -> VC++ Programmer Guider -> Compiler and Linker -> Details -> Creating Precompiled Header files
预编译头的概念:
所谓的预编译头是把一个工E中的那一部分代码Q预先编译好攑֜一个文仉Q通常是以.pch为扩展名的)Q这个文件就UCؓ预编译头文gq些预先~译好的代码可以是Q何的C/C++代码Q甚xinline的函敎ͼ但是必须是稳定的Q在工程开发的q程中不会被l常改变。如果这些代码被修改Q则需要重新编译生成预~译头文件。注意生成预~译头文件是很耗时间的。同时你得注意预~译头文仉常很大Q通常?-7M大。注意及时清理那些没有用的预~译头文件?br>
也许你会问:现在的编译器都有Time stamp的功能,~译器在~译整个工程的时候,它只会编译那些经q修改的文gQ而不会去~译那些从上ơ编译过Q到现在没有被修改过的文件。那么ؓ什么还要预~译头文件呢Q答案在q里Q我们知道编译器是以文g为单位编译的Q一个文件经q修改后Q会重新~译整个文gQ当然在q个文g里包含的所有头文g中的东西Q?eg Macro, Preprocessor Q都要重新处理一遍。VC的预~译头文件保存的正是q部分信息。以避免每次都要重新处理q些头文件?br>
Ҏ上文介绍Q预~译头文件的作用当然是提高便宜速度了,有了它你没有必要每次都编译那些不需要经常改变的代码。编译性能当然提高了。但Ҏ我的实践l验Q用VC6~译器,在编译一个小E序的时候,用不用预~译头文Ӟ基本看不出编译速度的差距,可能对于一个大型程序来_q种~译速度差才能体现出来?br>
要用预~译_我们必须指定一个头文gQ这个头文g包含我们不会l常改变的代码和其他的头文gQ然后我们用q个头文件来生成一个预~译头文Ӟ.pch文gQ想必大安知道 StdAfx.hq个文g。很多h都认是VC提供的一?#8220;pȝU别”的,~译器带的一个头文g。其实不是的Q这个文件可以是M名字的。我们来考察一个典型的由AppWizard生成的MFC Dialog BasedE序的预~译头文件。(因ؓAppWizard会ؓ我们指定好如何用预~译头文Ӟ默认的是StdAfx.hQ这是VCL名字Q。我们会发现q个头文仉包含了以下的头文Ӟ
#include <afxwin.h> // MFC core and standard components
#include <afxext.h> // MFC extensions
#include <afxdisp.h> // MFC Automation classes
#include <afxdtctl.h> // MFC support for Internet Explorer 4 Common Controls
#include <afxcmn.h>
q些正是使用MFC的必d含的头文Ӟ当然我们不太可能在我们的工程中修改这些头文g的,所以说他们是稳定的?br>
那么我们如何指定它来生成预编译头文g。我们知道一个头文g是不能编译的。所以我们还需要一个cpp文g来生?pch 文g。这个文仉认的是StdAfx.cpp。在q个文g里只有一句代码就是:#include “Stdafx.h”。原因是理所当然的,我们仅仅是要它能够编译而已―――也是_要的只是它的.cpp的扩展名。我们可以用/Yc~译开x指定StdAfx.cpp来生成一?pch文gQ通过/Fp~译开x指定生成的pch文g的名字。打开project ->Setting->C/C++ 对话框。把Category指向Precompiled Header。在左边的树形视N选择整个工程Q如下图Q?br>
Q图Q)
在图中我们的Project Options(右下角的那个白的地方)可以看到 /Fp “debug/PCH.pch”Q这是指定生成?pch文g的名字,默认的通常是 <工程?gt;.pchQ我的示例工E名是PCHQ?br>
然后Q在左边的树形视N选择StdAfx.cpp.如图Q?br>
Q图Q)
q时原来的Project Option变成?Source File OptionQ原来是工程Q现在是一个文Ӟ当然变了Q。在q里我们可以看到 /Yc开养I/Yc的作用就是指定这个文件来创徏一个Pch文g?Yc后面的文件名是那个包含了E_代码的头文gQ一个工E里只能有一个文件的可以有YC开兟뀂VC根据这个选项?StdAfx.cpp~译成一个Obj文g和一个PCH文g?br>
然后我们再选择一个其它的文g来看看,如图Q?br>
Q图3Q?br>
在这里,Precomplier 选择?Use ………一,头文件是我们指定创徏PCH 文g的stdafx.h文g。事实上Q这里是使用工程里的讄Q(如图1Q?Yu "stdafx.h"?br>
q样Q我们就讄好了预编译头文g。也是_我们可以使用预编译头功能了。以下是注意事项Q?br>
1Q如果用了/YuQ就是说使用了预~译Q我们在每个.cpp文g的最开_我强调一遍是最开_包含你指定生pch文g?h文gQ默认是stdafx.hQ不然就会有问题。如果你没有包含q个文gQ就告诉你Unexpected file end. 如果你不是在最开头包含的Q你自己试以下就知道了,l对有很惊h的效果?br>
2Q如果你把pch文g不小心丢了,Ҏ以上的分析,你只要让~译器生成一个pch文g可以了。也是说把 stdafx.cppQ即指定/Yc的那个cpp文gQ重新编译一遍就可以了。当然你可以d?Rebuild all。简单一点就是选择那个cpp文gQ按一下Ctrl + F7可以了?/p>
1、GDI名字的意?br>GDI Graphic Device InterfaceQ我说不清和GUI有什么区别。可能一U针对设备,一U针对用戯言吧,反正以后都说GDIQ也是Windows的图形编E?br>
2、设备描qC下文HDC
Handle Device Context
q个是关键的关键Q可以理解成d的环境,何谓环境Q画板,U,W,寸Q字体,颜色Q区域等。反正包括你能想到的和未能想到的了。要d不同的图形,很大E度上是对HDC的修攏V?br>
3、HDC的创建方?br>WM_PAINT消息来的时候,用:
l束后ps区域自动变有效。还有:
q一寚w要调用ValidateRect()函数来得区域有效?/p>
q有别的办法Q但不常用,q里略过。不q有一个还是值得一_那就是取得Window HDC的办法,用它可以在Windows非客户区dQ?/p>
4、获取HDC信息
GetDeviceCaps()
其中比较重要的指标是Q?br>HORZRES - horizontal resolution
VERTRES - vertical resolution
BITSPIXEL - 色深
5、保存HDC与还原HDC
6、画囄图!
7、画WHPENQ改变线条)
Ҏ1Q?br>HPEN hPen = GetStockObject(ARGUMENTS);
ARGUMENTS为:BLACK_PEN 黑笔WHITE_PEN 白笔
Ҏ2Q?br>HPEN hPen = CreatePen(iPenStyle, iWidth, crColor);
iPenStyle为:PS_SOLID实线PS_DASH虚线PS_DOT点线PS_DASHDOT虚点UPS_DASHDOTDOT虚双点线PS_NULLI线PS_INSIDEFRAMEQ?
获取ȝ信息Q?/p>
选出HDC中的ȝQ?/p>
8、背景模?/span>
改变背景模式Q也是制定虚线间间隙的填充问题了:
有Set嘛,相应地有Get了,q里略过?br>
9、叠加模?/span>
10、画刷HBRUSHQ改变填充)
d能指明区域填充模式?br>Ҏ1Q?/p>
Ҏ2Q?/p>
11、多边Ş填充模式及画多边?/span>
12、区域HRGNQ绘画范_
区域合ƈ
注意QhDestRgn, hSrcRgn1, hSrcRgn2都必L效?/p>
使区域无?有效Q?/p>