小技巧、小經驗(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 鍵入“附加選項”框中,然後點擊應用。







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