• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            road420

            導航

            <2025年6月>
            25262728293031
            1234567
            891011121314
            15161718192021
            22232425262728
            293012345

            統計

            常用鏈接

            留言簿(2)

            隨筆檔案

            文章檔案

            搜索

            最新評論

            閱讀排行榜

            評論排行榜

            #

            堆~棧區別

            來自任我行C++blog:


            堆和棧的區別
            一、預備知識—程序的內存分配
            一個由c/C++編譯的程序占用的內存分為以下幾個部分
            1、棧區(stack)— 由編譯器自動分配釋放 ,存放函數的參數值,局部變量的值等。其操作方式類似于數據結構中的棧。
            2、堆區(heap) — 一般由程序員分配釋放, 若程序員不釋放,程序結束時可能由OS回收 。注意它與數據結構中的堆是兩回事,分配方式倒是類似于鏈表,呵呵。
            3、全局區(靜態區)(static)—,全局變量和靜態變量的存儲是放在一塊的,初始化的全局變量和靜態變量在一塊區域, 未初始化的全局變量和未初始化的靜態變量在相鄰的另一塊區域。 - 程序結束后有系統釋放 
            4、文字常量區—常量字符串就是放在這里的。 程序結束后由系統釋放
            5、程序代碼區—存放函數體的二進制代碼。
            二、例子程序 
            這是一個前輩寫的,非常詳細 
            //main.cpp 
            int a = 0; 全局初始化區 
            char *p1; 全局未初始化區 
            main() 

            int b; 棧 
            char s[] = "abc"; 棧 
            char *p2; 棧 
            char *p3 = "123456"; 123456\0在常量區,p3在棧上。 
            static int c =0; 全局(靜態)初始化區 
            p1 = (char *)malloc(10); 
            p2 = (char *)malloc(20); 
            分配得來得10和20字節的區域就在堆區。 
            strcpy(p1, "123456"); 123456\0放在常量區,編譯器可能會將它與p3所指向的"123456"優化成一個地方。 

             


            二、堆和棧的理論知識 
            2.1申請方式 
            stack: 
            由系統自動分配。 例如,聲明在函數中一個局部變量 int b; 系統自動在棧中為b開辟空間 
            heap: 
            需要程序員自己申請,并指明大小,在c中malloc函數 
            如p1 = (char *)malloc(10); 
            在C++中用new運算符 
            如p2 = (char *)malloc(10); 
            但是注意p1、p2本身是在棧中的。 


            2.2 
            申請后系統的響應 
            棧:只要棧的剩余空間大于所申請空間,系統將為程序提供內存,否則將報異常提示棧溢出。 
            堆:首先應該知道操作系統有一個記錄空閑內存地址的鏈表,當系統收到程序的申請時, 
            會遍歷該鏈表,尋找第一個空間大于所申請空間的堆結點,然后將該結點從空閑結點鏈表中刪除,并將該結點的空間分配給程序,另外,對于大多數系統,會在這塊內存空間中的首地址處記錄本次分配的大小,這樣,代碼中的delete語句才能正確的釋放本內存空間。另外,由于找到的堆結點的大小不一定正好等于申請的大小,系統會自動的將多余的那部分重新放入空閑鏈表中。 

            2.3申請大小的限制 
            棧:在Windows下,棧是向低地址擴展的數據結構,是一塊連續的內存的區域。這句話的意思是棧頂的地址和棧的最大容量是系統預先規定好的,在WINDOWS下,棧的大小是2M(也有的說是1M,總之是一個編譯時就確定的常數),如果申請的空間超過棧的剩余空間時,將提示overflow。因此,能從棧獲得的空間較小。 
            堆:堆是向高地址擴展的數據結構,是不連續的內存區域。這是由于系統是用鏈表來存儲的空閑內存地址的,自然是不連續的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限于計算機系統中有效的虛擬內存。由此可見,堆獲得的空間比較靈活,也比較大。 


            2.4申請效率的比較: 
            棧由系統自動分配,速度較快。但程序員是無法控制的。 
            堆是由new分配的內存,一般速度比較慢,而且容易產生內存碎片,不過用起來最方便. 
            另外,在WINDOWS下,最好的方式是用VirtualAlloc分配內存,他不是在堆,也不是在棧是直接在進程的地址空間中保留一快內存,雖然用起來最不方便。但是速度快,也最靈活。 

            2.5堆和棧中的存儲內容 
            棧: 在函數調用時,第一個進棧的是主函數中后的下一條指令(函數調用語句的下一條可執行語句)的地址,然后是函數的各個參數,在大多數的C編譯器中,參數是由右往左入棧的,然后是函數中的局部變量。注意靜態變量是不入棧的。 
            當本次函數調用結束后,局部變量先出棧,然后是參數,最后棧頂指針指向最開始存的地址,也就是主函數中的下一條指令,程序由該點繼續運行。 
            堆:一般是在堆的頭部用一個字節存放堆的大小。堆中的具體內容有程序員安排。 

            2.6存取效率的比較 

            char s1[] = "aaaaaaaaaaaaaaa"; 
            char *s2 = "bbbbbbbbbbbbbbbbb"; 
            aaaaaaaaaaa是在運行時刻賦值的; 
            而bbbbbbbbbbb是在編譯時就確定的; 
            但是,在以后的存取中,在棧上的數組比指針所指向的字符串(例如堆)快。 
            比如: 
            #include 
            void main() 

            char a = 1; 
            char c[] = "1234567890"; 
            char *p ="1234567890"; 
            a = c[1]; 
            a = p[1]; 
            return; 

            對應的匯編代碼 
            10: a = c[1]; 
            00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh] 
            0040106A 88 4D FC mov byte ptr [ebp-4],cl 
            11: a = p[1]; 
            0040106D 8B 55 EC mov edx,dword ptr [ebp-14h] 
            00401070 8A 42 01 mov al,byte ptr [edx+1] 
            00401073 88 45 FC mov byte ptr [ebp-4],al 
            第一種在讀取時直接就把字符串中的元素讀到寄存器cl中,而第二種則要先把指針值讀到edx中,在根據edx讀取字符,顯然慢了。 


            2.7小結: 
            堆和棧的區別可以用如下的比喻來看出: 
            使用棧就象我們去飯館里吃飯,只管點菜(發出申請)、付錢、和吃(使用),吃飽了就走,不必理會切菜、洗菜等準備工作和洗碗、刷鍋等掃尾工作,他的好處是快捷,但是自由度小。 
            使用堆就象是自己動手做喜歡吃的菜肴,比較麻煩,但是比較符合自己的口味,而且自由度大。 



            windows進程中的內存結構


            在閱讀本文之前,如果你連堆棧是什么多不知道的話,請先閱讀文章后面的基礎知識。 

            接觸過編程的人都知道,高級語言都能通過變量名來訪問內存中的數據。那么這些變量在內存中是如何存放的呢?程序又是如何使用這些變量的呢?下面就會對此進行深入的討論。下文中的C語言代碼如沒有特別聲明,默認都使用VC編譯的release版。 

            首先,來了解一下 C 語言的變量是如何在內存分部的。C 語言有全局變量(Global)、本地變量(Local),靜態變量(Static)、寄存器變量(Regeister)。每種變量都有不同的分配方式。先來看下面這段代碼: 

            #include <stdio.h> 

            int g1=0, g2=0, g3=0; 

            int main() 

            static int s1=0, s2=0, s3=0; 
            int v1=0, v2=0, v3=0; 

            //打印出各個變量的內存地址 

            printf("0x%08x\n",&v1); //打印各本地變量的內存地址 
            printf("0x%08x\n",&v2); 
            printf("0x%08x\n\n",&v3); 
            printf("0x%08x\n",&g1); //打印各全局變量的內存地址 
            printf("0x%08x\n",&g2); 
            printf("0x%08x\n\n",&g3); 
            printf("0x%08x\n",&s1); //打印各靜態變量的內存地址 
            printf("0x%08x\n",&s2); 
            printf("0x%08x\n\n",&s3); 
            return 0; 

            編譯后的執行結果是: 

            0x0012ff78 
            0x0012ff7c 
            0x0012ff80 

            0x004068d0 
            0x004068d4 
            0x004068d8 

            0x004068dc 
            0x004068e0 
            0x004068e4 

            輸出的結果就是變量的內存地址。其中v1,v2,v3是本地變量,g1,g2,g3是全局變量,s1,s2,s3是靜態變量。你可以看到這些變量在內存是連續分布的,但是本地變量和全局變量分配的內存地址差了十萬八千里,而全局變量和靜態變量分配的內存是連續的。這是因為本地變量和全局/靜態變量是分配在不同類型的內存區域中的結果。對于一個進程的內存空間而言,可以在邏輯上分成3個部份:代碼區,靜態數據區和動態數據區。動態數據區一般就是“堆棧”。“棧(stack)”和“堆(heap)”是兩種不同的動態數據區,棧是一種線性結構,堆是一種鏈式結構。進程的每個線程都有私有的“棧”,所以每個線程雖然代碼一樣,但本地變量的數據都是互不干擾。一個堆棧可以通過“基地址”和“棧頂”地址來描述。全局變量和靜態變量分配在靜態數據區,本地變量分配在動態數據區,即堆棧中。程序通過堆棧的基地址和偏移量來訪問本地變量。 


            ├———————┤低端內存區域 
            │ …… │ 
            ├———————┤ 
            │ 動態數據區 │ 
            ├———————┤ 
            │ …… │ 
            ├———————┤ 
            │ 代碼區 │ 
            ├———————┤ 
            │ 靜態數據區 │ 
            ├———————┤ 
            │ …… │ 
            ├———————┤高端內存區域 


            堆棧是一個先進后出的數據結構,棧頂地址總是小于等于棧的基地址。我們可以先了解一下函數調用的過程,以便對堆棧在程序中的作用有更深入的了解。不同的語言有不同的函數調用規定,這些因素有參數的壓入規則和堆棧的平衡。windows API的調用規則和ANSI C的函數調用規則是不一樣的,前者由被調函數調整堆棧,后者由調用者調整堆棧。兩者通過“__stdcall”和“__cdecl”前綴區分。先看下面這段代碼: 

            #include <stdio.h> 

            void __stdcall func(int param1,int param2,int param3) 

            int var1=param1; 
            int var2=param2; 
            int var3=param3; 
            printf("0x%08x\n",¶m1); //打印出各個變量的內存地址 
            printf("0x%08x\n",¶m2); 
            printf("0x%08x\n\n",¶m3); 
            printf("0x%08x\n",&var1); 
            printf("0x%08x\n",&var2); 
            printf("0x%08x\n\n",&var3); 
            return; 

            int main() 

            func(1,2,3); 
            return 0; 

            編譯后的執行結果是: 

            0x0012ff78 
            0x0012ff7c 
            0x0012ff80 

            0x0012ff68 
            0x0012ff6c 
            0x0012ff70 


            ├———————┤<—函數執行時的棧頂(ESP)、低端內存區域 
            │ …… │ 
            ├———————┤ 
            │ var 1 │ 
            ├———————┤ 
            │ var 2 │ 
            ├———————┤ 
            │ var 3 │ 
            ├———————┤ 
            │ RET │ 
            ├———————┤<—“__cdecl”函數返回后的棧頂(ESP) 
            │ parameter 1 │ 
            ├———————┤ 
            │ parameter 2 │ 
            ├———————┤ 
            │ parameter 3 │ 
            ├———————┤<—“__stdcall”函數返回后的棧頂(ESP) 
            │ …… │ 
            ├———————┤<—棧底(基地址 EBP)、高端內存區域 


            上圖就是函數調用過程中堆棧的樣子了。首先,三個參數以從又到左的次序壓入堆棧,先壓“param3”,再壓“param2”,最后壓入“param1”;然后壓入函數的返回地址(RET),接著跳轉到函數地址接著執行(這里要補充一點,介紹UNIX下的緩沖溢出原理的文章中都提到在壓入RET后,繼續壓入當前EBP,然后用當前ESP代替EBP。然而,有一篇介紹windows下函數調用的文章中說,在windows下的函數調用也有這一步驟,但根據我的實際調試,并未發現這一步,這還可以從param3和var1之間只有4字節的間隙這點看出來);第三步,將棧頂(ESP)減去一個數,為本地變量分配內存空間,上例中是減去12字節(ESP=ESP-3*4,每個int變量占用4個字節);接著就初始化本地變量的內存空間。由于“__stdcall”調用由被調函數調整堆棧,所以在函數返回前要恢復堆棧,先回收本地變量占用的內存(ESP=ESP+3*4),然后取出返回地址,填入EIP寄存器,回收先前壓入參數占用的內存(ESP=ESP+3*4),繼續執行調用者的代碼。參見下列匯編代碼: 

            ;--------------func 函數的匯編代碼------------------- 

            :00401000 83EC0C sub esp, 0000000C //創建本地變量的內存空間 
            :00401003 8B442410 mov eax, dword ptr [esp+10] 
            :00401007 8B4C2414 mov ecx, dword ptr [esp+14] 
            :0040100B 8B542418 mov edx, dword ptr [esp+18] 
            :0040100F 89442400 mov dword ptr [esp], eax 
            :00401013 8D442410 lea eax, dword ptr [esp+10] 
            :00401017 894C2404 mov dword ptr [esp+04], ecx 

            ……………………(省略若干代碼) 

            :00401075 83C43C add esp, 0000003C ;恢復堆棧,回收本地變量的內存空間 
            :00401078 C3 ret 000C ;函數返回,恢復參數占用的內存空間 
            ;如果是“__cdecl”的話,這里是“ret”,堆棧將由調用者恢復 

            ;-------------------函數結束------------------------- 


            ;--------------主程序調用func函數的代碼-------------- 

            :00401080 6A03 push 00000003 //壓入參數param3 
            :00401082 6A02 push 00000002 //壓入參數param2 
            :00401084 6A01 push 00000001 //壓入參數param1 
            :00401086 E875FFFFFF call 00401000 //調用func函數 
            ;如果是“__cdecl”的話,將在這里恢復堆棧,“add esp, 0000000C” 

            聰明的讀者看到這里,差不多就明白緩沖溢出的原理了。先來看下面的代碼: 

            #include <stdio.h> 
            #include <string.h> 

            void __stdcall func() 

            char lpBuff[8]="\0"; 
            strcat(lpBuff,"AAAAAAAAAAA"); 
            return; 

            int main() 

            func(); 
            return 0; 

            編譯后執行一下回怎么樣?哈,“"0x00414141"指令引用的"0x00000000"內存。該內存不能為"read"。”,“非法操作”嘍!"41"就是"A"的16進制的ASCII碼了,那明顯就是strcat這句出的問題了。"lpBuff"的大小只有8字節,算進結尾的\0,那strcat最多只能寫入7個"A",但程序實際寫入了11個"A"外加1個\0。再來看看上面那幅圖,多出來的4個字節正好覆蓋了RET的所在的內存空間,導致函數返回到一個錯誤的內存地址,執行了錯誤的指令。如果能精心構造這個字符串,使它分成三部分,前一部份僅僅是填充的無意義數據以達到溢出的目的,接著是一個覆蓋RET的數據,緊接著是一段shellcode,那只要著個RET地址能指向這段shellcode的第一個指令,那函數返回時就能執行shellcode了。但是軟件的不同版本和不同的運行環境都可能影響這段shellcode在內存中的位置,那么要構造這個RET是十分困難的。一般都在RET和shellcode之間填充大量的NOP指令,使得exploit有更強的通用性。 


            ├———————┤<—低端內存區域 
            │ …… │ 
            ├———————┤<—由exploit填入數據的開始 
            │ │ 
            │ buffer │<—填入無用的數據 
            │ │ 
            ├———————┤ 
            │ RET │<—指向shellcode,或NOP指令的范圍 
            ├———————┤ 
            │ NOP │ 
            │ …… │<—填入的NOP指令,是RET可指向的范圍 
            │ NOP │ 
            ├———————┤ 
            │ │ 
            │ shellcode │ 
            │ │ 
            ├———————┤<—由exploit填入數據的結束 
            │ …… │ 
            ├———————┤<—高端內存區域 


            windows下的動態數據除了可存放在棧中,還可以存放在堆中。了解C++的朋友都知道,C++可以使用new關鍵字來動態分配內存。來看下面的C++代碼: 

            #include <stdio.h> 
            #include <iostream.h> 
            #include <windows.h> 

            void func() 

            char *buffer=new char[128]; 
            char bufflocal[128]; 
            static char buffstatic[128]; 
            printf("0x%08x\n",buffer); //打印堆中變量的內存地址 
            printf("0x%08x\n",bufflocal); //打印本地變量的內存地址 
            printf("0x%08x\n",buffstatic); //打印靜態變量的內存地址 

            void main() 

            func(); 
            return; 

            程序執行結果為: 

            0x004107d0 
            0x0012ff04 
            0x004068c0 

            可以發現用new關鍵字分配的內存即不在棧中,也不在靜態數據區。VC編譯器是通過windows下的“堆(heap)”來實現new關鍵字的內存動態分配。在講“堆”之前,先來了解一下和“堆”有關的幾個API函數: 

            HeapAlloc 在堆中申請內存空間 
            HeapCreate 創建一個新的堆對象 
            HeapDestroy 銷毀一個堆對象 
            HeapFree 釋放申請的內存 
            HeapWalk 枚舉堆對象的所有內存塊 
            GetProcessHeap 取得進程的默認堆對象 
            GetProcessHeaps 取得進程所有的堆對象 
            LocalAlloc 
            GlobalAlloc 

            當進程初始化時,系統會自動為進程創建一個默認堆,這個堆默認所占內存的大小為1M。堆對象由系統進行管理,它在內存中以鏈式結構存在。通過下面的代碼可以通過堆動態申請內存空間: 

            HANDLE hHeap=GetProcessHeap(); 
            char *buff=HeapAlloc(hHeap,0,8); 

            其中hHeap是堆對象的句柄,buff是指向申請的內存空間的地址。那這個hHeap究竟是什么呢?它的值有什么意義嗎?看看下面這段代碼吧: 

            #pragma comment(linker,"/entry:main") //定義程序的入口 
            #include <windows.h> 

            _CRTIMP int (__cdecl *printf)(const char *, ...); //定義STL函數printf 
            /*--------------------------------------------------------------------------- 
            寫到這里,我們順便來復習一下前面所講的知識: 
            (*注)printf函數是C語言的標準函數庫中函數,VC的標準函數庫由msvcrt.dll模塊實現。 
            由函數定義可見,printf的參數個數是可變的,函數內部無法預先知道調用者壓入的參數個數,函數只能通過分析第一個參數字符串的格式來獲得壓入參數的信息,由于這里參數的個數是動態的,所以必須由調用者來平衡堆棧,這里便使用了__cdecl調用規則。BTW,Windows系統的API函數基本上是__stdcall調用形式,只有一個API例外,那就是wsprintf,它使用__cdecl調用規則,同printf函數一樣,這是由于它的參數個數是可變的緣故。 
            ---------------------------------------------------------------------------*/ 
            void main() 

            HANDLE hHeap=GetProcessHeap(); 
            char *buff=HeapAlloc(hHeap,0,0x10); 
            char *buff2=HeapAlloc(hHeap,0,0x10); 
            HMODULE hMsvcrt=LoadLibrary("msvcrt.dll"); 
            printf=(void *)GetProcAddress(hMsvcrt,"printf"); 
            printf("0x%08x\n",hHeap); 
            printf("0x%08x\n",buff); 
            printf("0x%08x\n\n",buff2); 

            執行結果為: 

            0x00130000 
            0x00133100 
            0x00133118 

            hHeap的值怎么和那個buff的值那么接近呢?其實hHeap這個句柄就是指向HEAP首部的地址。在進程的用戶區存著一個叫PEB(進程環境塊)的結構,這個結構中存放著一些有關進程的重要信息,其中在PEB首地址偏移0x18處存放的ProcessHeap就是進程默認堆的地址,而偏移0x90處存放了指向進程所有堆的地址列表的指針。windows有很多API都使用進程的默認堆來存放動態數據,如windows 2000下的所有ANSI版本的函數都是在默認堆中申請內存來轉換ANSI字符串到Unicode字符串的。對一個堆的訪問是順序進行的,同一時刻只能有一個線程訪問堆中的數據,當多個線程同時有訪問要求時,只能排隊等待,這樣便造成程序執行效率下降。 

            最后來說說內存中的數據對齊。所位數據對齊,是指數據所在的內存地址必須是該數據長度的整數倍,DWORD數據的內存起始地址能被4除盡,WORD數據的內存起始地址能被2除盡,x86 CPU能直接訪問對齊的數據,當他試圖訪問一個未對齊的數據時,會在內部進行一系列的調整,這些調整對于程序來說是透明的,但是會降低運行速度,所以編譯器在編譯程序時會盡量保證數據對齊。同樣一段代碼,我們來看看用VC、Dev-C++和lcc三個不同編譯器編譯出來的程序的執行結果: 

            #include <stdio.h> 

            int main() 

            int a; 
            char b; 
            int c; 
            printf("0x%08x\n",&a); 
            printf("0x%08x\n",&b); 
            printf("0x%08x\n",&c); 
            return 0; 

            這是用VC編譯后的執行結果: 
            0x0012ff7c 
            0x0012ff7b 
            0x0012ff80 
            變量在內存中的順序:b(1字節)-a(4字節)-c(4字節)。 

            這是用Dev-C++編譯后的執行結果: 
            0x0022ff7c 
            0x0022ff7b 
            0x0022ff74 
            變量在內存中的順序:c(4字節)-中間相隔3字節-b(占1字節)-a(4字節)。 

            這是用lcc編譯后的執行結果: 
            0x0012ff6c 
            0x0012ff6b 
            0x0012ff64 
            變量在內存中的順序:同上。 

            三個編譯器都做到了數據對齊,但是后兩個編譯器顯然沒VC“聰明”,讓一個char占了4字節,浪費內存哦。 


            基礎知識: 
            堆棧是一種簡單的數據結構,是一種只允許在其一端進行插入或刪除的線性表。允許插入或刪除操作的一端稱為棧頂,另一端稱為棧底,對堆棧的插入和刪除操作被稱為入棧和出棧。有一組CPU指令可以實現對進程的內存實現堆棧訪問。其中,POP指令實現出棧操作,PUSH指令實現入棧操作。CPU的ESP寄存器存放當前線程的棧頂指針,EBP寄存器中保存當前線程的棧底指針。CPU的EIP寄存器存放下一個CPU指令存放的內存地址,當CPU執行完當前的指令后,從EIP寄存器中讀取下一條指令的內存地址,然后繼續執行。 


            參考:《Windows下的HEAP溢出及其利用》by: isno 
            《windows核心編程》by: Jeffrey Richter 



            摘要: 討論常見的堆性能問題以及如何防范它們。(共 9 頁)

            前言
            您是否是動態分配的 C/C++ 對象忠實且幸運的用戶?您是否在模塊間的往返通信中頻繁地使用了“自動化”?您的程序是否因堆分配而運行起來很慢?不僅僅您遇到這樣的問題。幾乎所有項目遲早都會遇到堆問題。大家都想說,“我的代碼真正好,只是堆太慢”。那只是部分正確。更深入理解堆及其用法、以及會發生什么問題,是很有用的。

            什么是堆?
            (如果您已經知道什么是堆,可以跳到“什么是常見的堆性能問題?”部分)

            在程序中,使用堆來動態分配和釋放對象。在下列情況下,調用堆操作: 

            事先不知道程序所需對象的數量和大小。


            對象太大而不適合堆棧分配程序。
            堆使用了在運行時分配給代碼和堆棧的內存之外的部分內存。下圖給出了堆分配程序的不同層。


            GlobalAlloc/GlobalFree:Microsoft Win32 堆調用,這些調用直接與每個進程的默認堆進行對話。

            LocalAlloc/LocalFree:Win32 堆調用(為了與 Microsoft Windows NT 兼容),這些調用直接與每個進程的默認堆進行對話。

            COM 的 IMalloc 分配程序(或 CoTaskMemAlloc / CoTaskMemFree):函數使用每個進程的默認堆。自動化程序使用“組件對象模型 (COM)”的分配程序,而申請的程序使用每個進程堆。

            C/C++ 運行時 (CRT) 分配程序:提供了 malloc() 和 free() 以及 new 和 delete 操作符。如 Microsoft Visual Basic 和 Java 等語言也提供了新的操作符并使用垃圾收集來代替堆。CRT 創建自己的私有堆,駐留在 Win32 堆的頂部。

            Windows NT 中,Win32 堆是 Windows NT 運行時分配程序周圍的薄層。所有 API 轉發它們的請求給 NTDLL。

            Windows NT 運行時分配程序提供 Windows NT 內的核心堆分配程序。它由具有 128 個大小從 8 到 1,024 字節的空閑列表的前端分配程序組成。后端分配程序使用虛擬內存來保留和提交頁。

            在圖表的底部是“虛擬內存分配程序”,操作系統使用它來保留和提交頁。所有分配程序使用虛擬內存進行數據的存取。

            分配和釋放塊不就那么簡單嗎?為何花費這么長時間?

            堆實現的注意事項
            傳統上,操作系統和運行時庫是與堆的實現共存的。在一個進程的開始,操作系統創建一個默認堆,叫做“進程堆”。如果沒有其他堆可使用,則塊的分配使用“進程堆”。語言運行時也能在進程內創建單獨的堆。(例如,C 運行時創建它自己的堆。)除這些專用的堆外,應用程序或許多已載入的動態鏈接庫 (DLL) 之一可以創建和使用單獨的堆。Win32 提供一整套 API 來創建和使用私有堆。有關堆函數(英文)的詳盡指導,請參見 MSDN。

            當應用程序或 DLL 創建私有堆時,這些堆存在于進程空間,并且在進程內是可訪問的。從給定堆分配的數據將在同一個堆上釋放。(不能從一個堆分配而在另一個堆釋放。)

            在所有虛擬內存系統中,堆駐留在操作系統的“虛擬內存管理器”的頂部。語言運行時堆也駐留在虛擬內存頂部。某些情況下,這些堆是操作系統堆中的層,而語言運行時堆則通過大塊的分配來執行自己的內存管理。不使用操作系統堆,而使用虛擬內存函數更利于堆的分配和塊的使用。

            典型的堆實現由前、后端分配程序組成。前端分配程序維持固定大小塊的空閑列表。對于一次分配調用,堆嘗試從前端列表找到一個自由塊。如果失敗,堆被迫從后端(保留和提交虛擬內存)分配一個大塊來滿足請求。通用的實現有每塊分配的開銷,這將耗費執行周期,也減少了可使用的存儲空間。

            Knowledge Base 文章 Q10758,“用 calloc() 和 malloc() 管理內存” (搜索文章編號), 包含了有關這些主題的更多背景知識。另外,有關堆實現和設計的詳細討論也可在下列著作中找到:“Dynamic Storage Allocation: A Survey and Critical Review”,作者 Paul R. Wilson、Mark S. Johnstone、Michael Neely 和 David Boles;“International Workshop on Memory Management”, 作者 Kinross, Scotland, UK, 1995 年 9 月(http://www.cs.utexas.edu/users/oops/papers.html)(英文)。

            Windows NT 的實現(Windows NT 版本 4.0 和更新版本) 使用了 127 個大小從 8 到 1,024 字節的 8 字節對齊塊空閑列表和一個“大塊”列表。“大塊”列表(空閑列表[0]) 保存大于 1,024 字節的塊。空閑列表容納了用雙向鏈表鏈接在一起的對象。默認情況下,“進程堆”執行收集操作。(收集是將相鄰空閑塊合并成一個大塊的操作。)收集耗費了額外的周期,但減少了堆塊的內部碎片。

            單一全局鎖保護堆,防止多線程式的使用。(請參見“Server Performance and Scalability Killers”中的第一個注意事項, George Reilly 所著,在 “MSDN Online Web Workshop”上(站點:http://msdn.microsoft.com/workshop/server/iis/tencom.asp(英文)。)單一全局鎖本質上是用來保護堆數據結構,防止跨多線程的隨機存取。若堆操作太頻繁,單一全局鎖會對性能有不利的影響。

            什么是常見的堆性能問題?
            以下是您使用堆時會遇到的最常見問題: 

            分配操作造成的速度減慢。光分配就耗費很長時間。最可能導致運行速度減慢原因是空閑列表沒有塊,所以運行時分配程序代碼會耗費周期尋找較大的空閑塊,或從后端分配程序分配新塊。


            釋放操作造成的速度減慢。釋放操作耗費較多周期,主要是啟用了收集操作。收集期間,每個釋放操作“查找”它的相鄰塊,取出它們并構造成較大塊,然后再把此較大塊插入空閑列表。在查找期間,內存可能會隨機碰到,從而導致高速緩存不能命中,性能降低。


            堆競爭造成的速度減慢。當兩個或多個線程同時訪問數據,而且一個線程繼續進行之前必須等待另一個線程完成時就發生競爭。競爭總是導致麻煩;這也是目前多處理器系統遇到的最大問題。當大量使用內存塊的應用程序或 DLL 以多線程方式運行(或運行于多處理器系統上)時將導致速度減慢。單一鎖定的使用—常用的解決方案—意味著使用堆的所有操作是序列化的。當等待鎖定時序列化會引起線程切換上下文。可以想象交叉路口閃爍的紅燈處走走停停導致的速度減慢。 
            競爭通常會導致線程和進程的上下文切換。上下文切換的開銷是很大的,但開銷更大的是數據從處理器高速緩存中丟失,以及后來線程復活時的數據重建。

            堆破壞造成的速度減慢。造成堆破壞的原因是應用程序對堆塊的不正確使用。通常情形包括釋放已釋放的堆塊或使用已釋放的堆塊,以及塊的越界重寫等明顯問題。(破壞不在本文討論范圍之內。有關內存重寫和泄漏等其他細節,請參見 Microsoft Visual C++(R) 調試文檔 。)


            頻繁的分配和重分配造成的速度減慢。這是使用腳本語言時非常普遍的現象。如字符串被反復分配,隨重分配增長和釋放。不要這樣做,如果可能,盡量分配大字符串和使用緩沖區。另一種方法就是盡量少用連接操作。
            競爭是在分配和釋放操作中導致速度減慢的問題。理想情況下,希望使用沒有競爭和快速分配/釋放的堆。可惜,現在還沒有這樣的通用堆,也許將來會有。

            在所有的服務器系統中(如 IIS、MSProxy、DatabaseStacks、網絡服務器、 Exchange 和其他), 堆鎖定實在是個大瓶頸。處理器數越多,競爭就越會惡化。

            盡量減少堆的使用
            現在您明白使用堆時存在的問題了,難道您不想擁有能解決這些問題的超級魔棒嗎?我可希望有。但沒有魔法能使堆運行加快—因此不要期望在產品出貨之前的最后一星期能夠大為改觀。如果提前規劃堆策略,情況將會大大好轉。調整使用堆的方法,減少對堆的操作是提高性能的良方。

            如何減少使用堆操作?通過利用數據結構內的位置可減少堆操作的次數。請考慮下列實例:

            struct ObjectA {
               // objectA 的數據 
            }

            struct ObjectB {
               // objectB 的數據 
            }

            // 同時使用 objectA 和 objectB

            //
            // 使用指針 
            //
            struct ObjectB {
               struct ObjectA * pObjA;
               // objectB 的數據 
            }

            //
            // 使用嵌入
            //
            struct ObjectB {
               struct ObjectA pObjA;
               // objectB 的數據 
            }

            //
            // 集合 – 在另一對象內使用 objectA 和 objectB
            //

            struct ObjectX {
               struct ObjectA  objA;
               struct ObjectB  objB;
            }

            避免使用指針關聯兩個數據結構。如果使用指針關聯兩個數據結構,前面實例中的對象 A 和 B 將被分別分配和釋放。這會增加額外開銷—我們要避免這種做法。


            把帶指針的子對象嵌入父對象。當對象中有指針時,則意味著對象中有動態元素(百分之八十)和沒有引用的新位置。嵌入增加了位置從而減少了進一步分配/釋放的需求。這將提高應用程序的性能。


            合并小對象形成大對象(聚合)。聚合減少分配和釋放的塊的數量。如果有幾個開發者,各自開發設計的不同部分,則最終會有許多小對象需要合并。集成的挑戰就是要找到正確的聚合邊界。


            內聯緩沖區能夠滿足百分之八十的需要(aka 80-20 規則)。個別情況下,需要內存緩沖區來保存字符串/二進制數據,但事先不知道總字節數。估計并內聯一個大小能滿足百分之八十需要的緩沖區。對剩余的百分之二十,可以分配一個新的緩沖區和指向這個緩沖區的指針。這樣,就減少分配和釋放調用并增加數據的位置空間,從根本上提高代碼的性能。


            在塊中分配對象(塊化)。塊化是以組的方式一次分配多個對象的方法。如果對列表的項連續跟蹤,例如對一個 {名稱,值} 對的列表,有兩種選擇:選擇一是為每一個“名稱-值”對分配一個節點;選擇二是分配一個能容納(如五個)“名稱-值”對的結構。例如,一般情況下,如果存儲四對,就可減少節點的數量,如果需要額外的空間數量,則使用附加的鏈表指針。 
            塊化是友好的處理器高速緩存,特別是對于 L1-高速緩存,因為它提供了增加的位置 —不用說對于塊分配,很多數據塊會在同一個虛擬頁中。

            正確使用 _amblksiz。C 運行時 (CRT) 有它的自定義前端分配程序,該分配程序從后端(Win32 堆)分配大小為 _amblksiz 的塊。將 _amblksiz 設置為較高的值能潛在地減少對后端的調用次數。這只對廣泛使用 CRT 的程序適用。
            使用上述技術將獲得的好處會因對象類型、大小及工作量而有所不同。但總能在性能和可升縮性方面有所收獲。另一方面,代碼會有點特殊,但如果經過深思熟慮,代碼還是很容易管理的。

            其他提高性能的技術
            下面是一些提高速度的技術: 

            使用 Windows NT5 堆 
            由于幾個同事的努力和辛勤工作,1998 年初 Microsoft Windows(R) 2000 中有了幾個重大改進:

            改進了堆代碼內的鎖定。堆代碼對每堆一個鎖。全局鎖保護堆數據結構,防止多線程式的使用。但不幸的是,在高通信量的情況下,堆仍受困于全局鎖,導致高競爭和低性能。Windows 2000 中,鎖內代碼的臨界區將競爭的可能性減到最小,從而提高了可伸縮性。


            使用 “Lookaside”列表。堆數據結構對塊的所有空閑項使用了大小在 8 到 1,024 字節(以 8-字節遞增)的快速高速緩存。快速高速緩存最初保護在全局鎖內。現在,使用 lookaside 列表來訪問這些快速高速緩存空閑列表。這些列表不要求鎖定,而是使用 64 位的互鎖操作,因此提高了性能。


            內部數據結構算法也得到改進。
            這些改進避免了對分配高速緩存的需求,但不排除其他的優化。使用 Windows NT5 堆評估您的代碼;它對小于 1,024 字節 (1 KB) 的塊(來自前端分配程序的塊)是最佳的。GlobalAlloc() 和 LocalAlloc() 建立在同一堆上,是存取每個進程堆的通用機制。如果希望獲得高的局部性能,則使用 Heap(R) API 來存取每個進程堆,或為分配操作創建自己的堆。如果需要對大塊操作,也可以直接使用 VirtualAlloc() / VirtualFree() 操作。

            上述改進已在 Windows 2000 beta 2 和 Windows NT 4.0 SP4 中使用。改進后,堆鎖的競爭率顯著降低。這使所有 Win32 堆的直接用戶受益。CRT 堆建立于 Win32 堆的頂部,但它使用自己的小塊堆,因而不能從 Windows NT 改進中受益。(Visual C++ 版本 6.0 也有改進的堆分配程序。)

            使用分配高速緩存 
            分配高速緩存允許高速緩存分配的塊,以便將來重用。這能夠減少對進程堆(或全局堆)的分配/釋放調用的次數,也允許最大限度的重用曾經分配的塊。另外,分配高速緩存允許收集統計信息,以便較好地理解對象在較高層次上的使用。

            典型地,自定義堆分配程序在進程堆的頂部實現。自定義堆分配程序與系統堆的行為很相似。主要的差別是它在進程堆的頂部為分配的對象提供高速緩存。高速緩存設計成一套固定大小(如 32 字節、64 字節、128 字節等)。這一個很好的策略,但這種自定義堆分配程序丟失與分配和釋放的對象相關的“語義信息”。 

            與自定義堆分配程序相反,“分配高速緩存”作為每類分配高速緩存來實現。除能夠提供自定義堆分配程序的所有好處之外,它們還能夠保留大量語義信息。每個分配高速緩存處理程序與一個目標二進制對象關聯。它能夠使用一套參數進行初始化,這些參數表示并發級別、對象大小和保持在空閑列表中的元素的數量等。分配高速緩存處理程序對象維持自己的私有空閑實體池(不超過指定的閥值)并使用私有保護鎖。合在一起,分配高速緩存和私有鎖減少了與主系統堆的通信量,因而提供了增加的并發、最大限度的重用和較高的可伸縮性。

            需要使用清理程序來定期檢查所有分配高速緩存處理程序的活動情況并回收未用的資源。如果發現沒有活動,將釋放分配對象的池,從而提高性能。

            可以審核每個分配/釋放活動。第一級信息包括對象、分配和釋放調用的總數。通過查看它們的統計信息可以得出各個對象之間的語義關系。利用以上介紹的許多技術之一,這種關系可以用來減少內存分配。

            分配高速緩存也起到了調試助手的作用,幫助您跟蹤沒有完全清除的對象數量。通過查看動態堆棧返回蹤跡和除沒有清除的對象之外的簽名,甚至能夠找到確切的失敗的調用者。

            MP 堆 
            MP 堆是對多處理器友好的分布式分配的程序包,在 Win32 SDK(Windows NT 4.0 和更新版本)中可以得到。最初由 JVert 實現,此處堆抽象建立在 Win32 堆程序包的頂部。MP 堆創建多個 Win32 堆,并試圖將分配調用分布到不同堆,以減少在所有單一鎖上的競爭。

            本程序包是好的步驟 —一種改進的 MP-友好的自定義堆分配程序。但是,它不提供語義信息和缺乏統計功能。通常將 MP 堆作為 SDK 庫來使用。如果使用這個 SDK 創建可重用組件,您將大大受益。但是,如果在每個 DLL 中建立這個 SDK 庫,將增加工作設置。

            重新思考算法和數據結構 
            要在多處理器機器上伸縮,則算法、實現、數據結構和硬件必須動態伸縮。請看最經常分配和釋放的數據結構。試問,“我能用不同的數據結構完成此工作嗎?”例如,如果在應用程序初始化時加載了只讀項的列表,這個列表不必是線性鏈接的列表。如果是動態分配的數組就非常好。動態分配的數組將減少內存中的堆塊和碎片,從而增強性能。

            減少需要的小對象的數量減少堆分配程序的負載。例如,我們在服務器的關鍵處理路徑上使用五個不同的對象,每個對象單獨分配和釋放。一起高速緩存這些對象,把堆調用從五個減少到一個,顯著減少了堆的負載,特別當每秒鐘處理 1,000 個以上的請求時。

            如果大量使用“Automation”結構,請考慮從主線代碼中刪除“Automation BSTR”,或至少避免重復的 BSTR 操作。(BSTR 連接導致過多的重分配和分配/釋放操作。)

            摘要
            對所有平臺往往都存在堆實現,因此有巨大的開銷。每個單獨代碼都有特定的要求,但設計能采用本文討論的基本理論來減少堆之間的相互作用。 

            評價您的代碼中堆的使用。


            改進您的代碼,以使用較少的堆調用:分析關鍵路徑和固定數據結構。


            在實現自定義的包裝程序之前使用量化堆調用成本的方法。


            如果對性能不滿意,請要求 OS 組改進堆。更多這類請求意味著對改進堆的更多關注。


            要求 C 運行時組針對 OS 所提供的堆制作小巧的分配包裝程序。隨著 OS 堆的改進,C 運行時堆調用的成本將減小。


            操作系統(Windows NT 家族)正在不斷改進堆。請隨時關注和利用這些改進。
            Murali Krishnan 是 Internet Information Server (IIS) 組的首席軟件設計工程師。從 1.0 版本開始他就設計 IIS,并成功發行了 1.0 版本到 4.0 版本。Murali 組織并領導 IIS 性能組三年 (1995-1998), 從一開始就影響 IIS 性能。他擁有威斯康星州 Madison 大學的 M.S.和印度 Anna 大學的 B.S.。工作之外,他喜歡閱讀、打排球和家庭烹飪。



            http://community.csdn.net/Expert/FAQ/FAQ_Index.asp?id=172835
            我在學習對象的生存方式的時候見到一種是在堆棧(stack)之中,如下  
            CObject  object;  
            還有一種是在堆(heap)中  如下  
            CObject*  pobject=new  CObject();  
             
            請問  
            (1)這兩種方式有什么區別?  
            (2)堆棧與堆有什么區別??  
             
             
            ---------------------------------------------------------------  
             
            1)  about  stack,  system  will  allocate  memory  to  the  instance  of  object  automatically,  and  to  the
             heap,  you  must  allocate  memory  to  the  instance  of  object  with  new  or  malloc  manually.  
            2)  when  function  ends,  system  will  automatically  free  the  memory  area  of  stack,  but  to  the 
            heap,  you  must  free  the  memory  area  manually  with  free  or  delete,  else  it  will  result  in  memory
            leak.  
            3)棧內存分配運算內置于處理器的指令集中,效率很高,但是分配的內存容量有限。  
            4)堆上分配的內存可以有我們自己決定,使用非常靈活。  

            posted @ 2008-09-09 19:00 深邃者 閱讀(138) | 評論 (0)編輯 收藏

            Ansi、Unicode、UTF8字符串之間的轉換和寫入文本文件

            轉載請注明出處http://m.shnenglu.com/greatws/archive/2008/08/31/60546.html

            最近有人問我關于這個的問題,就此寫一篇blog

            Ansi字符串我們最熟悉,英文占一個字節,漢字2個字節,以一個\0結尾,常用于txt文本文件
            Unicode字符串,每個字符(漢字、英文字母)都占2個字節,以2個連續的\0結尾,NT操作系統內核用的是這種字符串,常被定義為typedef unsigned short wchar_t;所以我們有時常會見到什么char*無法轉換為unsigned short*之類的錯誤,其實就是unicode
            UTF8是Unicode一種壓縮形式,英文A在unicode中表示為0x0041,老外覺得這種存儲方式太浪費,因為浪費了50%的空間,于是就把英文壓縮成1個字節,成了utf8編碼,但是漢字在utf8中占3個字節,顯然用做中文不如ansi合算,這就是中國的網頁用作ansi編碼而老外的網頁常用utf8的原因。
            UTF8在還游戲里運用的很廣泛,比如WOW的lua腳本等

            下面來說一下轉換,主要用代碼來說明吧
            寫文件我用了CFile類,其實用FILE*之類的也是一樣,寫文件和字符串什么類別沒有關系,硬件只關心數據和長度

            Ansi轉Unicode
            介紹2種方法


            void CConvertDlg::OnBnClickedButtonAnsiToUnicode()
            {
                
            // ansi to unicode
                char* szAnsi = "abcd1234你我他";
                
            //預轉換,得到所需空間的大小
                int wcsLen = ::MultiByteToWideChar(CP_ACP, NULL, szAnsi, strlen(szAnsi), NULL, 0);
                
            //分配空間要給'\0'留個空間,MultiByteToWideChar不會給'\0'空間
                wchar_t* wszString = new wchar_t[wcsLen + 1];
                
            //轉換
                ::MultiByteToWideChar(CP_ACP, NULL, szAnsi, strlen(szAnsi), wszString, wcsLen);
                
            //最后加上'\0'
                wszString[wcsLen] = '\0';
                
            //unicode版的MessageBox API
                ::MessageBoxW(GetSafeHwnd(), wszString, wszString, MB_OK);

                
            //接下來寫入文本
                
            //寫文本文件,頭2個字節0xfeff,低位0xff寫在前
                CFile cFile;
                cFile.Open(_T(
            "1.txt"), CFile::modeWrite | CFile::modeCreate);
                
            //文件開頭
                cFile.SeekToBegin();
                cFile.Write(
            "\xff\xfe"2);
                
            //寫入內容
                cFile.Write(wszString, wcsLen * sizeof(wchar_t));
                cFile.Flush();
                cFile.Close();
                delete[] wszString;
                wszString 
            =NULL;


                
            //方法2
                
            //設置當前地域信息,不設置的話,使用這種方法,中文不會正確顯示
                
            //需要#include<locale.h>
                setlocale(LC_CTYPE, "chs"); 
                wchar_t wcsStr[
            100];
                
            //注意下面是大寫S,在unicode中,代表后面是ansi字符串
                
            //swprintf是sprintf的unicode版本
                
            //格式的前面要加大寫L,代表是unicode
                swprintf(wcsStr, L"%S", szAnsi);
                ::MessageBoxW(GetSafeHwnd(), wcsStr, wcsStr, MB_OK);

            }


            Unicode轉Ansi
            也是2種方法

            void CConvertDlg::OnBnClickedButtonUnicodeToAnsi()
            {
                
            // unicode to ansi
                wchar_t* wszString = L"abcd1234你我他";
                
            //預轉換,得到所需空間的大小,這次用的函數和上面名字相反
                int ansiLen = ::WideCharToMultiByte(CP_ACP, NULL, wszString, wcslen(wszString), NULL, 0, NULL, NULL);
                
            //同上,分配空間要給'\0'留個空間
                char* szAnsi = new char[ansiLen + 1];
                
            //轉換
                
            //unicode版對應的strlen是wcslen
                ::WideCharToMultiByte(CP_ACP, NULL, wszString, wcslen(wszString), szAnsi, ansiLen, NULL, NULL);
                
            //最后加上'\0'
                szAnsi[ansiLen] = '\0';
                
            //Ansi版的MessageBox API
                ::MessageBoxA(GetSafeHwnd(), szAnsi, szAnsi, MB_OK);

                
            //接下來寫入文本
                
            //寫文本文件,ANSI文件沒有BOM
                CFile cFile;
                cFile.Open(_T(
            "1.txt"), CFile::modeWrite | CFile::modeCreate);
                
            //文件開頭
                cFile.SeekToBegin();
                
            //寫入內容
                cFile.Write(szAnsi, ansiLen * sizeof(char));
                cFile.Flush();
                cFile.Close();
                delete[] szAnsi;
                szAnsi 
            =NULL;


                
            //方法2
                
            //和上面一樣有另一種方法
                setlocale(LC_CTYPE, "chs"); 
                
            char szStr[100];
                
            //注意下面是大寫,在ansi中,代表后面是unicode字符串
                
            //sprintf
                sprintf(szStr, "%S", wszString);
                ::MessageBoxA(GetSafeHwnd(), szStr, szStr, MB_OK);
            }


            Unicode轉UTF8

            void CConvertDlg::OnBnClickedButtonUnicodeToU8()
            {
                
            // unicode to UTF8
                wchar_t* wszString = L"abcd1234你我他";
                
            //預轉換,得到所需空間的大小,這次用的函數和上面名字相反
                int u8Len = ::WideCharToMultiByte(CP_UTF8, NULL, wszString, wcslen(wszString), NULL, 0, NULL, NULL);
                
            //同上,分配空間要給'\0'留個空間
                
            //UTF8雖然是Unicode的壓縮形式,但也是多字節字符串,所以可以以char的形式保存
                char* szU8 = new char[u8Len + 1];
                
            //轉換
                
            //unicode版對應的strlen是wcslen
                ::WideCharToMultiByte(CP_UTF8, NULL, wszString, wcslen(wszString), szU8, u8Len, NULL, NULL);
                
            //最后加上'\0'
                szU8[u8Len] = '\0';
                
            //MessageBox不支持UTF8,所以只能寫文件

                
            //接下來寫入文本
                
            //寫文本文件,UTF8的BOM是0xbfbbef
                CFile cFile;
                cFile.Open(_T(
            "1.txt"), CFile::modeWrite | CFile::modeCreate);
                
            //文件開頭
                cFile.SeekToBegin();
                
            //寫BOM,同樣低位寫在前
                cFile.Write("\xef\xbb\xbf"3);
                
            //寫入內容
                cFile.Write(szU8, u8Len * sizeof(char));
                cFile.Flush();
                cFile.Close();
                delete[] szU8;
                szU8 
            =NULL;

            }

            UTF8轉UNICODE

            void CConvertDlg::OnBnClickedButtonU8ToUnicode()
            {
                
            //UTF8 to Unicode
                
            //由于中文直接復制過來會成亂碼,編譯器有時會報錯,故采用16進制形式
                char* szU8 = "abcd1234\xe4\xbd\xa0\xe6\x88\x91\xe4\xbb\x96\x00";
                
            //預轉換,得到所需空間的大小
                int wcsLen = ::MultiByteToWideChar(CP_UTF8, NULL, szU8, strlen(szU8), NULL, 0);
                
            //分配空間要給'\0'留個空間,MultiByteToWideChar不會給'\0'空間
                wchar_t* wszString = new wchar_t[wcsLen + 1];
                
            //轉換
                ::MultiByteToWideChar(CP_UTF8, NULL, szU8, strlen(szU8), wszString, wcsLen);
                
            //最后加上'\0'
                wszString[wcsLen] = '\0';
                
            //unicode版的MessageBox API
                ::MessageBoxW(GetSafeHwnd(), wszString, wszString, MB_OK);

                
            //寫文本同ansi to unicode
            }



            Ansi轉換utf8和utf8轉換Ansi就是上面2個的結合,把unicode作為中間量,進行2次轉換即可

            posted @ 2008-09-04 16:27 深邃者 閱讀(379) | 評論 (0)編輯 收藏

            讀寫ini文件

                    ini文件(即Initialization file),這種類型的文件中通常存放的是一個程序的初始化信息。ini文件由若干個節(Section)組成,每個Section由若干鍵(Key)組成,每個Key可以賦相應的值。讀寫ini文件實際上就是讀寫某個的Section中相應的Key的值,而這只要借助幾個函數即可完成。
            一、向ini文件中寫入信息的函數
            1. 把信息寫入系統的win.ini文件

            BOOL WriteProfileString(
                 LPCTSTR lpAppName, // 節的名字,是一個以0結束的字符串
                 LPCTSTR lpKeyName, // 鍵的名字,是一個以0結束的字符串。若為NULL,則刪除整個節
                 LPCTSTR lpString      // 鍵的值,是一個以0結束的字符串。若為NULL,則刪除對應的鍵
            )


            2. 把信息寫入自己定義的.ini文件
            BOOL WritePrivateProfileString(
                 LPCTSTR lpAppName,     // 同上
                 LPCTSTR lpKeyName,     // 同上
                 LPCTSTR lpString,      // 同上
                 LPCTSTR lpFileName     // 要寫入的文件的文件名。若該ini文件與程序在同一個目錄下,也可使用相對
                       //路徑,否則需要給出絕度路徑。
            )

            如:
            ::WriteProfileString("Test","id","xym");  
            //在win.ini中創建一個Test節,并在該節中創建一個鍵id,其值為xym

            ::WritePrivateProfileString("Test","id","xym","d:\\vc\\Ex1\\ex1.ini");
            //在Ex1目錄下的ex1.ini中創建一個Test節,并在該節中創建一個鍵id,其值為xym

            //若Ex1.ini文件與讀寫該文件的程序在同一個目錄下,則上面語句也可寫為:
            ::WritePrivateProfileString("Test","id","xym",".\\ex1.ini");

            需要注意的是,C系列的語言中,轉義字符'\\'表示反斜線'\'。另外,當使用相對路徑時,\\前的.號不能丟掉了。

            二、從ini文件中讀取數據的函數
            1、從系統的win.ini文件中讀取信息
            (1) 讀取字符串

            DWORD GetProfileString(
                 LPCTSTR lpAppName,           // 節名
                 LPCTSTR lpKeyName,           // 鍵名,讀取該鍵的值
                 LPCTSTR lpDefault,           // 若指定的鍵不存在,該值作為讀取的默認值
                 LPTSTR lpReturnedString,     // 一個指向緩沖區的指針,接收讀取的字符串
                 DWORD nSize                  // 指定lpReturnedString指向的緩沖區的大小
            )

            如:
            CString str;
            ::GetProfileString("Test","id","Error",str.GetBuffer(20),20);

            (2) 讀取整數
            UINT GetProfileInt(
                 LPCTSTR lpAppName,     // 同上
                 LPCTSTR lpKeyName,     // 同上
                 INT nDefault           // 若指定的鍵名不存在,該值作為讀取的默認值
            )

            如使用以下語句寫入了年齡信息:
            ::WriteProfileString("Test","age","25");  
            //在win.ini中創建一個Test節,并在該節中創建一個鍵age,其值為25

            則可用以下語句讀取age鍵的值:
            int age;
            age=::GetProfileInt("Test","age",0);

            2、從自己的ini文件中讀取信息
            (1) 讀取字符串
            DWORD GetPrivateProfileString(
                 LPCTSTR lpAppName,           // 同1(1)
                 LPCTSTR lpKeyName,           // 同1(1)
                 LPCTSTR lpDefault,           // 同1(1)
                 LPTSTR lpReturnedString,     // 同1(1)
                 DWORD nSize,                 // 同1(1)
                 LPCTSTR lpFileName           // 讀取信息的文件名。若該ini文件與程序在同一個目錄下,也可使用相      
                       //對路徑,否則需要給出絕度路徑。
            )

            如:
            CString str;
            ::GetPrivateProfileString("Test","id","Error",str.GetBuffer(20),20,".\\ex1.ini");
            或:
            ::GetPrivateProfileString("Test","id","Error",str.GetBuffer(20),20,"d:\\vc\\Ex1\\ex1.ini");

            (2) 讀取整數

            UINT GetPrivateProfileInt(
                 LPCTSTR lpAppName,     // 同上
                 LPCTSTR lpKeyName,     // 同上
                 INT nDefault,          // 若指定的鍵名不存在,該值作為讀取的默認值
                 LPCTSTR lpFileName     // 同上
            )

            如使用以下語句寫入了年齡信息:
            ::WritePrivateProfileString("Test","age","25",".\\ex1.ini");  
            //在ex1.ini中創建一個Test節,并在該節中創建一個鍵age,其值為25

            則可用以下語句讀取age鍵的值:
            int age;
            age=::GetPrivateProfileInt("Test","age",0,".\\ex1.ini");

            三、 刪除鍵值或節

                  回顧一下WriteProfileString函數的說明
            BOOL WriteProfileString(
                 LPCTSTR lpAppName, // 節的名字,是一個以0結束的字符串
                 LPCTSTR lpKeyName, // 鍵的名字,是一個以0結束的字符串。若為NULL,則刪除整個節
                 LPCTSTR lpString      // 鍵的值,是一個以0結束的字符串。若為NULL,則刪除對應的鍵
            )

                  由此可見,要刪除某個節,只需要將WriteProfileString第二個參數設為NULL即可。而要刪除某個鍵,則只需要將該函數的第三個參數設為NULL即可。這是刪除系統的win.ini中的節或鍵,類似的,要刪除自己定義的ini文件中的節或鍵,也可做相同的操作。
                  如:
            ::WriteProfileString("Test",NULL,NULL);     //刪除win.ini中的Test節
            ::WriteProfileString("Test","id",NULL);     //刪除win.ini中的id鍵

            ::WritePrivateProfileString("Test",NULL,NULL,".\\ex1.ini");     //刪除ex1.ini中的Test節
            ::WritePrivateProfileString("Test","id",NULL,".\\ex1.ini");     //刪除ex1.ini中的id鍵

            四、如何判斷一個ini文件中有多少個節
                  要判斷一個ini文件中有多少個節,最簡單的辦法就是將所有的節名都找出來,然后統計節名的個數。而要將所有的節名找出來,使用GetPrivateProfileSectionNames函數就可以了,其原型如下:
            DWORD GetPrivateProfileSectionNames(
                 LPTSTR lpszReturnBuffer,     // 指向一個緩沖區,用來保存返回的所有節名
                 DWORD nSize,                 // 參數lpszReturnBuffer的大小
                 LPCTSTR lpFileName           // 文件名,若該ini文件與程序在同一個目錄下,

                                                           //也可使用相對路徑,否則需要給出絕度路徑
            )

            下面的是用來統計一個ini文件中共有多少個節的函數,當然,如果需要同時找到每個節中的各個鍵及其值,根據找到節名就可以很容易的得到了。


            /*統計共有多少個節
            節名的分離方法:若chSectionNames數組的第一字符是'\0'字符,則表明
            有0個節。否則,從chSectionNames數組的第一個字符開始,順序往后找,
            直到找到一個'\0'字符,若該字符的后繼字符不是 '\0'字符,則表明前
            面的字符組成一個節名。若連續找到兩個'\0'字符,則統計結束*/


            int CTestDlg::CalcCount(void)
            {
            TCHAR      chSectionNames[2048]={0};      //所有節名組成的字符數組
            char * pSectionName; //保存找到的某個節名字符串的首地址
            int i;      //i指向數組chSectionNames的某個位置,從0開始,順序后移
            int j=0;     //j用來保存下一個節名字符串的首地址相對于當前i的位置偏移量
            int count=0;     //統計節的個數

            //CString name;
            //char id[20];
            ::GetPrivateProfileSectionNames(chSectionNames,2048,".\\ex1.ini");   
            for(i=0;i<2048;i++,j++)
            {
                 if(chSectionNames[0]=='\0')
                  break;      //如果第一個字符就是0,則說明ini中一個節也沒有
                 if(chSectionNames[i]=='\0')
                 {
                  pSectionName=&chSectionNames[i-j]; //找到一個0,則說明從這個字符往前,減掉j個偏移量,
                       //就是一個節名的首地址

                  j=-1;        //找到一個節名后,j的值要還原,以統計下一個節名地址的偏移量
                       //賦成-1是因為節名字符串的最后一個字符0是終止符,不能作為節名

                       //的一部分
                  /*::GetPrivateProfileString(pSectionName,"id","Error",id,20,".\\ex1.ini");
                  name.Format("%s",id);*/   
                  //在獲取節名的時候可以獲取該節中鍵的值,前提是我們知道該節中有哪些鍵。
               
                  AfxMessageBox(pSectionName);     //把找到的顯示出來

                  if(chSectionNames[i+1]==0)
                  {
                    break;     //當兩個相鄰的字符都是0時,則所有的節名都已找到,循環終止
                  }
                 }   
              
            }

            return count;
            }

            posted @ 2008-09-02 15:19 深邃者 閱讀(218) | 評論 (0)編輯 收藏

            Window API函數大全(2)

            10. API之硬件與系統函數
            ActivateKeyboardLayout 激活一個新的鍵盤布局。鍵盤布局定義了按鍵在一種物理性鍵盤上的位置與含義
            Beep 用于生成簡單的聲音
            CharToOem 將一個字串從ANSI字符集轉換到OEM字符集
            ClipCursor 將指針限制到指定區域
            ConvertDefaultLocale 將一個特殊的地方標識符轉換成真實的地方ID
            CreateCaret 根據指定的信息創建一個插入符(光標),并將它選定為指定窗口的默認插入符
            DestroyCaret 清除(破壞)一個插入符
            EnumCalendarInfo 枚舉在指定“地方”環境中可用的日歷信息
            EnumDateFormats 列舉指定的“當地”設置中可用的長、短日期格式
            EnumSystemCodePages 枚舉系統中已安裝或支持的代碼頁
            EnumSystemLocales 枚舉系統已經安裝或提供支持的“地方”設置
            EnumTimeFormats 枚舉一個指定的地方適用的時間格式
            ExitWindowsEx 退出windows,并用特定的選項重新啟動
            ExpandEnvironmentStrings 擴充環境字串
            FreeEnvironmentStrings 翻譯指定的環境字串塊
            GetACP 判斷目前正在生效的ANSI代碼頁
            GetAsyncKeyState 判斷函數調用時指定虛擬鍵的狀態
            GetCaretBlinkTime 判斷插入符光標的閃爍頻率
            GetCaretPos 判斷插入符的當前位置
            GetClipCursor 取得一個矩形,用于描述目前為鼠標指針規定的剪切區域
            GetCommandLine 獲得指向當前命令行緩沖區的一個指針
            GetComputerName 取得這臺計算機的名稱
            GetCPInfo 取得與指定代碼頁有關的信息
            GetCurrencyFormat 針對指定的“地方”設置,根據貨幣格式格式化一個數字
            GetCursor 獲取目前選擇的鼠標指針的句柄
            GetCursorPos 獲取鼠標指針的當前位置
            GetDateFormat 針對指定的“當地”格式,對一個系統日期進行格式化
            GetDoubleClickTime 判斷連續兩次鼠標單擊之間會被處理成雙擊事件的間隔時間
            GetEnvironmentStrings 為包含了當前環境字串設置的一個內存塊分配和返回一個句柄
            GetEnvironmentVariable 取得一個環境變量的值
            GetInputState 判斷是否存在任何待決(等待處理)的鼠標或鍵盤事件
            GetKBCodePage 由GetOEMCP取代,兩者功能完全相同
            GetKeyboardLayout 取得一個句柄,描述指定應用程序的鍵盤布局
            GetKeyboardLayoutList 獲得系統適用的所有鍵盤布局的一個列表
            GetKeyboardLayoutName 取得當前活動鍵盤布局的名稱
            GetKeyboardState 取得鍵盤上每個虛擬鍵當前的狀態
            GetKeyboardType 了解與正在使用的鍵盤有關的信息
            GetKeyNameText 在給出掃描碼的前提下,判斷鍵名
            GetKeyState 針對已處理過的按鍵,在最近一次輸入信息時,判斷指定虛擬鍵的狀態
            GetLastError 針對之前調用的api函數,用這個函數取得擴展錯誤信息
            GetLocaleInfo 取得與指定“地方”有關的信息
            GetLocalTime 取得本地日期和時間
            GetNumberFormat 針對指定的“地方”,按特定的格式格式化一個數字
            GetOEMCP 判斷在OEM和ANSI字符集間轉換的windows代碼頁
            GetQueueStatus 判斷應用程序消息隊列中待決(等待處理)的消息類型
            GetSysColor 判斷指定windows顯示對象的顏色
            GetSystemDefaultLangID 取得系統的默認語言ID
            GetSystemDefaultLCID 取得當前的默認系統“地方”
            GetSystemInfo 取得與底層硬件平臺有關的信息
            GetSystemMetrics 返回與windows環境有關的信息
            GetSystemPowerStatus 獲得與當前系統電源狀態有關的信息
            GetSystemTime 取得當前系統時間,這個時間采用的是“協同世界時間”(即UTC,也叫做GMT)格式
            GetSystemTimeAdjustment 使內部系統時鐘與一個外部的時鐘信號源同步
            GetThreadLocale 取得當前線程的地方ID
            GetTickCount 用于獲取自windows啟動以來經歷的時間長度(毫秒)
            GetTimeFormat 針對當前指定的“地方”,按特定的格式格式化一個系統時間
            GetTimeZoneInformation 取得與系統時區設置有關的信息
            GetUserDefaultLangID 為當前用戶取得默認語言ID
            GetUserDefaultLCID 取得當前用戶的默認“地方”設置
            GetUserName 取得當前用戶的名字
            GetVersion 判斷當前運行的Windows和DOS版本
            GetVersionEx 取得與平臺和操作系統有關的版本信息
            HideCaret 在指定的窗口隱藏插入符(光標)
            IsValidCodePage 判斷一個代碼頁是否有效
            IsValidLocale 判斷地方標識符是否有效
            keybd_event 這個函數模擬了鍵盤行動
            LoadKeyboardLayout 載入一個鍵盤布局
            MapVirtualKey 根據指定的映射類型,執行不同的掃描碼和字符轉換
            MapVirtualKeyEx 根據指定的映射類型,執行不同的掃描碼和字符轉換
            MessageBeep 播放一個系統聲音。系統聲音的分配方案是在控制面板里決定的
            mouse_event 模擬一次鼠標事件
            OemKeyScan 判斷OEM字符集中的一個ASCII字符的掃描碼和Shift鍵狀態
            OemToChar 將OEM字符集的一個字串轉換到ANSI字符集
            SetCaretBlinkTime 指定插入符(光標)的閃爍頻率
            SetCaretPos 指定插入符的位置
            SetComputerName 設置新的計算機名
            SetCursor 將指定的鼠標指針設為當前指針
            SetCursorPos 設置指針的位置
            SetDoubleClickTime 設置連續兩次鼠標單擊之間能使系統認為是雙擊事件的間隔時間
            SetEnvironmentVariable 將一個環境變量設為指定的值
            SetKeyboardState 設置每個虛擬鍵當前在鍵盤上的狀態
            SetLocaleInfo 改變用戶“地方”設置信息
            SetLocalTime 設置當前地方時間
            SetSysColors 設置指定窗口顯示對象的顏色
            SetSystemCursor 改變任何一個標準系統指針
            SetSystemTime 設置當前系統時間
            SetSystemTimeAdjustment 定時添加一個校準值使內部系統時鐘與一個外部的時鐘信號源同步
            SetThreadLocale 為當前線程設置地方
            SetTimeZoneInformation 設置系統時區信息
            ShowCaret 在指定的窗口里顯示插入符(光標)
            ShowCursor 控制鼠標指針的可視性
            SwapMouseButton 決定是否互換鼠標左右鍵的功能
            SystemParametersInfo 獲取和設置數量眾多的windows系統參數
            SystemTimeToTzSpecificLocalTime 將系統時間轉換成地方時間
            ToAscii 根據當前的掃描碼和鍵盤信息,將一個虛擬鍵轉換成ASCII字符
            ToUnicode 根據當前的掃描碼和鍵盤信息,將一個虛擬鍵轉換成Unicode字符
            UnloadKeyboardLayout 卸載指定的鍵盤布局
            VkKeyScan 針對Windows字符集中一個ASCII字符,判斷虛擬鍵碼和Shift鍵的狀態

            11. API之進程和線程函數

            CancelWaitableTimer 這個函數用于取消一個可以等待下去的計時器操作
            CallNamedPipe 這個函數由一個希望通過管道通信的一個客戶進程調用
            ConnectNamedPipe 指示一臺服務器等待下去,直至客戶機同一個命名管道連接
            CreateEvent 創建一個事件對象
            CreateMailslot 創建一個郵路。返回的句柄由郵路服務器使用(收件人)
            CreateMutex 創建一個互斥體(MUTEX)
            CreateNamedPipe 創建一個命名管道。返回的句柄由管道的服務器端使用
            CreatePipe 創建一個匿名管道
            CreateProcess 創建一個新進程(比如執行一個程序)
            CreateSemaphore 創建一個新的信號機
            CreateWaitableTimer 創建一個可等待的計時器對象
            DisconnectNamedPipe 斷開一個客戶與一個命名管道的連接
            DuplicateHandle 在指出一個現有系統對象當前句柄的情況下,為那個對象創建一個新句柄
            ExitProcess 中止一個進程
            FindCloseChangeNotification 關閉一個改動通知對象
            FindExecutable 查找與一個指定文件關聯在一起的程序的文件名
            FindFirstChangeNotification 創建一個文件通知對象。該對象用于監視文件系統發生的變化
            FindNextChangeNotification 重設一個文件改變通知對象,令其繼續監視下一次變化
            FreeLibrary 釋放指定的動態鏈接庫
            GetCurrentProcess 獲取當前進程的一個偽句柄
            GetCurrentProcessId 獲取當前進程一個唯一的標識符
            GetCurrentThread 獲取當前線程的一個偽句柄
            GetCurrentThreadId 獲取當前線程一個唯一的線程標識符
            GetExitCodeProces 獲取一個已中斷進程的退出代碼
            GetExitCodeThread 獲取一個已中止線程的退出代碼
            GetHandleInformation 獲取與一個系統對象句柄有關的信息
            GetMailslotInfo 獲取與一個郵路有關的信息
            GetModuleFileName 獲取一個已裝載模板的完整路徑名稱
            GetModuleHandle 獲取一個應用程序或動態鏈接庫的模塊句柄
            GetPriorityClass 獲取特定進程的優先級別
            GetProcessShutdownParameters 調查系統關閉時一個指定的進程相對于其它進程的關閉早遲情況
            GetProcessTimes 獲取與一個進程的經過時間有關的信息
            GetProcessWorkingSetSize 了解一個應用程序在運行過程中實際向它交付了多大容量的內存
            GetSartupInfo 獲取一個進程的啟動信息
            GetThreadPriority 獲取特定線程的優先級別
            GetTheardTimes 獲取與一個線程的經過時間有關的信息
            GetWindowThreadProcessId 獲取與指定窗口關聯在一起的一個進程和線程標識符
            LoadLibrary 載入指定的動態鏈接庫,并將它映射到當前進程使用的地址空間
            LoadLibraryEx 裝載指定的動態鏈接庫,并為當前進程把它映射到地址空間
            LoadModule 載入一個Windows應用程序,并在指定的環境中運行
            MsgWaitForMultipleObjects 等侯單個對象或一系列對象發出信號。如返回條件已經滿足,則立即返回
            SetPriorityClass 設置一個進程的優先級別
            SetProcessShutdownParameters 在系統關閉期間,為指定進程設置他相對于其它程序的關閉順序
            SetProcessWorkingSetSize 設置操作系統實際劃分給進程使用的內存容量
            SetThreadPriority 設定線程的優先級別
            ShellExecute 查找與指定文件關聯在一起的程序的文件名
            TerminateProcess 結束一個進程
            WinExec 運行指定的程序

            12. API之控件與消息函數

            AdjustWindowRect 給定一種窗口樣式,計算獲得目標客戶區矩形所需的窗口大小
            AnyPopup 判斷屏幕上是否存在任何彈出式窗口
            ArrangeIconicWindows 排列一個父窗口的最小化子窗口
            AttachThreadInput 連接線程輸入函數
            BeginDeferWindowPos 啟動構建一系列新窗口位置的過程
            BringWindowToTop 將指定的窗口帶至窗口列表頂部
            CascadeWindows 以層疊方式排列窗口
            ChildWindowFromPoint 返回父窗口中包含了指定點的第一個子窗口的句柄
            ClientToScreen 判斷窗口內以客戶區坐標表示的一個點的屏幕坐標
            CloseWindow 最小化指定的窗口
            CopyRect 矩形內容復制
            DeferWindowPos 該函數為特定的窗口指定一個新窗口位置
            DestroyWindow 清除指定的窗口以及它的所有子窗口
            DrawAnimatedRects 描繪一系列動態矩形
            EnableWindow 指定的窗口里允許或禁止所有鼠標及鍵盤輸入
            EndDeferWindowPos 同時更新DeferWindowPos調用時指定的所有窗口的位置及狀態
            EnumChildWindows 為指定的父窗口枚舉子窗口
            EnumThreadWindows 枚舉與指定任務相關的窗口
            EnumWindows 枚舉窗口列表中的所有父窗口
            EqualRect 判斷兩個矩形結構是否相同
            FindWindow 尋找窗口列表中第一個符合指定條件的頂級窗口
            FindWindowEx 在窗口列表中尋找與指定條件相符的第一個子窗口
            FlashWindow 閃爍顯示指定窗口
            GetActiveWindow 獲得活動窗口的句柄
            GetCapture 獲得一個窗口的句柄,這個窗口位于當前輸入線程,且擁有鼠標捕獲(鼠標活動由它接收)
            GetClassInfo 取得WNDCLASS結構(或WNDCLASSEX結構)的一個副本,結構中包含了與指定類有關的信息
            GetClassLong 取得窗口類的一個Long變量條目
            GetClassName 為指定的窗口取得類名
            GetClassWord 為窗口類取得一個整數變量
            GetClientRect 返回指定窗口客戶區矩形的大小
            GetDesktopWindow 獲得代表整個屏幕的一個窗口(桌面窗口)句柄
            GetFocus 獲得擁有輸入焦點的窗口的句柄
            GetForegroundWindow 獲得前臺窗口的句柄
            GetLastActivePopup 獲得在一個給定父窗口中最近激活過的彈出式窗口的句柄
            GetParent 判斷指定窗口的父窗口
            GetTopWindow 搜索內部窗口列表,尋找隸屬于指定窗口的頭一個窗口的句柄
            GetUpdateRect 獲得一個矩形,它描敘了指定窗口中需要更新的那一部分
            GetWindow 獲得一個窗口的句柄,該窗口與某源窗口有特定的關系
            GetWindowContextHelpId 取得與窗口關聯在一起的幫助場景ID
            GetWindowLong 從指定窗口的結構中取得信息
            GetWindowPlacement 獲得指定窗口的狀態及位置信息
            GetWindowRect 獲得整個窗口的范圍矩形,窗口的邊框、標題欄、滾動條及菜單等都在這個矩形內
            GetWindowText 取得一個窗體的標題(caption)文字,或者一個控件的內容
            GetWindowTextLength 調查窗口標題文字或控件內容的長短
            GetWindowWord 獲得指定窗口結構的信息
            InflateRect 增大或減小一個矩形的大小
            IntersectRect 這個函數在lpDestRect里載入一個矩形,它是lpSrc1Rect與lpSrc2Rect兩個矩形的交集
            InvalidateRect 屏蔽一個窗口客戶區的全部或部分區域
            IsChild 判斷一個窗口是否為另一窗口的子或隸屬窗口
            IsIconic 判斷窗口是否已最小化
            IsRectEmpty 判斷一個矩形是否為空
            IsWindow 判斷一個窗口句柄是否有效
            IsWindowEnabled 判斷窗口是否處于活動狀態
            IsWindowUnicode 判斷一個窗口是否為Unicode窗口。這意味著窗口為所有基于文本的消息都接收Unicode文字
            IsWindowVisible 判斷窗口是否可見
            IsZoomed 判斷窗口是否最大化
            LockWindowUpdate 鎖定指定窗口,禁止它更新
            MapWindowPoints 將一個窗口客戶區坐標的點轉換到另一窗口的客戶區坐標系統
            MoveWindow 改變指定窗口的位置和大小
            OffsetRect 通過應用一個指定的偏移,從而讓矩形移動起來
            OpenIcon 恢復一個最小化的程序,并將其激活
            PtInRect 判斷指定的點是否位于矩形內部
            RedrawWindow 重畫全部或部分窗口
            ReleaseCapture 為當前的應用程序釋放鼠標捕獲
            ScreenToClient 判斷屏幕上一個指定點的客戶區坐標
            ScrollWindow 滾動窗口客戶區的全部或一部分
            ScrollWindowEx 根據附加的選項,滾動窗口客戶區的全部或部分
            SetActiveWindow 激活指定的窗口
            SetCapture 將鼠標捕獲設置到指定的窗口
            SetClassLong 為窗口類設置一個Long變量條目
            SetClassWord 為窗口類設置一個條目
            SetFocusAPI 將輸入焦點設到指定的窗口。如有必要,會激活窗口
            SetForegroundWindow 將窗口設為系統的前臺窗口
            SetParent 指定一個窗口的新父
            SetRect 設置指定矩形的內容
            SetRectEmpty 將矩形設為一個空矩形
            SetWindowContextHelpId 為指定的窗口設置幫助場景(上下文)ID
            SetWindowLong 在窗口結構中為指定的窗口設置信息
            SetWindowPlacement 設置窗口狀態和位置信息
            SetWindowPos 為窗口指定一個新位置和狀態
            SetWindowText 設置窗口的標題文字或控件的內容
            SetWindowWord 在窗口結構中為指定的窗口設置信息
            ShowOwnedPopups 顯示或隱藏由指定窗口所有的全部彈出式窗口
            ShowWindow 控制窗口的可見性
            ShowWindowAsync 與ShowWindow相似
            SubtractRect 裝載矩形lprcDst,它是在矩形lprcSrc1中減去lprcSrc2得到的結果
            TileWindows 以平鋪順序排列窗口
            UnionRect 裝載一個lpDestRect目標矩形,它是lpSrc1Rect和lpSrc2Rect聯合起來的結果
            UpdateWindow 強制立即更新窗口
            ValidateRect 校驗窗口的全部或部分客戶區
            WindowFromPoint 返回包含了指定點的窗口的句柄。忽略屏蔽、隱藏以及透明窗口

            消息,就是指Windows發出的一個通知,告訴應用程序某個事情發生了。例如,單擊鼠標、改變窗口尺寸、按下鍵盤上的一個鍵都會使Windows 發送一個消息給應用程序。消息本身是作為一個記錄傳遞給應用程序的,這個記錄中包含了消息的類型以及其他信息。例如,對于單擊鼠標所產生的消息來說,這個 記錄中包含了單擊鼠標時的坐標。這個記錄類型叫做TMsg,

            它在Windows單元中是這樣聲明的:
            type
            TMsg = packed record
            hwnd: HWND; / /窗口句柄
            message: UINT; / /消息常量標識符
            wParam: WPARAM ; // 32位消息的特定附加信息
            lParam: LPARAM ; // 32位消息的特定附加信息
            time: DWORD; / /消息創建時的時間
            pt: TPoint; / /消息創建時的鼠標位置
            end;


            posted @ 2008-08-28 10:30 深邃者 閱讀(429) | 評論 (0)編輯 收藏

            Window API函數大全(3)

            再補一點消息詳解
            消息中有什么?
            是否覺得一個消息記錄中的信息像希臘語一樣?如果是這樣,那么看一看下面的解釋:
            hwnd 32位的窗口句柄。窗口可以是任何類型的屏幕對象,因為Win32能夠維護大多數可視對象的句柄(窗口、對話框、按鈕、編輯框等)。
            message 用于區別其他消息的常量值,這些常量可以是Windows單元中預定義的常量,也可以是自定義的常量。
            wParam 通常是一個與消息有關的常量值,也可能是窗口或控件的句柄。
            lParam 通常是一個指向內存中數據的指針。由于W P a r a m、l P a r a m和P o i n t e r都是3 2位的,
            因此,它們之間可以相互轉換。

            WM_NULL = $0000;
            WM_CREATE = $0001;
            應用程序創建一個窗口
            WM_DESTROY = $0002;
            一個窗口被銷毀
            WM_MOVE = $0003;
            移動一個窗口
            WM_SIZE = $0005;
            改變一個窗口的大小
            WM_ACTIVATE = $0006;
            一個窗口被激活或失去激活狀態;
            WM_SETFOCUS = $0007;
            獲得焦點后
            WM_KILLFOCUS = $0008;
            失去焦點
            WM_ENABLE = $000A;
            改變enable狀態
            WM_SETREDRAW = $000B;
            設置窗口是否能重畫
            WM_SETTEXT = $000C;
            應用程序發送此消息來設置一個窗口的文本
            WM_GETTEXT = $000D;
            應用程序發送此消息來復制對應窗口的文本到緩沖區
            WM_GETTEXTLENGTH = $000E;
            得到與一個窗口有關的文本的長度(不包含空字符)
            WM_PAINT = $000F;
            要求一個窗口重畫自己
            WM_CLOSE = $0010;
            當一個窗口或應用程序要關閉時發送一個信號
            WM_QUERYENDSESSION = $0011;
            當用戶選擇結束對話框或程序自己調用ExitWindows函數
            WM_QUIT = $0012;
            用來結束程序運行或當程序調用postquitmessage函數
            WM_QUERYOPEN = $0013;
            當用戶窗口恢復以前的大小位置時,把此消息發送給某個圖標
            WM_ERASEBKGND = $0014;
            當窗口背景必須被擦除時(例在窗口改變大小時)
            WM_SYSCOLORCHANGE = $0015;
            當系統顏色改變時,發送此消息給所有頂級窗口
            WM_ENDSESSION = $0016;
            當系統進程發出WM_QUERYENDSESSION消息后,此消息發送給應用程序,
            通知它對話是否結束
            WM_SYSTEMERROR = $0017;
            WM_SHOWWINDOW = $0018;
            當隱藏或顯示窗口是發送此消息給這個窗口
            WM_ACTIVATEAPP = $001C;
            發此消息給應用程序哪個窗口是激活的,哪個是非激活的;
            WM_FONTCHANGE = $001D;
            當系統的字體資源庫變化時發送此消息給所有頂級窗口
            WM_TIMECHANGE = $001E;
            當系統的時間變化時發送此消息給所有頂級窗口
            WM_CANCELMODE = $001F;
            發送此消息來取消某種正在進行的摸態(操作)
            WM_SETCURSOR = $0020;
            如果鼠標引起光標在某個窗口中移動且鼠標輸入沒有被捕獲時,就發消息給某個窗口
            WM_MOUSEACTIVATE = $0021;
            當光標在某個非激活的窗口中而用戶正按著鼠標的某個鍵發送此消息給當前窗口
            WM_CHILDACTIVATE = $0022;
            發送此消息給MDI子窗口當用戶點擊此窗口的標題欄,或當窗口被激活,移動,改變大小
            WM_QUEUESYNC = $0023;
            此消息由基于計算機的訓練程序發送,通過WH_JOURNALPALYBACK的hook程序
            分離出用戶輸入消息
            WM_GETMINMAXINFO = $0024;
            此消息發送給窗口當它將要改變大小或位置;
            WM_PAINTICON = $0026;
            發送給最小化窗口當它圖標將要被重畫
            WM_ICONERASEBKGND = $0027;
            此消息發送給某個最小化窗口,僅當它在畫圖標前它的背景必須被重畫
            WM_NEXTDLGCTL = $0028;
            發送此消息給一個對話框程序去更改焦點位置
            WM_SPOOLERSTATUS = $002A;
            每當打印管理列隊增加或減少一條作業時發出此消息
            WM_DRAWITEM = $002B;
            當button,combobox,listbox,menu的可視外觀改變時發送
            此消息給這些空件的所有者
            WM_MEASUREITEM = $002C;
            當button, combo box, list box, list view control, or menu item 被創建時
            發送此消息給控件的所有者
            WM_DELETEITEM = $002D;
            當the list box 或 combo box 被銷毀 或 當 某些項被刪除通過LB_DELETESTRING, LB_RESETCONTENT, CB_DELETESTRING, or CB_RESETCONTENT 消息
            WM_VKEYTOITEM = $002E;
            此消息有一個LBS_WANTKEYBOARDINPUT風格的發出給它的所有者來響應WM_KEYDOWN消息
            WM_CHARTOITEM = $002F;
            此消息由一個LBS_WANTKEYBOARDINPUT風格的列表框發送給他的所有者來響應WM_CHAR消息
            WM_SETFONT = $0030;
            當繪制文本時程序發送此消息得到控件要用的顏色
            WM_GETFONT = $0031;
            應用程序發送此消息得到當前控件繪制文本的字體
            WM_SETHOTKEY = $0032;
            應用程序發送此消息讓一個窗口與一個熱鍵相關連
            WM_GETHOTKEY = $0033;
            應用程序發送此消息來判斷熱鍵與某個窗口是否有關聯
            WM_QUERYDRAGICON = $0037;
            此消息發送給最小化窗口,當此窗口將要被拖放而它的類中沒有定義圖標,應用程序能返回一個圖標或光標的句柄,當用戶拖放圖標時系統顯示這個圖標或光標
            WM_COMPAREITEM = $0039;
            發送此消息來判定combobox或listbox新增加的項的相對位置
            WM_GETOBJECT = $003D;
            WM_COMPACTING = $0041;
            顯示內存已經很少了
            WM_WINDOWPOSCHANGING = $0046;
            發送此消息給那個窗口的大小和位置將要被改變時,來調用setwindowpos函數或其它窗口管理函數
            WM_WINDOWPOSCHANGED = $0047;
            發送此消息給那個窗口的大小和位置已經被改變時,來調用setwindowpos函數或其它窗口管理函數
            WM_POWER = $0048;(適用于16位的windows)
            當系統將要進入暫停狀態時發送此消息
            WM_COPYDATA = $004A;
            當一個應用程序傳遞數據給另一個應用程序時發送此消息
            WM_CANCELJOURNAL = $004B;
            當某個用戶取消程序日志激活狀態,提交此消息給程序
            WM_NOTIFY = $004E;
            當某個控件的某個事件已經發生或這個控件需要得到一些信息時,發送此消息給它的父窗口
            WM_INPUTLANGCHANGEREQUEST = $0050;
            當用戶選擇某種輸入語言,或輸入語言的熱鍵改變
            WM_INPUTLANGCHANGE = $0051;
            當平臺現場已經被改變后發送此消息給受影響的最頂級窗口
            WM_TCARD = $0052;
            當程序已經初始化windows幫助例程時發送此消息給應用程序
            WM_HELP = $0053;
            此消息顯示用戶按下了F1,如果某個菜單是激活的,就發送此消息個此窗口關聯的菜單,否則就
            發送給有焦點的窗口,如果當前都沒有焦點,就把此消息發送給當前激活的窗口
            WM_USERCHANGED = $0054;
            當用戶已經登入或退出后發送此消息給所有的窗口,當用戶登入或退出時系統更新用戶的具體
            設置信息,在用戶更新設置時系統馬上發送此消息;
            WM_NOTIFYFORMAT = $0055;
            公用控件,自定義控件和他們的父窗口通過此消息來判斷控件是使用ANSI還是UNICODE結構
            在WM_NOTIFY消息,使用此控件能使某個控件與它的父控件之間進行相互通信
            WM_CONTEXTMENU = $007B;
            當用戶某個窗口中點擊了一下右鍵就發送此消息給這個窗口
            WM_STYLECHANGING = $007C;
            當調用SETWINDOWLONG函數將要改變一個或多個 窗口的風格時發送此消息給那個窗口
            WM_STYLECHANGED = $007D;
            當調用SETWINDOWLONG函數一個或多個 窗口的風格后發送此消息給那個窗口
            WM_DISPLAYCHANGE = $007E;
            當顯示器的分辨率改變后發送此消息給所有的窗口
            WM_GETICON = $007F;
            此消息發送給某個窗口來返回與某個窗口有關連的大圖標或小圖標的句柄;
            WM_SETICON = $0080;
            程序發送此消息讓一個新的大圖標或小圖標與某個窗口關聯;
            WM_NCCREATE = $0081;
            當某個窗口第一次被創建時,此消息在WM_CREATE消息發送前發送;
            WM_NCDESTROY = $0082;
            此消息通知某個窗口,非客戶區正在銷毀
            WM_NCCALCSIZE = $0083;
            當某個窗口的客戶區域必須被核算時發送此消息
            WM_NCHITTEST = $0084;//移動鼠標,按住或釋放鼠標時發生
            WM_NCPAINT = $0085;
            程序發送此消息給某個窗口當它(窗口)的框架必須被繪制時;
            WM_NCACTIVATE = $0086;
            此消息發送給某個窗口 僅當它的非客戶區需要被改變來顯示是激活還是非激活狀態;
            WM_GETDLGCODE = $0087;
            發送此消息給某個與對話框程序關聯的控件,widdows控制方位鍵和TAB鍵使輸入進入此控件
            通過響應WM_GETDLGCODE消息,應用程序可以把他當成一個特殊的輸入控件并能處理它
            WM_NCMOUSEMOVE = $00A0;
            當光標在一個窗口的非客戶區內移動時發送此消息給這個窗口 //非客戶區為:窗體的標題欄及窗
            的邊框體
            WM_NCLBUTTONDOWN = $00A1;
            當光標在一個窗口的非客戶區同時按下鼠標左鍵時提交此消息
            WM_NCLBUTTONUP = $00A2;
            當用戶釋放鼠標左鍵同時光標某個窗口在非客戶區十發送此消息;
            WM_NCLBUTTONDBLCLK = $00A3;
            當用戶雙擊鼠標左鍵同時光標某個窗口在非客戶區十發送此消息
            WM_NCRBUTTONDOWN = $00A4;
            當用戶按下鼠標右鍵同時光標又在窗口的非客戶區時發送此消息
            WM_NCRBUTTONUP = $00A5;
            當用戶釋放鼠標右鍵同時光標又在窗口的非客戶區時發送此消息
            WM_NCRBUTTONDBLCLK = $00A6;
            當用戶雙擊鼠標右鍵同時光標某個窗口在非客戶區十發送此消息
            WM_NCMBUTTONDOWN = $00A7;
            當用戶按下鼠標中鍵同時光標又在窗口的非客戶區時發送此消息
            WM_NCMBUTTONUP = $00A8;
            當用戶釋放鼠標中鍵同時光標又在窗口的非客戶區時發送此消息
            WM_NCMBUTTONDBLCLK = $00A9;
            當用戶雙擊鼠標中鍵同時光標又在窗口的非客戶區時發送此消息
            WM_KEYFIRST = $0100;
            WM_KEYDOWN = $0100;
            //按下一個鍵
            WM_KEYUP = $0101;
            //釋放一個鍵
            WM_CHAR = $0102;
            //按下某鍵,并已發出WM_KEYDOWN, WM_KEYUP消息
            WM_DEADCHAR = $0103;
            當用translatemessage函數翻譯WM_KEYUP消息時發送此消息給擁有焦點的窗口
            WM_SYSKEYDOWN = $0104;
            當用戶按住ALT鍵同時按下其它鍵時提交此消息給擁有焦點的窗口;
            WM_SYSKEYUP = $0105;
            當用戶釋放一個鍵同時ALT 鍵還按著時提交此消息給擁有焦點的窗口
            WM_SYSCHAR = $0106;
            當WM_SYSKEYDOWN消息被TRANSLATEMESSAGE函數翻譯后提交此消息給擁有焦點的窗口
            WM_SYSDEADCHAR = $0107;
            當WM_SYSKEYDOWN消息被TRANSLATEMESSAGE函數翻譯后發送此消息給擁有焦點的窗口
            WM_KEYLAST = $0108;
            WM_INITDIALOG = $0110;
            在一個對話框程序被顯示前發送此消息給它,通常用此消息初始化控件和執行其它任務
            WM_COMMAND = $0111;
            當用戶選擇一條菜單命令項或當某個控件發送一條消息給它的父窗口,一個快捷鍵被翻譯
            WM_SYSCOMMAND = $0112;
            當用戶選擇窗口菜單的一條命令或當用戶選擇最大化或最小化時那個窗口會收到此消息
            WM_TIMER = $0113; //發生了定時器事件
            WM_HSCROLL = $0114;
            當一個窗口標準水平滾動條產生一個滾動事件時發送此消息給那個窗口,也發送給擁有它的控件
            WM_VSCROLL = $0115;
            當一個窗口標準垂直滾動條產生一個滾動事件時發送此消息給那個窗口也,發送給擁有它的控件 WM_INITMENU = $0116;
            當一個菜單將要被激活時發送此消息,它發生在用戶菜單條中的某項或按下某個菜單鍵,它允許程序在顯示前更改菜單
            WM_INITMENUPOPUP = $0117;
            當一個下拉菜單或子菜單將要被激活時發送此消息,它允許程序在它顯示前更改菜單,而不要改變全部
            WM_MENUSELECT = $011F;
            當用戶選擇一條菜單項時發送此消息給菜單的所有者(一般是窗口)
            WM_MENUCHAR = $0120;
            當菜單已被激活用戶按下了某個鍵(不同于加速鍵),發送此消息給菜單的所有者;
            WM_ENTERIDLE = $0121;
            當一個模態對話框或菜單進入空載狀態時發送此消息給它的所有者,一個模態對話框或菜單進入空載狀態就是在處理完一條或幾條先前的消息后沒有消息它的列隊中等待
            WM_MENURBUTTONUP = $0122;
            WM_MENUDRAG = $0123;
            WM_MENUGETOBJECT = $0124;
            WM_UNINITMENUPOPUP = $0125;
            WM_MENUCOMMAND = $0126;
            WM_CHANGEUISTATE = $0127;
            WM_UPDATEUISTATE = $0128;
            WM_QUERYUISTATE = $0129;
            WM_CTLCOLORMSGBOX = $0132;
            在windows繪制消息框前發送此消息給消息框的所有者窗口,通過響應這條消息,所有者窗口可以通過使用給定的相關顯示設備的句柄來設置消息框的文本和背景顏色
            WM_CTLCOLOREDIT = $0133;
            當一個編輯型控件將要被繪制時發送此消息給它的父窗口;通過響應這條消息,所有者窗口可以通過使用給定的相關顯示設備的句柄來設置編輯框的文本和背景顏色
            WM_CTLCOLORLISTBOX = $0134;
            當一個列表框控件將要被繪制前發送此消息給它的父窗口;通過響應這條消息,所有者窗口可以通過使用給定的相關顯示設備的句柄來設置列表框的文本和背景顏色
            WM_CTLCOLORBTN = $0135;
            當一個按鈕控件將要被繪制時發送此消息給它的父窗口;通過響應這條消息,所有者窗口可以通過使用給定的相關顯示設備的句柄來設置按紐的文本和背景顏色
            WM_CTLCOLORDLG = $0136;
            當一個對話框控件將要被繪制前發送此消息給它的父窗口;通過響應這條消息,所有者窗口可以通過使用給定的相關顯示設備的句柄來設置對話框的文本背景顏色
            WM_CTLCOLORSCROLLBAR= $0137;
            當一個滾動條控件將要被繪制時發送此消息給它的父窗口;通過響應這條消息,所有者窗口可以通過使用給定的相關顯示設備的句柄來設置滾動條的背景顏色
            WM_CTLCOLORSTATIC = $0138;
            當一個靜態控件將要被繪制時發送此消息給它的父窗口;通過響應這條消息,所有者窗口可以通過使用給定的相關顯示設備的句柄來設置靜態控件的文本和背景顏色
            WM_MOUSEFIRST = $0200;
            WM_MOUSEMOVE = $0200;
            // 移動鼠標
            WM_LBUTTONDOWN = $0201;
            //按下鼠標左鍵
            WM_LBUTTONUP = $0202;
            //釋放鼠標左鍵
            WM_LBUTTONDBLCLK = $0203;
            //雙擊鼠標左鍵
            WM_RBUTTONDOWN = $0204;
            //按下鼠標右鍵
            WM_RBUTTONUP = $0205;
            //釋放鼠標右鍵
            WM_RBUTTONDBLCLK = $0206;
            //雙擊鼠標右鍵
            WM_MBUTTONDOWN = $0207;
            //按下鼠標中鍵
            WM_MBUTTONUP = $0208;
            //釋放鼠標中鍵
            WM_MBUTTONDBLCLK = $0209;
            //雙擊鼠標中鍵
            WM_MOUSEWHEEL = $020A;
            當鼠標輪子轉動時發送此消息個當前有焦點的控件
            WM_MOUSELAST = $020A;
            WM_PARENTNOTIFY = $0210;
            當MDI子窗口被創建或被銷毀,或用戶按了一下鼠標鍵而光標在子窗口上時發送此消息給它的父窗口
            WM_ENTERMENULOOP = $0211;
            發送此消息通知應用程序的主窗口that已經進入了菜單循環模式
            WM_EXITMENULOOP = $0212;
            發送此消息通知應用程序的主窗口that已退出了菜單循環模式
            WM_NEXTMENU = $0213;
            WM_SIZING = 532;
            當用戶正在調整窗口大小時發送此消息給窗口;通過此消息應用程序可以監視窗口大小和位置也可以修改他們
            WM_CAPTURECHANGED = 533;
            發送此消息 給窗口當它失去捕獲的鼠標時;
            WM_MOVING = 534;
            當用戶在移動窗口時發送此消息,通過此消息應用程序可以監視窗口大小和位置也可以修改他們;
            WM_POWERBROADCAST = 536;
            此消息發送給應用程序來通知它有關電源管理事件;
            WM_DEVICECHANGE = 537;
            當設備的硬件配置改變時發送此消息給應用程序或設備驅動程序
            WM_IME_STARTCOMPOSITION = $010D;
            WM_IME_ENDCOMPOSITION = $010E;
            WM_IME_COMPOSITION = $010F;
            WM_IME_KEYLAST = $010F;
            WM_IME_SETCONTEXT = $0281;
            WM_IME_NOTIFY = $0282;
            WM_IME_CONTROL = $0283;
            WM_IME_COMPOSITIONFULL = $0284;
            WM_IME_SELECT = $0285;
            WM_IME_CHAR = $0286;
            WM_IME_REQUEST = $0288;
            WM_IME_KEYDOWN = $0290;
            WM_IME_KEYUP = $0291;
            WM_MDICREATE = $0220;
            應用程序發送此消息給多文檔的客戶窗口來創建一個MDI 子窗口
            WM_MDIDESTROY = $0221;
            應用程序發送此消息給多文檔的客戶窗口來關閉一個MDI 子窗口
            WM_MDIACTIVATE = $0222;
            應用程序發送此消息給多文檔的客戶窗口通知客戶窗口激活另一個MDI子窗口,當客戶窗口收到此消息后,它發出WM_MDIACTIVE消息給MDI子窗口(未激活)激活它;
            WM_MDIRESTORE = $0223;
            程序 發送此消息給MDI客戶窗口讓子窗口從最大最小化恢復到原來大小
            WM_MDINEXT = $0224;
            程序 發送此消息給MDI客戶窗口激活下一個或前一個窗口
            WM_MDIMAXIMIZE = $0225;
            程序發送此消息給MDI客戶窗口來最大化一個MDI子窗口;
            WM_MDITILE = $0226;
            程序 發送此消息給MDI客戶窗口以平鋪方式重新排列所有MDI子窗口
            WM_MDICASCADE = $0227;
            程序 發送此消息給MDI客戶窗口以層疊方式重新排列所有MDI子窗口
            WM_MDIICONARRANGE = $0228;
            程序 發送此消息給MDI客戶窗口重新排列所有最小化的MDI子窗口
            WM_MDIGETACTIVE = $0229;
            程序 發送此消息給MDI客戶窗口來找到激活的子窗口的句柄
            WM_MDISETMENU = $0230;
            程序 發送此消息給MDI客戶窗口用MDI菜單代替子窗口的菜單
            WM_ENTERSIZEMOVE = $0231;
            WM_EXITSIZEMOVE = $0232;
            WM_DROPFILES = $0233;
            WM_MDIREFRESHMENU = $0234;
            WM_MOUSEHOVER = $02A1;
            WM_MOUSELEAVE = $02A3;
            WM_CUT = $0300;
            程序發送此消息給一個編輯框或combobox來刪除當前選擇的文本
            WM_COPY = $0301;
            程序發送此消息給一個編輯框或combobox來復制當前選擇的文本到剪貼板
            WM_PASTE = $0302;
            程序發送此消息給editcontrol或combobox從剪貼板中得到數據
            WM_CLEAR = $0303;
            程序發送此消息給editcontrol或combobox清除當前選擇的內容;
            WM_UNDO = $0304;
            程序發送此消息給editcontrol或combobox撤消最后一次操作
            WM_RENDERFORMAT = $0305;

            WM_RENDERALLFORMATS = $0306;
            WM_DESTROYCLIPBOARD = $0307;
            當調用ENPTYCLIPBOARD函數時 發送此消息給剪貼板的所有者
            WM_DRAWCLIPBOARD = $0308;
            當剪貼板的內容變化時發送此消息給剪貼板觀察鏈的第一個窗口;它允許用剪貼板觀察窗口來
            顯示剪貼板的新內容;
            WM_PAINTCLIPBOARD = $0309;
            當剪貼板包含CF_OWNERDIPLAY格式的數據并且剪貼板觀察窗口的客戶區需要重畫;
            WM_VSCROLLCLIPBOARD = $030A;
            WM_SIZECLIPBOARD = $030B;
            當剪貼板包含CF_OWNERDIPLAY格式的數據并且剪貼板觀察窗口的客戶區域的大小已經改變是此消息通過剪貼板觀察窗口發送給剪貼板的所有者;
            WM_ASKCBFORMATNAME = $030C;
            通過剪貼板觀察窗口發送此消息給剪貼板的所有者來請求一個CF_OWNERDISPLAY格式的剪貼板的名字
            WM_CHANGECBCHAIN = $030D;
            當一個窗口從剪貼板觀察鏈中移去時發送此消息給剪貼板觀察鏈的第一個窗口;
            WM_HSCROLLCLIPBOARD = $030E;
            此消息通過一個剪貼板觀察窗口發送給剪貼板的所有者 ;它發生在當剪貼板包含CFOWNERDISPALY格式的數據并且有個事件在剪貼板觀察窗的水平滾動條上;所有者應滾動剪貼板圖象并更新滾動條的值;
            WM_QUERYNEWPALETTE = $030F;
            此消息發送給將要收到焦點的窗口,此消息能使窗口在收到焦點時同時有機會實現他的邏輯調色板
            WM_PALETTEISCHANGING= $0310;
            當一個應用程序正要實現它的邏輯調色板時發此消息通知所有的應用程序
            WM_PALETTECHANGED = $0311;
            此消息在一個擁有焦點的窗口實現它的邏輯調色板后發送此消息給所有頂級并重疊的窗口,以此來改變系統調色板
            WM_HOTKEY = $0312;
            當用戶按下由REGISTERHOTKEY函數注冊的熱鍵時提交此消息
            WM_PRINT = 791;
            應用程序發送此消息僅當WINDOWS或其它應用程序發出一個請求要求繪制一個應用程序的一部分;
            WM_PRINTCLIENT = 792;
            WM_HANDHELDFIRST = 856;
            WM_HANDHELDLAST = 863;
            WM_PENWINFIRST = $0380;
            WM_PENWINLAST = $038F;
            WM_COALESCE_FIRST = $0390;
            WM_COALESCE_LAST = $039F;
            WM_DDE_FIRST = $03E0;
            WM_DDE_INITIATE = WM_DDE_FIRST + 0;
            一個DDE客戶程序提交此消息開始一個與服務器程序的會話來響應那個指定的程序和主題名;
            WM_DDE_TERMINATE = WM_DDE_FIRST + 1;
            一個DDE應用程序(無論是客戶還是服務器)提交此消息來終止一個會話;
            WM_DDE_ADVISE = WM_DDE_FIRST + 2;
            一個DDE客戶程序提交此消息給一個DDE服務程序來請求服務器每當數據項改變時更新它
            WM_DDE_UNADVISE = WM_DDE_FIRST + 3;
            一個DDE客戶程序通過此消息通知一個DDE服務程序不更新指定的項或一個特殊的剪貼板格式的項
            WM_DDE_ACK = WM_DDE_FIRST + 4;
            此消息通知一個DDE(動態數據交換)程序已收到并正在處理WM_DDE_POKE, WM_DDE_EXECUTE, WM_DDE_DATA, WM_DDE_ADVISE, WM_DDE_UNADVISE, or WM_DDE_INITIAT消息
            WM_DDE_DATA = WM_DDE_FIRST + 5;
            一個DDE服務程序提交此消息給DDE客戶程序來傳遞個一數據項給客戶或通知客戶的一條可用數據項
            WM_DDE_REQUEST = WM_DDE_FIRST + 6;
            一個DDE客戶程序提交此消息給一個DDE服務程序來請求一個數據項的值;
            WM_DDE_POKE = WM_DDE_FIRST + 7;
            一個DDE客戶程序提交此消息給一個DDE服務程序,客戶使用此消息來請求服務器接收一個未經同意的數據項;服務器通過答復WM_DDE_ACK消息提示是否它接收這個數據項;
            WM_DDE_EXECUTE = WM_DDE_FIRST + 8;
            一個DDE客戶程序提交此消息給一個DDE服務程序來發送一個字符串給服務器讓它象串行命令一樣被處理,服務器通過提交WM_DDE_ACK消息來作回應;
            WM_DDE_LAST = WM_DDE_FIRST + 8;
            WM_APP = $8000;
            WM_USER = $0400;
            此消息能幫助應用程序自定義私有消息;
            /////////////////////////////////////////////////////////////////////
            通 知消息(Notification message)是指這樣一種消息,一個窗口內的子控件發生了一些事情,需要通知父窗口。通知消息只適用于標準的窗口控件如按鈕、列表框、組合框、編輯 框,以及Windows 95公共控件如樹狀視圖、列表視圖等。例如,單擊或雙擊一個控件、在控件中選擇部分文本、操作控件的滾動條都會產生通知消息。
            按扭
            B N _ C L I C K E D //用戶單擊了按鈕
            B N _ D I S A B L E //按鈕被禁止
            B N _ D O U B L E C L I C K E D //用戶雙擊了按鈕
            B N _ H I L I T E //用戶加亮了按鈕
            B N _ PA I N T按鈕應當重畫
            B N _ U N H I L I T E加亮應當去掉
            組合框
            C B N _ C L O S E U P組合框的列表框被關閉
            C B N _ D B L C L K用戶雙擊了一個字符串
            C B N _ D R O P D O W N組合框的列表框被拉出
            C B N _ E D I T C H A N G E用戶修改了編輯框中的文本
            C B N _ E D I T U P D AT E編輯框內的文本即將更新
            C B N _ E R R S PA C E組合框內存不足
            C B N _ K I L L F O C U S組合框失去輸入焦點
            C B N _ S E L C H A N G E在組合框中選擇了一項
            C B N _ S E L E N D C A N C E L用戶的選擇應當被取消
            C B N _ S E L E N D O K用戶的選擇是合法的
            C B N _ S E T F O C U S組合框獲得輸入焦點
            編輯框
            E N _ C H A N G E編輯框中的文本己更新
            E N _ E R R S PA C E編輯框內存不足
            E N _ H S C R O L L用戶點擊了水平滾動條
            E N _ K I L L F O C U S編輯框正在失去輸入焦點
            E N _ M A X T E X T插入的內容被截斷
            E N _ S E T F O C U S編輯框獲得輸入焦點
            E N _ U P D AT E編輯框中的文本將要更新
            E N _ V S C R O L L用戶點擊了垂直滾動條消息含義
            列表框
            L B N _ D B L C L K用戶雙擊了一項
            L B N _ E R R S PA C E列表框內存不夠
            L B N _ K I L L F O C U S列表框正在失去輸入焦點
            L B N _ S E L C A N C E L選擇被取消
            L B N _ S E L C H A N G E選擇了另一項
            L B N _ S E T F O C U S列表框獲得輸入焦點

            posted @ 2008-08-28 10:29 深邃者 閱讀(178) | 評論 (0)編輯 收藏

            Window API函數大全(一)

            1. API之網絡函數

            WNetAddConnection 創建同一個網絡資源的永久性連接 WNetAddConnection2 創建同一個網絡資源的連接 WNetAddConnection3 創建同一個網絡資源的連接 WNetCancelConnection 結束一個網絡連接 WNetCancelConnection2 結束一個網絡連接 WNetCloseEnum 結束一次枚舉操作 WNetConnectionDialog 啟動一個標準對話框,以便建立同網絡資源的連接 WNetDisconnectDialog 啟動一個標準對話框,以便斷開同網絡資源的連接 WNetEnumResource 枚舉網絡資源 WNetGetConnection 獲取本地或已連接的一個資源的網絡名稱 WNetGetLastError 獲取網絡錯誤的擴展錯誤信息 WNetGetUniversalName 獲取網絡中一個文件的遠程名稱以及/或者UNC(統一命名規范)名稱 WNetGetUser 獲取一個網絡資源用以連接的名字 WNetOpenEnum 啟動對網絡資源進行枚舉的過程

            2. API之消息函數

            BroadcastSystemMessage 將一條系統消息廣播給系統中所有的頂級窗口 GetMessagePos 取得消息隊列中上一條消息處理完畢時的鼠標指針屏幕位置 GetMessageTime 取得消息隊列中上一條消息處理完畢時的時間 PostMessage 將一條消息投遞到指定窗口的消息隊列 PostThreadMessage 將一條消息投遞給應用程序 RegisterWindowMessage 獲取分配給一個字串標識符的消息編號 ReplyMessage 答復一個消息 SendMessage 調用一個窗口的窗口函數,將一條消息發給那個窗口 SendMessageCallback 將一條消息發給窗口 SendMessageTimeout 向窗口發送一條消息 SendNotifyMessage 向窗口發送一條消息

            3. API之文件處理函數

            CloseHandle 關閉一個內核對象。其中包括文件、文件映射、進程、線程、安全和同步對象等 CompareFileTime 對比兩個文件的時間 CopyFile 復制文件 CreateDirectory 創建一個新目錄 CreateFile 打開和創建文件、管道、郵槽、通信服務、設備以及控制臺 CreateFileMapping 創建一個新的文件映射對象 DeleteFile 刪除指定文件 DeviceIoControl 對設備執行指定的操作 DosDateTimeToFileTime 將DOS日期和時間值轉換成一個 win32 FILETIME 值 FileTimeToDosDateTime 將一個 win32 FILETIME 值轉換成DOS日期和時間值 FileTimeToLocalFileTime 將一個FILETIME結構轉換成本地時間 FileTimeToSystemTime 根據一個FILETIME結構的內容,裝載一個SYSTEMTIME結構 FindClose 關閉由FindFirstFile函數創建的一個搜索句柄 FindFirstFile 根據文件名查找文件 FindNextFile 根據調用FindFirstFile函數時指定的一個文件名查找下一個文件 FlushFileBuffers 針對指定的文件句柄,刷新內部文件緩沖區 FlushViewOfFile 將寫入文件映射緩沖區的所有數據都刷新到磁盤 GetBinaryType 判斷文件是否可以執行 GetCompressedFileSize 判斷一個壓縮文件在磁盤上實際占據的字節數 GetCurrentDirectory 在一個緩沖區中裝載當前目錄 GetDiskFreeSpace 獲取與一個磁盤的組織有關的信息,以及了解剩余空間的容量 GetDiskFreeSpaceEx 獲取與一個磁盤的組織以及剩余空間容量有關的信息 GetDriveType 判斷一個磁盤驅動器的類型 GetExpandedName 取得一個壓縮文件的全名 GetFileAttributes 判斷指定文件的屬性 GetFileInformationByHandle 這個函數提供了獲取文件信息的一種機制 GetFileSize 判斷文件長度 GetFileTime 取得指定文件的時間信息 GetFileType 在給出文件句柄的前提下,判斷文件類型 GetFileVersionInfo 從支持版本標記的一個模塊里獲取文件版本信息 GetFileVersionInfoSize 針對包含了版本資源的一個文件,判斷容納文件版本信息需要一個多大的緩沖區 GetFullPathName 獲取指定文件的完整路徑名 GetLogicalDrives 判斷系統中存在哪些邏輯驅動器字母 GetLogicalDriveStrings 獲取一個字串,其中包含了當前所有邏輯驅動器的根驅動器路徑 GetOverlappedResult 判斷一個重疊操作當前的狀態 GetPrivateProfileInt 為初始化文件(.ini文件)中指定的條目獲取一個整數值 GetPrivateProfileSection 獲取指定小節(在.ini文件中)所有項名和值的一個列表 GetPrivateProfileString 為初始化文件中指定的條目取得字串 GetProfileInt 取得win.ini初始化文件中指定條目的一個整數值 GetProfileSection 獲取指定小節(在win.ini文件中)所有項名和值的一個列表 GetProfileString 為win.ini初始化文件中指定的條目取得字串 GetShortPathName 獲取指定文件的短路徑名 GetSystemDirectory 取得Windows系統目錄(即System目錄)的完整路徑名 GetTempFileName 這個函數包含了一個臨時文件的名字,它可由應用程序使用 GetTempPath 獲取為臨時文件指定的路徑 GetVolumeInformation 獲取與一個磁盤卷有關的信息 GetWindowsDirectory 獲取Windows目錄的完整路徑名 hread 參考lread hwrite 參考lwrite函數 lclose 關閉指定的文件 lcreat 創建一個文件 llseek 設置文件中進行讀寫的當前位置 LockFile 鎖定文件的某一部分,使其不與其他應用程序共享 LockFileEx 與LockFile相似,只是它提供了更多的功能 lopen 以二進制模式打開指定的文件 lread 將文件中的數據讀入內存緩沖區 lwrite 將數據從內存緩沖區寫入一個文件 LZClose 關閉由LZOpenFile 或 LZInit函數打開的一個文件 LZCopy 復制一個文件 LZInit 這個函數用于初始化內部緩沖區 LZOpenFile 該函數能執行大量不同的文件處理,而且兼容于壓縮文件 LZRead 將數據從文件讀入內存緩沖區 LZSeek 設置一個文件中進行讀寫的當前位置 MapViewOfFile 將一個文件映射對象映射到當前應用程序的地址空間 MoveFile 移動文件 OpenFile 這個函數能執行大量不同的文件操作 OpenFileMapping 打開一個現成的文件映射對象 QueryDosDevice 在Windows NT中,DOS設備名會映射成NT系統設備名。該函數可判斷當前的設備映射情況 ReadFile 從文件中讀出數據 ReadFileEx 與ReadFile相似,只是它只能用于異步讀操作,并包含了一個完整的回調 RegCloseKey 關閉系統注冊表中的一個項(或鍵) RegConnectRegistry 訪問遠程系統的部分注冊表 RegCreateKey 在指定的項下創建或打開一個項 RegCreateKeyEx 在指定項下創建新項的更復雜的方式。在Win32環境中建議使用這個函數 RegDeleteKey 刪除現有項下方一個指定的子項 RegDeleteValue 刪除指定項下方的一個值 RegEnumKey 枚舉指定項的子項。在Win32環境中應使用RegEnumKeyEx RegEnumKeyEx 枚舉指定項下方的子項 RegEnumValue 枚舉指定項的值 RegFlushKey 將對項和它的子項作出的改動實際寫入磁盤 RegGetKeySecurity 獲取與一個注冊表項有關的安全信息 RegLoadKey 從以前用RegSaveKey函數創建的一個文件里裝載注冊表信息 RegNotifyChangeKeyValue 注冊表項或它的任何一個子項發生變化時,用這個函數提供一種通知機制 RegOpenKey 打開一個現有的注冊表項 RegOpenKeyEx 打開一個現有的項。在win32下推薦使用這個函數 RegQueryInfoKey 獲取與一個項有關的信息 RegQueryValue 取得指定項或子項的默認(未命名)值 RegQueryValueEx 獲取一個項的設置值 RegReplaceKey 用一個磁盤文件保存的信息替換注冊表信息;并創建一個備份,在其中包含當前注冊表信息 RegRestoreKey 從一個磁盤文件恢復注冊表信息 RegSaveKey 將一個項以及它的所有子項都保存到一個磁盤文件 RegSetKeySecurity 設置指定項的安全特性 RegSetValue 設置指定項或子項的默認值 RegSetValueEx 設置指定項的值 RegUnLoadKey 卸載指定的項以及它的所有子項 RemoveDirectory 刪除指定目錄 SearchPath 查找指定文件 SetCurrentDirectory 設置當前目錄 SetEndOfFile 針對一個打開的文件,將當前文件位置設為文件末尾 SetFileAttributes 設置文件屬性 SetFilePointer 在一個文件中設置當前的讀寫位置 SetFileTime 設置文件的創建、訪問及上次修改時間 SetHandleCount 這個函數不必在win32下使用;即使使用,也不會有任何效果 SetVolumeLabel 設置一個磁盤的卷標(Label) SystemTimeToFileTime 根據一個FILETIME結構的內容,載入一個SYSTEMTIME結構 UnlockFile 解除對一個文件的鎖定 UnlockFileEx 解除對一個文件的鎖定 UnmapViewOfFile 在當前應用程序的內存地址空間解除對一個文件映射對象的映射 VerFindFile 用這個函數決定一個文件應安裝到哪里 VerInstallFile 用這個函數安裝一個文件 VerLanguageName 這個函數能根據16位語言代碼獲取一種語言的名稱 VerQueryValue 這個函數用于從版本資源中獲取信息 WriteFile 將數據寫入一個文件 WriteFileEx 與WriteFile類似,只是它只能用于異步寫操作,并包括了一個完整的回調 WritePrivateProfileSection 為一個初始化文件(.ini)中指定的小節設置所有項名和值 WritePrivateProfileString 在初始化文件指定小節內設置一個字串 WriteProfileSection 為Win.ini初始化文件中一個指定的小節設置所有項名和值 WriteProfileString 在Win.ini初始化文件指定小節內設置一個字串

            4. API之打印函數 AbortDoc 取消一份文檔的打印 AbortPrinter 刪除與一臺打印機關聯在一起的緩沖文件 AddForm 為打印機的表單列表添加一個新表單 AddJob 用于獲取一個有效的路徑名,以便用它為作業創建一個后臺打印文件。它也會為作業分配一個作業編號 AddMonitor 為系統添加一個打印機監視器 AddPort 啟動“添加端口”對話框,允許用戶在系統可用端口列表中加入一個新端口 AddPrinter 在系統中添加一臺新打印機 AddPrinterConnection 連接指定的打印機 AddPrinterDriver 為指定的系統添加一個打印驅動程序 AddPrintProcessor 為指定的系統添加一個打印處理器 AddPrintProvidor 為系統添加一個打印供應商 AdvancedDocumentProperties 啟動打印機文檔設置對話框 ClosePrinter 關閉一個打開的打印機對象 ConfigurePort 針對指定的端口,啟動一個端口配置對話框 ConnectToPrinterDlg 啟動連接打印機對話框,用它同訪問網絡的打印機連接 DeleteForm 從打印機可用表單列表中刪除一個表單 DeleteMonitor 刪除指定的打印監視器 DeletePort 啟動“刪除端口”對話框,允許用戶從當前系統刪除一個端口 DeletePrinter 將指定的打印機標志為從系統中刪除 DeletePrinterConnection 刪除與指定打印機的連接 DeletePrinterDriver 從系統刪除一個打印機驅動程序 DeletePrintProcessor 從指定系統刪除一個打印處理器 DeletePrintProvidor 從系統中刪除一個打印供應商 DeviceCapabilities 利用這個函數可獲得與一個設備的能力有關的信息 DocumentProperties 打印機配置控制函數 EndDocAPI 結束一個成功的打印作業 EndDocPrinter 在后臺打印程序的級別指定一個文檔的結束 EndPage 用這個函數完成一個頁面的打印,并準備設備場景,以便打印下一個頁 EndPagePrinter 指定一個頁在打印作業中的結尾 EnumForms 枚舉一臺打印機可用的表單 EnumJobs 枚舉打印隊列中的作業 EnumMonitors 枚舉可用的打印監視器 EnumPorts 枚舉一個系統可用的端口 EnumPrinterDrivers 枚舉指定系統中已安裝的打印機驅動程序 EnumPrinters 枚舉系統中安裝的打印機 EnumPrintProcessorDatatypes 枚舉由一個打印處理器支持的數據類型 EnumPrintProcessors 枚舉系統中可用的打印處理器 Escape 設備控制函數 FindClosePrinterChangeNotification 關閉用FindFirstPrinterChangeNotification函數獲取的一個打印機通告對象 FindFirstPrinterChangeNotification 創建一個新的改變通告對象,以便我們注意打印機狀態的各種變化 FindNextPrinterChangeNotification 用這個函數判斷觸發一次打印機改變通告信號的原因 FreePrinterNotifyInfo 釋放由FindNextPrinterChangeNotification函數分配的一個緩沖區 GetForm 取得與指定表單有關的信息 GetJob 獲取與指定作業有關的信息 GetPrinter 取得與指定打印機有關的信息 GetPrinterData 為打印機設置注冊表配置信息 GetPrinterDriver 針對指定的打印機,獲取與打印機驅動程序有關的信息 GetPrinterDriverDirectory 判斷指定系統中包含了打印機驅動程序的目錄是什么 GetPrintProcessorDirectory 判斷指定系統中包含了打印機處理器驅動程序及文件的目錄 OpenPrinter 打開指定的打印機,并獲取打印機的句柄 PrinterMessageBox 在擁有指定打印作業的系統上顯示一個打印機出錯消息框 PrinterProperties 啟動打印機屬性對話框,以便對打印機進行配置 ReadPrinter 從打印機讀入數據 ResetDC 重設一個設備場景 ResetPrinter 改變指定打印機的默認數據類型及文檔設置 ScheduleJob 提交一個要打印的作業 SetAbortProc 為Windows指定取消函數的地址 SetForm 為指定的表單設置信息 SetJob 對一個打印作業的狀態進行控制 SetPrinter 對一臺打印機的狀態進行控制 SetPrinterData 設置打印機的注冊表配置信息 StartDoc 開始一個打印作業 StartDocPrinter 在后臺打印的級別啟動一個新文檔 StartPage 打印一個新頁前要先調用這個函數 StartPagePrinter 在打印作業中指定一個新頁的開始 WritePrinter 將發送目錄中的數據寫入打印機

            5. API之文本和字體函數

            AddFontResource 在Windows系統中添加一種字體資源 CreateFont 用指定的屬性創建一種邏輯字體 CreateFontIndirect 用指定的屬性創建一種邏輯字體 CreateScalableFontResource 為一種TureType字體創建一個資源文件,以便能用API函數AddFontResource將其加入Windows系統 DrawText 將文本描繪到指定的矩形中 DrawTextEx 與DrawText相似,只是加入了更多的功能 EnumFontFamilies 列舉指定設備可用的字體 EnumFontFamiliesEx 列舉指定設備可用的字體 EnumFonts 列舉指定設備可用的字體 ExtTextOut 經過擴展的文本描繪函數。也請參考SetTextAlign函數 GetAspectRatioFilterEx 用SetMapperFlags要求Windows只選擇與設備當前縱橫比相符的光柵字體時,本函數可判斷縱橫比大小 GetCharABCWidths 判斷TureType字體中一個或多個字符的A-B-C大小 GetCharABCWidthsFloat 查詢一種字體中一個或多個字符的A-B-C尺寸 GetCharacterPlacement 該函數用于了解如何用一個給定的字符顯示一個字串 GetCharWidth 調查字體中一個或多個字符的寬度 GetFontData 接收一種可縮放字體文件的數據 GetFontLanguageInfo 返回目前選入指定設備場景中的字體的信息 GetGlyphOutline 取得TureType字體中構成一個字符的曲線信息 GetKerningPairs 取得指定字體的字距信息 GetOutlineTextMetrics 接收與TureType字體內部特征有關的詳細信息 GetRasterizerCaps 了解系統是否有能力支持可縮放的字體 GetTabbedTextExtent 判斷一個字串占據的范圍,同時考慮制表站擴充的因素 GetTextAlign 接收一個設備場景當前的文本對齊標志 GetTextCharacterExtra 判斷額外字符間距的當前值 GetTextCharset 接收當前選入指定設備場景的字體的字符集標識符 GetTextCharsetInfo 獲取與當前選定字體的字符集有關的詳細信息 GetTextColor 判斷當前字體顏色。通常也稱為“前景色” GetTextExtentExPoint 判斷要填入指定區域的字符數量。也用一個數組裝載每個字符的范圍信息 GetTextExtentPoint 判斷一個字串的大小(范圍) GetTextFace 獲取一種字體的字樣名 GetTextMetrics 獲取與選入一種設備場景的物理字體有關的信息 GrayString 描繪一個以灰色顯示的字串。通常由Windows用于標識禁止狀態 PolyTextOut 描繪一系列字串 RemoveFontResource 從Windows系統中刪除一種字體資源 SetMapperFlags Windows對字體進行映射時,可用該函數選擇與目標設備的縱橫比相符的光柵字體 SetTextAlign 設置文本對齊方式,并指定在文本輸出過程中使用設備場景的當前位置 SetTextCharacterExtra 描繪文本的時候,指定要在字符間插入的額外間距 SetTextColor 設置當前文本顏色。這種顏色也稱為“前景色” SetTextJustification 通過指定一個文本行應占據的額外空間,可用這個函數對文本進行兩端對齊處理 TabbedTextOut 支持制表站的一個文本描繪函數 TextOut 文本繪圖函數

            6. API之菜單函數

            AppendMenu 在指定的菜單里添加一個菜單項 CheckMenuItem 復選或撤消復選指定的菜單條目 CheckMenuRadioItem 指定一個菜單條目被復選成“單選”項目 CreateMenu 創建新菜單 CreatePopupMenu 創建一個空的彈出式菜單 DeleteMenu 刪除指定的菜單條目 DestroyMenu 刪除指定的菜單 DrawMenuBar 為指定的窗口重畫菜單 EnableMenuItem 允許或禁止指定的菜單條目 GetMenu 取得窗口中一個菜單的句柄 GetMenuCheckMarkDimensions 返回一個菜單復選符的大小 GetMenuContextHelpId 取得一個菜單的幫助場景ID GetMenuDefaultItem 判斷菜單中的哪個條目是默認條目 GetMenuItemCount 返回菜單中條目(菜單項)的數量 GetMenuItemID 返回位于菜單中指定位置處的條目的菜單ID GetMenuItemInfo 取得(接收)與一個菜單條目有關的特定信息 GetMenuItemRect 在一個矩形中裝載指定菜單條目的屏幕坐標信息 GetMenuState 取得與指定菜單條目狀態有關的信息 GetMenuString 取得指定菜單條目的字串 GetSubMenu 取得一個彈出式菜單的句柄,它位于菜單中指定的位置 GetSystemMenu 取得指定窗口的系統菜單的句柄 HiliteMenuItem 控制頂級菜單條目的加亮顯示狀態 InsertMenu 在菜單的指定位置處插入一個菜單條目,并根據需要將其他條目向下移動 InsertMenuItem 插入一個新菜單條目 IsMenu 判斷指定的句柄是否為一個菜單的句柄 LoadMenu 從指定的模塊或應用程序實例中載入一個菜單 LoadMenuIndirect 載入一個菜單 MenuItemFromPoint 判斷哪個菜單條目包含了屏幕上一個指定的點 ModifyMenu 改變菜單條目 RemoveMenu 刪除指定的菜單條目 SetMenu 設置窗口菜單 SetMenuContextHelpId 設置一個菜單的幫助場景ID SetMenuDefaultItem 將一個菜單條目設為默認條目 SetMenuItemBitmaps 設置一幅特定位圖,令其在指定的菜單條目中使用,代替標準的復選符號(√) SetMenuItemInfo 為一個菜單條目設置指定的信息 TrackPopupMenu 在屏幕的任意地方顯示一個彈出式菜單 TrackPopupMenuEx 與TrackPopupMenu相似,只是它提供了額外的功能

            posted @ 2008-08-28 10:28 深邃者 閱讀(247) | 評論 (0)編輯 收藏

            windows消息說明

            WM_PAINT = $000F;
            要求一個窗口重畫自己
            WM_CLOSE = $0010;
            當一個窗口或應用程序要關閉時發送一個信號
            WM_QUERYENDSESSION = $0011;
            當用戶選擇結束對話框或程序自己調用ExitWindows函數
            WM_QUIT = $0012;
            用來結束程序運行或當程序調用postquitmessage函數
            WM_QUERYOPEN = $0013;
            當用戶窗口恢復以前的大小位置時,把此消息發送給某個圖標
            WM_ERASEBKGND = $0014;
            當窗口背景必須被擦除時(例在窗口改變大小時)
            WM_SYSCOLORCHANGE = $0015;
            當系統顏色改變時,發送此消息給所有頂級窗口
            WM_ENDSESSION = $0016;
            當系統進程發出WM_QUERYENDSESSION消息后,此消息發送給應用程序,
            通知它對話是否結束
            WM_SYSTEMERROR = $0017;
            WM_SHOWWINDOW = $0018;
            當隱藏或顯示窗口是發送此消息給這個窗口
            WM_ACTIVATEAPP = $001C;
            發此消息給應用程序哪個窗口是激活的,哪個是非激活的;
            WM_FONTCHANGE = $001D;
            當系統的字體資源庫變化時發送此消息給所有頂級窗口
            WM_TIMECHANGE = $001E;
            當系統的時間變化時發送此消息給所有頂級窗口
            WM_CANCELMODE = $001F;
            發送此消息來取消某種正在進行的摸態(操作)
            WM_SETCURSOR = $0020;
            如果鼠標引起光標在某個窗口中移動且鼠標輸入沒有被捕獲時,就發消息給某個窗口
            WM_MOUSEACTIVATE = $0021;
            當光標在某個非激活的窗口中而用戶正按著鼠標的某個鍵發送此消息給當前窗口
            WM_CHILDACTIVATE = $0022;
            發送此消息給MDI子窗口當用戶點擊此窗口的標題欄,或當窗口被激活,移動,改變大小

            WM_QUEUESYNC = $0023;
            此消息由基于計算機的訓練程序發送,通過WH_JOURNALPALYBACK的hook程序
            分離出用戶輸入消息
            WM_GETMINMAXINFO = $0024;
            此消息發送給窗口當它將要改變大小或位置;
            WM_PAINTICON = $0026;
            發送給最小化窗口當它圖標將要被重畫
            WM_ICONERASEBKGND = $0027;
            此消息發送給某個最小化窗口,僅當它在畫圖標前它的背景必須被重畫
            WM_NEXTDLGCTL = $0028;
            發送此消息給一個對話框程序去更改焦點位置
            WM_SPOOLERSTATUS = $002A;
            每當打印管理列隊增加或減少一條作業時發出此消息
            WM_DRAWITEM = $002B;
            當button,combobox,listbox,menu的可視外觀改變時發送
            此消息給這些空件的所有者

            WM_MEASUREITEM = $002C;
            當button, combo box, list box, list view control, or menu item 被創建時
            發送此消息給控件的所有者
            WM_DELETEITEM = $002D;
            當the list box 或 combo box 被銷毀 或 當 某些項被刪除通過LB_DELETESTRING, LB_RESETCONTENT, CB_DELETESTRING, or CB_RESETCONTENT 消息
            WM_VKEYTOITEM = $002E;
            此消息有一個LBS_WANTKEYBOARDINPUT風格的發出給它的所有者來響應WM_KEYDOWN消息
            WM_CHARTOITEM = $002F;
            此消息由一個LBS_WANTKEYBOARDINPUT風格的列表框發送給他的所有者來響應WM_CHAR消息

            posted @ 2008-07-22 18:16 深邃者 閱讀(137) | 評論 (0)編輯 收藏

            MFC共享DLL模塊狀態的切換AFX_MODULE_STATE

            在DLL中使用資源(一)

            yuwei - 轉載 (2004-12-10 14:02:00)  .Net控件開發 ActiveX/COM開發 CLX/VCL開發 Java組件開發 VC/MFC 控件使用經驗談 DHtml/Script 控件開發基礎 數據庫 編程規范 單元測試 可重用軟件技術理論 其 它
             
             
             
               

            在DLL中使用資源(一)
            現在最常看見的關于DLL的問題就是如何在DLL中使用對話框,這是一個很普遍的關于如何在DLL中使用資源的問題。這里我們從Win32 DLL和MFC DLL兩個方面來分析并解決這個問題。

            1.Win32 DLL
            在Win32 DLL中使用對話框很簡單,你只需要在你的DLL中添加對話框資源,而且可以在對話框上面設置你所需要的控件。然后使用DialogBox或者CreateDialog這兩個函數(或相同作用的其它函數)來創建對話框,并定義你自己的對話框回調函數處理對話框收到的消息。下面通過一個具體實例來學習如何在Win32 DLL中使用對話框,可以按照以下步驟來完成這個例子:


            1)在VC菜單中File->New新建一個命名為UseDlg的Win32 Dynamic-Link Library工程,下一步選擇A simple DLL project。


            2)在VC菜單中Insert->Resource添加一個ID為IDD_DLG_SHOW的Dialog資源,將此Dialog上的Cancel按鈕去掉,僅保留OK按鈕。再添加一個ID為IDD_ABOUTBOX的對話框,其Caption為About。保存此資源,將資源文件命名為UseDlg.rc。并將resource.h和UseDlg.rc加入到工程里面。


            3)在UseDlg.app中包含resource.h,并添加如下代碼:


            HINSTANCE hinst = NULL;

            HWND hwndDLG = NULL;


            BOOL CALLBACK DlgProc(HWND hDlg, UINT message,

            WPARAM wParam, LPARAM lParam);

            BOOL CALLBACK AboutProc(HWND hDlg, UINT message,

            WPARAM wParam, LPARAM lParam);

            extern "C" __declspec(dllexport) void ShowDlg();


            BOOL APIENTRY DllMain( HANDLE hModule,

            DWORD ul_reason_for_call,

            LPVOID lpReserved

            )

            {

            switch(ul_reason_for_call)

            {

            case DLL_PROCESS_ATTACH:

            hinst = (HINSTANCE)hModule;

            case DLL_PROCESS_DETACH:

            break;

            }

            return TRUE;

            }


            extern "C" __declspec(dllexport) void ShowDlg()

            {

            hwndDLG = CreateDialog(hinst,MAKEINTRESOURCE(IDD_DLG_SHOW),

            NULL,(DLGPROC)DlgProc);

            ShowWindow(hwndDLG, SW_SHOW);

            }


            BOOL CALLBACK DlgProc(HWND hDlg, UINT message,

            WPARAM wParam, LPARAM lParam)

            {

            switch(message)

            {

            case WM_INITDIALOG:

            return TRUE;


            case WM_COMMAND:

            if(LOWORD(wParam)==IDOK)

            DialogBox(hinst,MAKEINTRESOURCE(IDD_ABOUTBOX),

            hDlg,(DLGPROC)AboutProc);

            return TRUE;

            case WM_CLOSE:

            DestroyWindow(hDlg);

            hwndDLG = NULL;

            return TRUE;

            }

            return FALSE;

            }


            BOOL CALLBACK AboutProc(HWND hDlg, UINT message,

            WPARAM wParam, LPARAM lParam)

            {

            switch(message)

            {

            case WM_CLOSE:

            EndDialog(hDlg,NULL);

            hwndDLG = NULL;

            return TRUE;

            }

            return FALSE;

            }


            4)編譯生成UseDlg.dll和UseDlg.lib。


            接下來我們建立調用此DLL的應用程序,其步驟如下:



            1)在VC菜單中File->New新建一個命名為Use的MFC AppWizard(exe)工程,下一步選擇Dialog Based之后點擊Finish按鈕。


            2)在主對話框上面添加一個按鈕,之后雙擊此按鈕,會彈出Add Member Function的對話框,直接點擊OK進入void CUseDlg::OnButton1()函數。并在此函數內添加一個函數調用:ShowDlg();。


            3)緊跟在#include語句后面加上如下代碼:


            extern "C" __declspec(dllexport) void ShowDlg();

            #pragma comment(lib,"debug/UseDlg")


            4)將上面UseDlg工程中生成的UseDlg.dll和UseDlg.lib兩個文件復制到Use工程的Debug目錄內。


            5)編譯生成Use.exe。


            運行Use.exe,點擊Button1按鈕,可以看到一個名稱為Dialog的非模態對話框彈出。點擊上面的按鈕,可以彈出模態對話框About。運行成功。


            讓我們來回顧一下在Win32 DLL中使用對話框的過程。


            在DLL中,我們定義了兩個對話框資源:IDD_DLG_SHOW和IDD_ABOUTBOX,并且導出了函數ShowDlg。在函數ShowDlg之中使用CreateDialog函數創建了非模態對話框IDD_DLG_SHOW,并指定了該對話框的回調函數DlgProc。在DlgProc之中處理了WM_INITDIALOG、WM_COMMAND和WM_CLOSE消息,以響應用戶對對話框所做的動作。在處理按鈕動作的時候,使用DialogBox函數創建IDD_ABOUTBOX這個模態對話框,指定其回調函數為AboutProc,并且在AboutProc中處理其相應消息。


            在EXE中,我們使用隱式鏈接的方法來調用DLL,并使用DLL中導出的ShowDlg函數來調用DLL中的對話框。


            在Win32 DLL中使用對話框就是這么簡單,下面讓我們來看一下在MFC DLL中如何使用對話框。

            2.MFC DLL
            在MFC DLL中使用對話框不像Win32 DLL中那么簡單,主要是因為MFC程序中存在一個模塊狀態(Module State)的問題,也就是資源重復的問題。(此處的術語模塊是指一個可執行程序,或指其操作不依賴于應用程序的其余部分但使用MFC運行庫的共享副本的一個DLL(或一組DLL)。我們所創建的MFC DLL就是這種模塊的一個典型實例。)


            在每個模塊(EXE或DLL)中,都存在一種全局的狀態數據,MFC依靠這種全局的狀態數據來區分不同的模塊,以執行正確的操作。這種數據包括:Windows實例句柄(用于加載資源),指向應用程序當前的CWinApp和CWinThread對象的指針,OLE模塊引用計數,以及維護Windows對象句柄與相應的MFC對象實例之間連接的各種映射等。但當應用程序使用多個模塊時,每個模塊的狀態數據不是應用程序范圍的。相反,每個模塊具有自已的MFC狀態數據的私有副本。這種全局的狀態數據就叫做MFC模塊狀態。


            模塊的狀態數據包含在結構中,并且總是可以通過指向該結構的指針使用。當代碼在執行時進入了某一個模塊時,只有此模塊的狀態為“當前”或“有效”狀態時,MFC才能正確的區分此模塊并執行正確的操作。


            例如,MFC應用程序可以使用下面代碼從資源文件中加載字符串:


            CString str;

            str.LoadString(IDS_MYSTRING);


            使用這種代碼非常方便,但它掩蓋了這樣一個事實:即此程序中IDS_MYSTRING可能不是唯一的標識符。一個程序可以加載多個DLL,某些DLL可能也用IDS_MYSTRING標識符定義了一個資源。MFC怎樣知道應該加載哪個資源呢?MFC使用當前模塊狀態查找資源句柄。如果當前模塊不是我們要使用的正確模塊,那么就會產生不正確的調用或者錯誤。



            按照MFC庫的鏈接方法,一個MFC DLL有兩種使用MFC庫的方法:靜態鏈接到MFC的DLL和動態鏈接到MFC的DLL。下面我們就按照這兩種類型的MFC DLL來介紹如何切換當前模塊狀態以正確的在MFC DLL中使用資源。

            1、靜態鏈接到MFC的DLL

            靜態鏈接到MFC的規則DLL與MFC庫靜態鏈接,則此時MFC庫不能共享,所以MFC總是使用它所鏈接的DLL的模塊狀態。這樣也就不存在管理模塊狀態的問題。但使用這種方法的缺點是DLL程序將會變大,而且會在程序中留下重復代碼。下面給出的例子驗證了這一點。本例可以按照以下步驟來完成:


            1)在VC菜單中File->New新建一個命名為DLLStatic的MFC AppWizard的工程,下一步選擇Regular DLL with MFC statically linked。


            2)在工程中添加一個對話框資源,其ID為:IDD_ABOUTBOX。并在resource.h之中將IDD_ABOUTBOX 的數值改為100。


            3)在DLLStatic.cpp中定義如下函數:


            void ShowDlg()

            {

            CDialog dlg(IDD_ABOUTBOX);

            dlg.DoModal();

            }


            4)在DLLStatic.def文件中的EXPORTS語句中添加一行:ShowDlg,以導出ShowDlg函數。


            5)編譯生成DLLStatic.dll和DLLStatic.lib。


            繼續使用上一節中的Use工程,將前面生成的DLLStatic.dll和DLLStatic.lib兩個文件復制到工程的Debug目錄內,并將


            extern "C" __declspec(dllexport) void ShowDlg();

            #pragma comment(lib,"debug/UseDlg")


            這兩行改為:


            void ShowDlg();

            #pragma comment(lib,"debug/DLLStatic")


            編譯并運行Use.exe。點擊按鈕,可以看到DLLStatic中的模態對話框彈出。


            本例中,可以注意到DLL中所定義的About對話框資源與EXE中所定義的About對話框資源ID完全相同,但是當我們點擊Use.exe上面的按鈕時,彈出的是DLL中的模態對話框。說明,當使用靜態鏈接到MFC的規則DLL時,不存在管理模塊狀態的問題。

             
            2、動態鏈接到MFC的DLL

            在討論關于動態鏈接到MFC的DLL的模塊狀態問題之前,先來看一個例子。本例可以通過如下步驟來完成:



            1)在VC菜單中File->New新建一個命名為DLLShared的MFC AppWizard的工程,下一步選擇Regular DLL using shared MFC DLL。



            2)在工程中添加一個對話框資源,其ID為:IDD_ABOUTBOX。并在resource.h之中將IDD_ABOUTBOX 的數值改為100。



            3)在DLLShared.cpp中定義如下函數:



            void ShowDlg()

            {

            CDialog dlg(IDD_ABOUTBOX);

            dlg.DoModal();

            }



            4)在DLLShared.def文件中的EXPORTS語句中添加一行:ShowDlg,以導出ShowDlg函數。



            5)編譯生成DLLShared.dll和DLLShared.lib。



            繼續使用上面的Use工程,將前面生成的DLLShared.dll和DLLShared.lib兩個文件復制到工程的Debug目錄內,并將



            extern "C" __declspec(dllexport) void ShowDlg();

            #pragma comment(lib,"debug/DLLStatic")



            這兩行改為:



            void ShowDlg();

            #pragma comment(lib,"debug/DLLShared")



            編譯并運行Use.exe。點擊按鈕,這次你看到了什么?對,沒錯,這次彈出的是Use.exe的關于對話框。將上述例子的DLL類型換成MFC Extension DLL(using shared MFC DLL)也會出現相同的問題。



            為什么會出現上面的問題?這是因為在使用了MFC共享庫的時候,默認情況下,MFC使用主應用程序的資源句柄來加載資源模板。雖然我們調用的是DLL中的函數來顯示DLL中的對話框,并且對應的對話框模板是存儲在DLL中的,但MFC仍舊在主應用程序也就是Use.exe中尋找相應的對話框模板。由于在DLL中所定義的對話框資源ID與主應用程序中所定義的關于對話框的資源ID相同,所以MFC就把主應用程序中的關于對話框顯示了出來。如果二者不同,則MFC就認為DLL中所定義的對話框資源不存在,dlg.DoModal會返回0,也就是什么都不會顯示。



            那么如何解決上述問題呢?解決辦法就是在適當的時候進行模塊狀態切換,以保證具有當前狀態的模塊是我們所需要的模塊從而使用正確的資源。MFC提供了下列函數和宏來完成這些工作:



            AfxGetStaticModuleState:這是一個函數,其函數原型為:



            AFX_MODULE_STATE* AFXAPI AfxGetStaticModuleState( );



            此函數在堆棧上構造AFX_MODULE_STATE類的實例pModuleState并對其賦值后將其返回。在AFX_MODULE_STATE類的構造函數中,該類獲取指向當前模塊狀態的指針并將其存儲在成員變量中,然后將pModuleState設置為新的有效模塊狀態。在它的析構函數中,該類將存儲在其成員變量中的指針還原為存貯的前一個模塊狀態。



            AFX_MANAGE_STATE:這是一個宏,其原型為:



            AFX_MANAGE_STATE( AFX_MODULE_STATE* pModuleState )



            該宏用于將pModuleState(指向包含模塊全局數據也就是模塊狀態的AFX_MODULE_STATE結構的指針)設置為當前的即時作用空間中(the remainder of the immediate containing scope)的有效模塊狀態。在離開包含該宏的作用空間時,前一個有效的模塊狀態自動還原。



            AfxGetResourceHandle:這個函數的原型為:



            HINSTANCE AfxGetResourceHandle( );



            該函數返回了一個保存了HINSTANCE類型的、應用程序默認所加載資源的模塊的句柄。



            AfxSetResourceHandle:這個函數的原型為:



            void AfxSetResourceHandle( HINSTANCE hInstResource );



            該函數將hInstResource所代表的模塊設置為具有當前狀態的模塊。



            通過使用上述四個函數或宏就可以正確的在動態鏈接到MFC的DLL中切換模塊狀態。接下來我們將通過修改上面出現問題的那個例子來介紹如何使用上述四個函數或宏。先來看看Regular DLL using shared MFC DLL類型:



            在上述例子的第三步的ShowDlg函數的第一條語句前加上如下語句(要確保該語句在函數實現的第一行):



            AFX_MANAGE_STATE(AfxGetStaticModuleState());



            之后重新編譯生成DLLShared.dll和DLLShared.lib,并將這兩個文件重新拷貝到Use工程的Debug目錄內。這次編譯生成Use.exe并運行,點擊按鈕,可以看到彈出的時我們在DLL中所加入的那個對話框,而不再是Use.exe的關于對話框了。



            通過上面的講解,相信你已經知道該語句的作用了。在函數ShowDlg的第一行加上這么一句后,每次調用DLL的應用程序使用該函數的時候,MFC庫都會自動切換當前模塊狀態,這樣就保證了資源讀取的正確性。



            AFX_MANAGE_STATE(AfxGetStaticModuleState());是自動切換當前模塊狀態,也可以通過使用AfxGetResourceHandle和AfxSetResourceHandle來手動切換當前模塊狀態。具體使用方法如下:



            在上述例子的第三步的ShowDlg函數的第一條語句前加上如下語句(要確保該語句在函數實現的第一行):



            HINSTANCE save_hInstance = AfxGetResourceHandle();

            AfxSetResourceHandle(theApp.m_hInstance);



            在調用對話框成功之后,也就是dlg.DoModal();之后,添加:



            AfxSetResourceHandle(save_hInstance);



            這種方法在進入ShowDlg函數之后,通過AfxGetResourceHandle來獲得并保存當前狀態模塊的句柄。然后獲得DLL模塊的句柄theApp.m_hInstance(當然,也可以使用GetModuleHandle函數來獲得DLL模塊的句柄),并使用AfxSetResourceHandle函數來將其設置為當前狀態狀態。最后在調用對話框成功之后再用恢復AfxSetResourceHandle資源句柄,將當前模塊狀態恢復。



            這樣做有些麻煩,但是有一點好處是可以在完成使用資源的任務之后就可以立即恢復資源句柄。而AFX_MANAGE_STATE(AfxGetStaticModuleState());的方法只能等函數的作用空間結束之后才恢復資源句柄。由于可執行文件必須重畫工具條等原因,因此建議只要有可能就必須恢復資源句柄,否則可能會遇到許多問題。比如說,如果用戶移動DLL的對話框,而此時資源句柄仍然為DLL的資源,那么程序就會崩潰。最好的恢復句柄的時機在對話框響應WM_INITDIALOG消息的時候,因為這時對話框的模板等已經讀出了。


            對于MFC Extension DLL(using shared MFC DLL)類型的MFC DLL,切換當前模塊狀態的方法與Regular DLL using shared MFC DLL類型的MFC DLL所使用的方法很相似,這里不再舉例實現。二者不同的地方如下:



            在MFC擴展DLL中使用AFX_MANAGE_STATE(AfxGetStaticModuleState());時,會產生如下錯誤:



            mfcs42d.lib(dllmodul.obj) : error LNK2005: __pRawDllMain already defined in dllextend.obj

            mfcs42d.lib(dllmodul.obj) : error LNK2005: _DllMain@12 already defined in dllextend.obj

            mfcs42d.lib(dllmodul.obj) : error LNK2005: __pRawDllMain already defined in dllextend.obj



            因此在MFC擴展DLL中需要將AFX_MANAGE_STATE(AfxGetStaticModuleState());換成AFX_MANAGE_STATE(AfxGetAppModuleState());才能正確切換當前模塊狀態。



            在MFC擴展DLL中使用AfxGetResourceHandle和AfxSetResourceHandle的方法與在Regular DLL using shared MFC DLL類型的MFC DLL中所使用的方法相同。并且,DLL模塊的句柄可以通過MFC提供的DlgextentDLL這個結構的hModule成員來獲得。即使用AfxSetResourceHandle(DlgextentDLL.hModule);語句。



            當然,對于動態鏈接到MFC的DLL,也可以在調用該DLL的MFC應用程序中使用AfxGetResourceHandle和AfxSetResourceHandle兩個函數來切換當前狀態模塊。該DLL模塊的句柄可以用GetModuleHandle函數來獲得。在此不再贅述。  

            posted @ 2008-07-22 18:14 深邃者 閱讀(366) | 評論 (0)編輯 收藏

            讓對話框對UPDATE_COMMAND_UI生效

             問題:一般情況下我們用UPDATE_COMMAND_UI來修改菜單的狀態(enable/disable, check/uncheck, change text),但這個方法在一個基于對話框上的菜單卻沒有效果。
            void CTestDlg::OnUpdateFileExit(CCmdUI* pCmdUI)
            {
                 pCmdUI->Enable(FALSE);
                 pCmdUI->SetCheck(TRUE);
                 pCmdUI->SetRadio(TRUE);
                 pCmdUI->SetText("Close");
            //以上方法在MDI、SDI程序中都能起作用,在對話框中卻沒有效果,根本沒有調用這個函數。
            }
             
            原因分析:當顯示一個下拉的菜單的時候,在顯示菜單前會發送WM_INITMENUPOPUP消息。而CFrameWnd::OnInitMenuPopup函數會刷新這個菜單項,同時如果有UPDATE_COMMAND_UI響應函數,則調用它。通過它來更新反應每個菜單的外觀效果(enabled/disabled, checked/unchecked).
            在一個基于對話框的程序中,因為沒有OnInitMenuPopup函數,所以不會調用UPDATE_COMMAND_UI響應函數,而是使用了CWnd類的默認處理, 這種處理沒有調用UPDATE_COMMAND_UI響應函數。

            解決方法如下:
            第一步:
            在對話框類的.cpp文件,添加一個ON_WM_INITMENUPOPUP入口到消息映射里面
            BEGIN_MESSAGE_MAP(CTestDlg, CDialog)
            //}}AFX_MSG_MAP
            ON_WM_INITMENUPOPUP()
            END_MESSAGE_MAP()
            第二步:
            在對話框類的.h文件添加消息函數聲明。
            // Generated message map functions
            //{{AFX_MSG(CDisableDlgMenuDlg)
            afx_msg void OnInitMenuPopup(CMenu *pPopupMenu, UINT nIndex,BOOL bSysMenu);
            //}}AFX_MSG
            DECLARE_MESSAGE_MAP()
            第三步:
            在對話框類的.cpp文件添加如下函數代碼(大部分代碼取自WinFrm.cpp文件的函數CFrameWnd::OnInitMenuPopup):
            void C******Dlg::OnInitMenuPopup(CMenu *pPopupMenu, UINT nIndex,BOOL bSysMenu)
            {
                 ASSERT(pPopupMenu != NULL);
                // Check the enabled state of various menu items.

                CCmdUI state;
                state.m_pMenu = pPopupMenu;
                ASSERT(state.m_pOther == NULL);
                ASSERT(state.m_pParentMenu == NULL);

                // Determine if menu is popup in top-level menu and set m_pOther to
                // it if so (m_pParentMenu == NULL indicates that it is secondary popup).
                HMENU hParentMenu;
                if (AfxGetThreadState()->m_hTrackingMenu == pPopupMenu->m_hMenu)
                     state.m_pParentMenu = pPopupMenu;??? // Parent == child for tracking popup.
               else if ((hParentMenu = ::GetMenu(m_hWnd)) != NULL)
               {
                   CWnd* pParent = this;
                   // Child windows don't have menus--need to go to the top!
                   if (pParent != NULL &&
            (hParentMenu = ::GetMenu(pParent->m_hWnd)) != NULL)
                  {
                       int nIndexMax = ::GetMenuItemCount(hParentMenu);
                      for (int nIndex = 0; nIndex < nIndexMax; nIndex++)
                     {
                         if (::GetSubMenu(hParentMenu, nIndex) == pPopupMenu->m_hMenu)
                         {
                            // When popup is found, m_pParentMenu is containing menu.
                            state.m_pParentMenu = CMenu::FromHandle(hParentMenu);
                            break;
                         }
                     }
                }
            }

              state.m_nIndexMax = pPopupMenu->GetMenuItemCount();
              for (state.m_nIndex = 0; state.m_nIndex < state.m_nIndexMax;
              state.m_nIndex++)
              {
                  state.m_nID = pPopupMenu->GetMenuItemID(state.m_nIndex);
                  if (state.m_nID == 0)
                      continue; // Menu separator or invalid cmd - ignore it.

                  ASSERT(state.m_pOther == NULL);
                  ASSERT(state.m_pMenu != NULL);
                  if (state.m_nID == (UINT)-1)
                  {
                      // Possibly a popup menu, route to first item of that popup.
                      state.m_pSubMenu = pPopupMenu->GetSubMenu(state.m_nIndex);
                      if (state.m_pSubMenu == NULL ||
             (state.m_nID = state.m_pSubMenu->GetMenuItemID(0)) == 0 ||
            state.m_nID == (UINT)-1)
                   {
                          continue;    // First item of popup can't be routed to.
                   }
                   state.DoUpdate(this, TRUE);?? // Popups are never auto disabled.
                  }
                  else
                 {
                      // Normal menu item.
                      // Auto enable/disable if frame window has m_bAutoMenuEnable
                      // set and command is _not_ a system command.
                     state.m_pSubMenu = NULL;
                     state.DoUpdate(this, FALSE);
                  }

                  // Adjust for menu deletions and additions.
                  UINT nCount = pPopupMenu->GetMenuItemCount();
                  if (nCount < state.m_nIndexMax)
                    {
                               state.m_nIndex -= (state.m_nIndexMax - nCount);
                               while (state.m_nIndex < nCount &&
                              pPopupMenu->GetMenuItemID(state.m_nIndex) == state.m_nID)
                       {
                        state.m_nIndex++;
                        }
                   }
                    state.m_nIndexMax = nCount;
               }
            }

            posted @ 2008-07-17 11:42 深邃者 閱讀(203) | 評論 (0)編輯 收藏

            字符類型數據轉換

                 摘要: 1/**//**//**///////////////////////////////////////////////////////////////////////////  2// Module: stringutility.h  3// Conversion among wchar_t, ch...  閱讀全文

            posted @ 2008-07-15 08:49 深邃者 閱讀(160) | 評論 (0)編輯 收藏

            僅列出標題
            共5頁: 1 2 3 4 5 
            国产AⅤ精品一区二区三区久久| 99国产欧美精品久久久蜜芽| 久久久一本精品99久久精品88| 久久中文字幕人妻丝袜| 久久久WWW成人| 久久人人爽人人爽人人片AV东京热| 午夜精品久久久久久影视777| 国产香蕉久久精品综合网| 久久五月精品中文字幕| 久久久久久极精品久久久| 久久精品亚洲精品国产欧美| 一本色综合久久| 成人久久精品一区二区三区| 国产午夜精品久久久久免费视 | 国产91久久综合| 丁香五月综合久久激情| 手机看片久久高清国产日韩| 久久人人爽人人爽人人片AV高清 | 日韩久久无码免费毛片软件| 一级a性色生活片久久无| 久久精品国产精品亚洲精品| 精品综合久久久久久888蜜芽| 99久久99久久精品国产片| 精品熟女少妇AV免费久久 | 丁香狠狠色婷婷久久综合| 国产精品美女久久久| 国产精品永久久久久久久久久| 久久青青草原亚洲av无码| 亚洲国产另类久久久精品黑人 | 国产伊人久久| 国产精品久久久久无码av| 精品无码久久久久久久久久| 97久久国产露脸精品国产| 久久综合一区二区无码| 亚洲AV无码久久精品成人| 66精品综合久久久久久久| 超级97碰碰碰碰久久久久最新 | 综合久久精品色| 久久综合九色综合97_久久久| 91视频国产91久久久| 亚洲欧美国产日韩综合久久|