小技巧、小经验(4)


1、InterlockedIncrement和InterlockedDecrement的妙用

一般来说,在多用户线程环境中,我们使用临界区、事件对象甚至互斥量来进行同步,尤其是临界区,可以很方便地对某些变量甚至代码块进行锁定执行,防止多线程之间资源恶性抢夺。既然如此,为啥微软又单独提供了专用于一个数值锁定计算的API函数InterlockedIncrement和InterlockedDecrement呢?他们又有什么特殊作用呢?
    恰好近段时间写了一个这方面的应用,帮我加深了对这类API函数的理解。
    首先描述一下需求,在应用中,有这样一个类,它可能只被实例化一次,也可能会被实例化多次,但不管被实例化了几次,它必须在构造函数里执行一项初始化计算,假设初始化计算的函数为WSAStartup,同时还需要在析构函数里执行一下注销计算,假设注销计算的函数为WSACleanup,现在有一个要求,就是它们的初始化和注销计算只能被执行一次,就如同在一个项目中,只能运行一次WSAStartup和WSACleanup一样。当然,大家可能会想到直接在工程的开始和结尾处实现这样的功能,但是,如果把这个类的文件包括在其它测试工程里进行测试,同时不改变其它工程的代码,又该如何实现呢?
    其实,我们可以充分利用InterlockedIncrement和InterlockedDecrement,就如同COM的CoInitialize()和CoUninitialize()一样,描述代码如下:

http://blog.csdn.net/rbagglo/article/details/5380078


2、windows判断用户是否在使用键盘鼠标的API

看《编程之美》1.10节,其中提了一个问题:windows是通过什么api了解用户是否在使用鼠标或键盘的?

有两个函数可以实现这个功能,它们都包含在头文件windows.h中。


1. BOOL GetInputState(VOID);
函数功能:该函数确定在当前线程的消息队列中是否有要处理的鼠标,键盘消息.如果检测到输入的话,则返回值为非零值,否则返回值为零

2.BOOL WINAPI GetLastInputInfo( __out PLASTINPUTINFO lpi);
函数功能:获取上次输入操作的时间
参数:[out] 类型:PLASTINPUTINFO结构是一个指向接收到最后一个输入事件时间的LASTINPUTINFO结构指针。
返回值:如果调用函数成功,返回值为非零。如果调用函数失败,返回值为零。
说明:先定义LASTINPUTINFO lpi;然后调用函数GetLastInputInfo(&lpi);获得lpi。调用函数GetLastInputInfo()以后, 结构成员lpi.dwTime 中的值并非上次输入事件发生以后的毫秒数。而是上次输入事件发生时的系统运行时间。相当于上次输入事件发生时执行了lpi.dwTime=::GetTickCount()。为获得上次输入事件发生以后的毫秒数需要执行语句::GetTickCount()-lpi.dwTime。

http://blog.csdn.net/plumlzm/article/details/12420639


3、拷贝构造函数的参数必须是引用类型。

如果将拷贝构造函数中的引用符号去掉&,编译将无法通过,出错的信息如下:

非法的复制构造函数第一个参数不应是“CClass

没有可用的复制构造函数或复制构造函数声明为“explicit

原因:

如果拷贝构造函数中的参数不是一个引用,即形如CClass(const CClass c_class),那么就相当于采用了传值的方式(pass-by-value),而传值的方式会调用该类的拷贝构造函数,从而造成无穷递归地调用拷贝构造函数。因此拷贝构造函数的参数必须是一个引用。


4、HANDLE的无效值:NULL还是INVALID_HANDLE_VALUE? 以及对HANDLE的RAII封装

打开/创建一个HANDLE而忘记close的情况时有发生。利用RAII的思想,将HANDLE封装为一个类,在其析构函数中进行close,是一个不错的方法。

ATL提供了一个CHandle类,但是提出了以下使用注意事项:

Some API functions will use NULL as an empty or invalid handle, while others use INVALID_HANDLE_VALUE. CHandle only uses NULL and will treat INVALID_HANDLE_VALUE as a real handle. If you call an API which can return INVALID_HANDLE_VALUE, you should check for this value before calling CHandle::Attach or passing it to the CHandle constructor, and instead pass NULL.

即:有些API将NULL作为无效的HANLDE,但有些则将INVALID_HANDLE_VALUE作为无效值。CHandle只使用NULL作为无效HANDLE,而将INVALID_HANLDE_VALUE视为一个真正的HANDLE.

看看相关定义:
HANDLE定义为:typedef void *HANDLE;(在WinNt.h中定义的更详细一些)
NULL定义为:#define NULL 0 (在stddef.h中定义的更详细一些)
INVALID_HANDLE_VALUE定义为:#define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1) . 其实就是补码表示的-1解释为
无符号数,就是0xFFFFFFFF.

比如,CreateThread返回的无效HANDLE是NULL,而CreateFile则以INVALID_HANDLE_VALUE表示无效HANDLE.因此使用返回
HANDLE的API需查看MSDN以保证安全.


下面这篇文章分析了HANDLE无效值不统一表示的历史原因以及相关注意:
Why are HANDLE return values so inconsistent?
那么,对HANDLE的封装怎么处理为好?
看看下例(http://stackoverflow.com/questions/13028872/proper-way-close-winapi-handles-avoiding-of-repeated-closing):



5、程序员的职业素养 Bob

1.不匆忙上线,测试充分后再上,即使到了交付期
2.分析自己容易遗漏的bug,检讨自己查bug的方法
3.让自己的项目灵活易于修改<= 时长改动它,发现不容易时,思考改进<=高覆盖的单元测试保证代码修改的信心
4.承诺的代价,加速的代价=>代码一片糟糕.
5.专业的回答,重于事实,不计较过去.
6.好代码的障碍:匆忙.
7.程序员的肯定承诺用语: 我会在XX前完成这个story=>在过程中如果感觉到突发困难,要提前警告,以寻求团队的帮助,避免最后down掉.
8.少用这些词语:试试,尽力,可能,需要,应当,单元,希望,
9.让我们和让我是不一样的,后者有强烈的责任感以及踏实感
10.如果story依赖别的团队,承诺前先理清依赖对象能否给予承诺
11.要精通每项技艺的话,关键都是要具备信心和出错感知能力
12.面对压力,要遵守自己的原则和纪律,压力越大,越要遵守,整洁,仔细流程,各方面考虑,先设计好再敲,不要犯最大的错误:胡来.可以找人帮助,及时沟通,避免意料之外的诧异.关注不确定点,防止返工
13.心烦意乱或者疲劳时千万不要编码.找点轻松的事做做.静一静
14.很多焦虑无法在一两个小时内解决,但是当影响工作时,把工作时间分块,分一块专门处理这件影响自己的焦虑事,即使不能解决,也能把后台进程终止,降低这个affect的优先级
15.专业人士善于合理分配时间,以确保工作时间段种尽可能富有成效.在业余时间,专门把影响因素解决掉,防止带入工作.
16.影响因素:焦虑,恐惧,沮丧,睡眠不足
17.任何事情,要想做得快,都离不开练习.尽可能快地重复编码/测试过程,要求你能迅速做出决定.这需要识别各种环境和问题,并懂得应付.
18.肌肉运动有助于改善心智注意力
19.即使拖了进度,上头也有看到,我在努力解决问题,并且有切实的进展,这才是他们需要的.一点也不生气.
20.最重要的是自己采取的方法是否是正确的=>要时刻审查,从整体想问题。


6、scanf,sscanf高级用法

最近遇到了解析配置的问题,用正规表达式感觉大题小做,使用sscanf因只会用基本用法,感觉功能不够,上网搜了下,解析起来不费吹灰之力,代码也很简洁。

原帖出处不详,网上到处是,我做了点修改

名称:
sscanf() - 从一个字符串中读进与指定格式相符的数据.
 
函数原型:
Int  sscanf( string str, string fmt, mixed var1, mixed var2 ... );
int  scanf( const char *format [,argument]... );
 
说明:
sscanf与scanf类似,都是用于输入的,只是后者以屏幕(stdin)为输入源,前者以固定字符串为输入源。
其中的format可以是一个或多个{%[*] [width] [size]type | ' ' | '\t' | '\n' | 非%符号}
 
注:
1、 * 亦可用于格式中, (即 %*d 和 %*s) 加了星号 (*) 表示跳过此数据不读入. (也就是不把此数据读入参数中)
2、{a|b|c}表示a,b,c中选一,[d],表示可以有d也可以没有d。
3、width表示读取宽度。 
4、参数的size: 常用的有hh表示单字节size,h表示2字节 size,其他详见man sscanf或msdn 
5、type :这就很多了,就是%s,%d之类。
 
控制字符 说明 
%c  一个单一的字符 
%d  一个十进制整数 
%i  一个整数 
%e, %f, %g 一个浮点数 
%o  一个八进制数 
%s  一个字符串 
%x  一个十六进制数 
%p  一个指针 
%n  一个等于读取字符数量的整数 
%u  一个无符号整数 
%[]  一个字符集 
%%  一个精度符


7、内存映射文件读写:

BOOL CMemFileDlg::LoadFile(CString strFileName)
{
 HANDLE hFile,hMapping;
 void *basePointer;
 if((hFile=CreateFile(strFileName,GENERIC_READ,FILE_SHARE_READ,0,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,0))==INVALID_HANDLE_VALUE)
 {
  AfxMessageBox("没有成功打开文件");
  return FALSE;
 }
 if(!(hMapping=CreateFileMapping(hFile,0,PAGE_READONLY|SEC_COMMIT,0,0,0)))//创建一个文件映射内核对象
 {
  AfxMessageBox("没有成功创建文件句柄");
  CloseHandle(hFile);
  return FALSE;
 }
 if(!(basePointer=MapViewOfFile(hMapping,FILE_MAP_READ,0,0,0)))//将文件数据映射到进程空间
 {
  AfxMessageBox("没有成功映射");
  CloseHandle(hMapping);
  CloseHandle(hFile);
  return FALSE;
 }
 CloseHandle(hMapping);
 CloseHandle(hFile);
 m_strText=(LPSTR)basePointer;
 UnmapViewOfFile(basePointer);//从进程空间解除映射
 return TRUE;
} 


BOOL C**DataSet::SaveData(const CString &sFilePathName,CString &sErrInfo)
{
    int nLen = GetDataLen();
    HANDLE hFile = CreateFileA(sFilePathName, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); 
    if (hFile == INVALID_HANDLE_VALUE) 
    {
        sErrInfo = "文件创建失败。";
        return FALSE;
    }
    HANDLE hMapObject = CreateFileMappingA(hFile, 0, PAGE_READWRITE,  0, nLen,NULL);//m_lFileLen
    if (hMapObject == NULL)
    { 
        CloseHandle(hFile);
        sErrInfo = "内存映射文件创建失败。";
        return FALSE;   
    } 
    LPVOID pBase = MapViewOfFile(hMapObject, FILE_MAP_WRITE, 0, 0, 0);
    BYTE *pBuf = (BYTE*)pBase;
    BYTE nHeadLen = sHead.GetLength();
    *pBuf = nHeadLen;
    pBuf++;
    memcpy(pBuf,sHead,nHeadLen);
    pBuf += nHeadLen;
 
    /*此处略去大量的写文件处理*/
 
    BOOL res = FlushViewOfFile(pBase,nLen);
    UnmapViewOfFile(pBase);
    CloseHandle(hMapObject);
    CloseHandle(hFile);
    //
    return TRUE;
}

  HANDLE hFile = CreateFile(  
     lpszFile,   
     GENERIC_READ ,// 如果要映射文件:此处必设置为只读(GENERIC_READ)或读写  
     0,    // 此设为打开文件的任何尝试均将失败  
     NULL,   
     OPEN_EXISTING,   
     FILE_ATTRIBUTE_NORMAL, //|FILE_FLAG_WRITE_THROUGH,
     NULL);  
  if (hFile != INVALID_HANDLE_VALUE)// 文件打开失败返回句柄为-1  
  {  
    HANDLE hFileMap = CreateFileMapping(  
       hFile, // 如果这值为INVALID_HANDLE_VALUE,是合法的,上步一定测试啊  
       NULL,   // 默认安全性  
       PAGE_READONLY,   //只读  
       0, // 2个32位数示1个64位数,最大文件字节数,  
       0,//dwFileSize, // 此为低字节,也就是最主要的参数,如果为0,取文件真实大小  
       NULL);  
 
    if (hFileMap != NULL)  
    {  
      LARGE_INTEGER dwFileSize = {0};  
      GetFileSizeEx(hFile, &dwFileSize);  
 
      if(pProcInfo)  
        pProcInfo->uLL_TotalSize = dwFileSize.QuadPart;  
 
      LARGE_INTEGER dwOffset = {0};  
      while(dwOffset.QuadPart < dwFileSize.QuadPart)  
      {  
        ULONGLONG partLen = dwFileSize.QuadPart - dwOffset.QuadPart;  
        UINT4 dwLen = (64 * 1024) * 1024; //64M 64K对齐  
        if(partLen < dwLen) dwLen = (UINT4)partLen;  
 
        LPVOID pvFile = MapViewOfFileEx( //pvFile就是得到的指针,用它来直接操作文件  
           hFileMap,   
           FILE_MAP_READ ,    //读  
           dwOffset.HighPart,     // 文件指针头位置 高字节  
           dwOffset.LowPart, // 文件指针头位置 低字节 必为分配粒度的整倍数,windows的粒度为64K  
           dwLen,   // 要映射的文件尾,如果为0,则从指针头到真实文件尾  
           NULL);  
 
        if (pvFile != NULL)  
        {    
          //do someting whit pvFile and dwLen 
 
          UnmapViewOfFile(pvFile); // 释放内存映射文件的头指针  
        }  
        else  
        {  
          TRACE( _T("MapViewOfFile Error=%d\n"), GetLastError());  
        }  
 
        dwOffset.QuadPart += dwLen;  //指向下一段
      }   
 
      CloseHandle(hFileMap);   // 内存映射文件句柄           
    }  
    else  
    {  
      TRACE( _T("CreateFileMapping Error=%d\n"), GetLastError());  
    }  
 
    CloseHandle(hFile);    // 关闭文件  
  }  
  else  
  {  
    TRACE( _T("CreateFile Error=%d\n"), GetLastError());  
  }  


8、CreateThread()与beginthread()的区别详细解析

我们知道在Windows下创建一个线程的方法有两种,一种就是调用Windows API CreateThread()来创建线程;另外一种就是调用MSVC CRT的函数_beginthread()或_beginthreadex()来创建线程。相应的退出线程也有两个函数Windows API的ExitThread()和CRT的_endthread()或_endthreadex()。这两套函数都是用来创建和退出线程的,它们有什么区别呢?


很多开发者不清楚这两者之间的关系,他们随意选一个函数来用,发现也没有什么大问题,于是就忙于解决更为紧迫的任务去了,而没有对它们进行深究。等到有一天忽然发现一个程序运行时间很长的时候会有细微的内存泄露,开发者绝对不会想到是因为这两套函数用混的结果。


根据Windows API和MSVC CRT的关系,可以看出来_beginthread()是对CreateThread()的包装,它最终还是调用CreateThread()来创建线程。那么在_beginthread()调用CreateThread()之前做了什么呢?我们可以看一下_beginthread()的源代码,它位于CRT源代码中的thread.c。我们可以发现它在调用CreateThread()之前申请了一个叫_tiddata的结构,然后将这个结构用_initptd()函数初始化之后传递给_beginthread()自己的线程入口函数_threadstart。_threadstart首先把由_beginthread()传过来的_tiddata结构指针保存到线程的显式TLS数组,然后它调用用户的线程入口真正开始线程。在用户线程结束之后,_threadstart()函数调用_endthread()结束线程。并且_threadstart还用__try/__except将用户线程入口函数包起来,用于捕获所有未处理的信号,并且将这些信号交给CRT处理。


所以除了信号之外,很明显CRT包装Windows API线程接口的最主要目的就是那个_tiddata。这个线程私有的结构里面保存的是什么呢?我们可以从mtdll.h中找到它的定义,它里面保存的是诸如线程ID、线程句柄、erron、strtok()的前一次调用位置、rand()函数的种子、异常处理等与CRT有关的而且是线程私有的信息。可见MSVC CRT并没有使用我们前面所说的__declspec(thread)这种方式来定义线程私有变量,从而防止库函数在多线程下失效,而是采用在堆上申请一个_tiddata结构,把线程私有变量放在结构内部,由显式TLS保存_tiddata的指针。


了解了这些信息以后,我们应该会想到一个问题,那就是如果我们用CreateThread()创建一个线程然后调用CRT的strtok()函数,按理说应该会出错,因为strtok()所需要的_tiddata并不存在,可是我们好像从来没碰到过这样的问题。查看strtok()函数就会发现,当一开始调用_getptd()去得到线程的_tiddata结构时,这个函数如果发现线程没有申请_tiddata结构,它就会申请这个结构并且负责初始化。于是无论我们调用哪个函数创建线程,都可以安全调用所有需要_tiddata的函数,因为一旦这个结构不存在,它就会被创建出来。


那么_tiddata在什么时候会被释放呢?ExitThread()肯定不会,因为它根本不知道有_tiddata这样一个结构存在,那么很明显是_endthread()释放的,这也正是CRT的做法。不过我们很多时候会发现,即使使用CreateThread()和ExitThread() (不调用ExitThread()直接退出线程函数的效果相同),也不会发现任何内存泄露,这又是为什么呢?经过仔细检查之后,我们发现原来密码在CRT DLL的入口函数DllMain中。我们知道,当一个进程/线程开始或退出的时候,每个DLL的DllMain都会被调用一次,于是动态链接版的CRT就有机会在DllMain中释放线程的_tiddata。可是DllMain只有当CRT是动态链接版的时候才起作用,静态链接CRT是没有DllMain的!这就是造成使用CreateThread()会导致内存泄露的一种情况,在这种情况下,_tiddata在线程结束时无法释放,造成了泄露。


我们可以用下面这个小程序来测试:


代码如下:


#include <Windows.h>
#include <process.h>
void thread(void *a)
{
    char* r = strtok( "aaa", "b" );
    ExitThread(0); // 这个函数是否调用都无所谓
}
int main(int argc, char* argv[])
{
    while(1) {
        CreateThread(  0, 0, (LPTHREAD_START_ROUTINE)thread, 0, 0, 0 );
        Sleep( 5 );
    }
return 0;
}


如果用动态链接的CRT (/MD,/MDd)就不会有问题,但是,如果使用静态链接CRT (/MT,/MTd),运行程序后在进程管理器中观察它就会发现内存用量不停地上升,但是如果我们把thread()函数中的ExitThread()改成_endthread()就不会有问题,因为_endthread()会将_tiddata()释放。
这个问题可以总结为:当使用CRT时(基本上所有的程序都使用CRT),请尽量使用_beginthread()/_beginthreadex()/_endthread()/_endthreadex()这组函数来创建线程。在MFC中,还有一组类似的函数是AfxBeginThread()和AfxEndThread(),根据上面的原理类推,它是MFC层面的线程包装函数,它们会维护线程与MFC相关的结构,当我们使用MFC类库时,尽量使用它提供的线程包装函数以保证程序运行正确。


9、new失败。
如果内存不足,更可能的情况是构造函数抛了异常,其次是某些非法操作导致程序crash,最后才是内存不足。
内存不足而导致new失败的情况几乎是不存在的。(可能某些嵌入式环境会出现,哪位遇到过的可以介绍下经历。)
第一种情况看《Exceptional C++》,第二种情况自己debug,第三种情况看《Exceptional C++ Style》。


10、 TCP组包问题及处理方法

http://blog.csdn.net/snipergzf/article/details/50810013


11、 wireshark源码vs2008编译

http://blog.csdn.net/qq_30549833/article/details/48918797

http://blog.csdn.net/alexander_vc/article/details/6198836


12、TCP层的分段和IP层的分片之间的关系 MTU和MSS存在的关系

http://blog.sina.com.cn/s/blog_648d306d0102v4z2.html

 IP分片和TCP分片的区别

http://blog.csdn.net/cumirror/article/details/5071234


13、如何把VS2008上编的debug、release程序在没装VS的xp机器上运行

http://blog.csdn.net/viggin/article/details/5712155


14、内存文件映射

往小了说,只要你把这几个API函数搞定了,一般的内存映射问题就可以解决了。。但是内存映射文件到底是干嘛的呢?让我们先来思考一个

如果您想读的内容大于系统分配的内存块怎么办?如果您想搜索的字符串刚好超过内存块的边界又该如何处理?对于第一个问题,您也许会说,只要不断地读就不解决了吗。至于第二个问题,您又会说在内存块的边界处做一些特别的处理,譬如放上一些标志位就可以了。原理上确实是行得通,但是这随问题复杂程度加深而显得非常难以处理。其中的第二个问题是有名的边界判断问题,程序中许许多多的错误都是由此引起。想一想,如果我们能够分配一个能够容纳整个文件的大内存块该多好啊,这样这两个问题不都迎刃而解了吗?是的,WIN32的内存映射文件确实允许我们分配一个装得下现实中可能存在的足够大的文件的内存。

利用内存映射文件您可以认为操作系统已经为您把文件全部装入了内存,然后您只要移动文件指针进行读写即可了。这样您甚至不需要调用那些分配、释放内存块和文件输入/输出的API函数,另外您可以把这用作不同的进程之间共享数据的一种办法。运用内存映射文件实际上没有涉及实际的文件操作,它更象为每个进程保留一个看得见的内存空间。至于把内存映射文件当成进程间共享数据的办法来用,则要加倍小心,因为您不得不处理数据的同步问题,否则您的应用程序也许很可能得到过时或错误的数据甚至崩溃。本课中我们将主要讲述内存映射文件,将不涉及进程间的同步。WIN32中的内存映射文件应用非常广泛,譬如:即使是系统的核心模块---PE格式文件装载器也用到了内存映射文件,因为PE格式的文件并不是一次性加载到内存中来的,譬如他它在首次加载时只加载必需加载的部分,而其他部分在用到时再加载,这正好可以利用到内存映射文件的长处。实际中的大多数文件存取都和PE加载器类似,所以您在处理该类问题时也应该充分利用内存映射文件。

内存映射文件本身还是有一些局限性的,譬如一旦您生成了一个内存映射文件,那么您在那个会话期间是不能够改变它的大小的。所以内存映射文件对于只读文件和不会影响其大小的文件操作是非常有用的。当然这并不意味着对于会引起改变其大小的文件操作就一定不能用内存影射文件的方法,您可以事先估计操作后的文件的可能大小,然后生成这么大小一块的内存映射文件,然后文件的长度就可以增长到这么一个大小

#include <iostream>  
#include <fcntl.h>  
#include <io.h>  
#include <Windows.h>
//#include <afxwin.h>  
using namespace std;  

int main()  
{  
	//开始  
	//获得文件句柄  
	HANDLE hFile=CreateFile(  
		"c:\\test1.txt",   //文件名
		GENERIC_READ|GENERIC_WRITE, //对文件进行读写操作  
		FILE_SHARE_READ|FILE_SHARE_WRITE,  
		NULL,       
		OPEN_EXISTING,  //打开已存在文件  
		FILE_ATTRIBUTE_NORMAL,     
		0);    

	//返回值size_high,size_low分别表示文件大小的高32位/低32位  
	DWORD size_low,size_high;  
	size_low= GetFileSize(hFile,&size_high);   

	//创建文件的内存映射文件。     
	HANDLE hMapFile=CreateFileMapping(    
		hFile,       
		NULL,     
		PAGE_READWRITE,  //对映射文件进行读写  
		size_high,      
		size_low,   //这两个参数共64位,所以支持的最大文件长度为16EB  
		NULL);     
	if(hMapFile==INVALID_HANDLE_VALUE)     
	{     
		cout << "Can't create file mapping.Error:" << GetLastError() << endl;
		CloseHandle(hFile);  
		return 0;     
	}    

	//把文件数据映射到进程的地址空间  
	void* pvFile=MapViewOfFile(  
		hMapFile,   
		FILE_MAP_READ|FILE_MAP_WRITE,   
		0,  
		0,  
		0);    
	unsigned char *p=(unsigned char*)pvFile;   

	//至此,就获得了外部文件test.dat在内存地址空间的映射,  
	//下面就可以用指针p"折磨"这个文件了  
	//CString s;  
	p[size_low-1]='!';   
	p[size_low-2]='X'; //修改该文件的最后两个字节(文件大小<4GB高32位为0)  
	//s.Format("%s",p); 
	cout << "p:" << p << endl;
	//读文件的最后3个字节  
	//AfxMessageBox(s);  


	//结束  
	UnmapViewOfFile(pvFile); //撤销映射  
	CloseHandle(hFile); //关闭文件  

	return 0;  
}

15、无法打开包括文件:“afxcontrolbars.h”: No such file or directory

原因分析:以前用了vs2008SP1写的程序,现在用没有SP1的vs2008编译引起的。
解决方法:

方法1:

安装SP1
如果从 Windows Update 获取 Service Pack 时出现问题,则可以从 Microsoft 下载中心网站将 SP1 作为独立安装包下载,然后手动安装 SP1。
使用独立安装包手动安装 SP1 的步骤
    根据您运行的是 32 位还是 64 位版本的 Windows Vista,请下载适合 32 位版本的更新或下载适合 64 位版本的更新。
        要确定您的 Windows Vista 是 32 位还是 64 位版本,请单击“开始”按钮 “开始”按钮的图片,右键单击“我的电脑”,然后单击“属性”。
    若要立即安装 SP1,请单击“打开”或“运行”,然后按照屏幕上的指示进行操作。若要在以后安装 SP1,请单击“保存”,将安装文件复制到您的计算机上。准备安装服务包时,请打开已复制到计算机中的文件。
    在“欢迎使用 Windows Vista Service Pack 1”页面上,单击“立即安装”。
    按照屏幕上的说明进行操作。在安装过程中,计算机可能会多次重新启动。
    安装完成后,请在显示 Windows 登录提示时登录到计算机。您将收到一条消息,指出更新是否成功。
    如果已禁用防病毒软件,请重新启用。

方法2:

若程序是对话框程序,和那些高端的controlbar根本没什么关系

可以进行如下操作:
#include <afxcontrolbars.h> // MFC support for ribbons and control bars

进行如下的修改:
//#include <afxcontrolbars.h> // MFC support for ribbons and control bars

#define CWinAppEx CWinApp    //add the line


16、nginx文章

 Nginx学习总结概述(一)
http://shift-alt-ctrl.iteye.com/blog/2229578


文章18 :Nginx中http请求的处理过程
http://blog.csdn.net/yankai0219/article/details/8220695


nginx 开发简单的http模块
http://blog.csdn.net/zhangxiao93/article/details/52836128


17、理解 COM 套间 (转自VCKBase)

http://www.cppblog.com/stdyh/archive/2013/06/11/200940.html


18、C++监视文件夹怎么做?

http://bbs.csdn.net/topics/220040086


19、利用sprintf和sscanf实现十六进制和十进制之间的相互转换

    #include <stdio.h>  
    #include <limits.h>  
      
    int main()  
    {  
        char s[100] = {0};  
        sprintf(s, "%x", INT_MAX);  
        printf("%s\n", s); // 7fffffff  
      
        char str[100] = "7fffffff";  
        int i = 0;  
        sscanf(str, "%x", &i);  
        printf("%d\n", i); // 2147483647  
          
        return 0;   
    }  

20、"链接器工具错误 LNK2026 XXX模块对于 SAFESEH 映像是不安全的"

解决方法:
1.打开该项目的“属性页”对话框。
2.单击“链接器”文件夹。
3.单击“命令行”属性页。
4.将 /SAFESEH:NO 键入“附加选项”框中,然后点击应用。







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章