• <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

            導(dǎo)航

            <2025年5月>
            27282930123
            45678910
            11121314151617
            18192021222324
            25262728293031
            1234567

            統(tǒng)計

            常用鏈接

            留言簿(2)

            隨筆檔案

            文章檔案

            搜索

            最新評論

            閱讀排行榜

            評論排行榜

            #

            堆~棧區(qū)別

            來自任我行C++blog:


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

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

             


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


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

            2.3申請大小的限制 
            棧:在Windows下,棧是向低地址擴展的數(shù)據(jù)結(jié)構(gòu),是一塊連續(xù)的內(nèi)存的區(qū)域。這句話的意思是棧頂?shù)牡刂泛蜅5淖畲笕萘渴窍到y(tǒng)預(yù)先規(guī)定好的,在WINDOWS下,棧的大小是2M(也有的說是1M,總之是一個編譯時就確定的常數(shù)),如果申請的空間超過棧的剩余空間時,將提示overflow。因此,能從棧獲得的空間較小。 
            堆:堆是向高地址擴展的數(shù)據(jù)結(jié)構(gòu),是不連續(xù)的內(nèi)存區(qū)域。這是由于系統(tǒng)是用鏈表來存儲的空閑內(nèi)存地址的,自然是不連續(xù)的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限于計算機系統(tǒng)中有效的虛擬內(nèi)存。由此可見,堆獲得的空間比較靈活,也比較大。 


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

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

            2.6存取效率的比較 

            char s1[] = "aaaaaaaaaaaaaaa"; 
            char *s2 = "bbbbbbbbbbbbbbbbb"; 
            aaaaaaaaaaa是在運行時刻賦值的; 
            而bbbbbbbbbbb是在編譯時就確定的; 
            但是,在以后的存取中,在棧上的數(shù)組比指針?biāo)赶虻淖址?例如堆)快。 
            比如: 
            #include 
            void main() 

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

            對應(yīng)的匯編代碼 
            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中,在根據(jù)edx讀取字符,顯然慢了。 


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



            windows進程中的內(nèi)存結(jié)構(gòu)


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

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

            首先,來了解一下 C 語言的變量是如何在內(nèi)存分部的。C 語言有全局變量(Global)、本地變量(Local),靜態(tài)變量(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; 

            //打印出各個變量的內(nèi)存地址 

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

            編譯后的執(zhí)行結(jié)果是: 

            0x0012ff78 
            0x0012ff7c 
            0x0012ff80 

            0x004068d0 
            0x004068d4 
            0x004068d8 

            0x004068dc 
            0x004068e0 
            0x004068e4 

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


            ├———————┤低端內(nèi)存區(qū)域 
            │ …… │ 
            ├———————┤ 
            │ 動態(tài)數(shù)據(jù)區(qū) │ 
            ├———————┤ 
            │ …… │ 
            ├———————┤ 
            │ 代碼區(qū) │ 
            ├———————┤ 
            │ 靜態(tài)數(shù)據(jù)區(qū) │ 
            ├———————┤ 
            │ …… │ 
            ├———————┤高端內(nèi)存區(qū)域 


            堆棧是一個先進后出的數(shù)據(jù)結(jié)構(gòu),棧頂?shù)刂房偸切∮诘扔跅5幕刂贰N覀兛梢韵攘私庖幌潞瘮?shù)調(diào)用的過程,以便對堆棧在程序中的作用有更深入的了解。不同的語言有不同的函數(shù)調(diào)用規(guī)定,這些因素有參數(shù)的壓入規(guī)則和堆棧的平衡。windows API的調(diào)用規(guī)則和ANSI C的函數(shù)調(diào)用規(guī)則是不一樣的,前者由被調(diào)函數(shù)調(diào)整堆棧,后者由調(diào)用者調(diào)整堆棧。兩者通過“__stdcall”和“__cdecl”前綴區(qū)分。先看下面這段代碼: 

            #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); //打印出各個變量的內(nèi)存地址 
            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; 

            編譯后的執(zhí)行結(jié)果是: 

            0x0012ff78 
            0x0012ff7c 
            0x0012ff80 

            0x0012ff68 
            0x0012ff6c 
            0x0012ff70 


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


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

            ;--------------func 函數(shù)的匯編代碼------------------- 

            :00401000 83EC0C sub esp, 0000000C //創(chuàng)建本地變量的內(nèi)存空間 
            :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 ;恢復(fù)堆棧,回收本地變量的內(nèi)存空間 
            :00401078 C3 ret 000C ;函數(shù)返回,恢復(fù)參數(shù)占用的內(nèi)存空間 
            ;如果是“__cdecl”的話,這里是“ret”,堆棧將由調(diào)用者恢復(fù) 

            ;-------------------函數(shù)結(jié)束------------------------- 


            ;--------------主程序調(diào)用func函數(shù)的代碼-------------- 

            :00401080 6A03 push 00000003 //壓入?yún)?shù)param3 
            :00401082 6A02 push 00000002 //壓入?yún)?shù)param2 
            :00401084 6A01 push 00000001 //壓入?yún)?shù)param1 
            :00401086 E875FFFFFF call 00401000 //調(diào)用func函數(shù) 
            ;如果是“__cdecl”的話,將在這里恢復(fù)堆棧,“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; 

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


            ├———————┤<—低端內(nèi)存區(qū)域 
            │ …… │ 
            ├———————┤<—由exploit填入數(shù)據(jù)的開始 
            │ │ 
            │ buffer │<—填入無用的數(shù)據(jù) 
            │ │ 
            ├———————┤ 
            │ RET │<—指向shellcode,或NOP指令的范圍 
            ├———————┤ 
            │ NOP │ 
            │ …… │<—填入的NOP指令,是RET可指向的范圍 
            │ NOP │ 
            ├———————┤ 
            │ │ 
            │ shellcode │ 
            │ │ 
            ├———————┤<—由exploit填入數(shù)據(jù)的結(jié)束 
            │ …… │ 
            ├———————┤<—高端內(nèi)存區(qū)域 


            windows下的動態(tài)數(shù)據(jù)除了可存放在棧中,還可以存放在堆中。了解C++的朋友都知道,C++可以使用new關(guān)鍵字來動態(tài)分配內(nèi)存。來看下面的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); //打印堆中變量的內(nèi)存地址 
            printf("0x%08x\n",bufflocal); //打印本地變量的內(nèi)存地址 
            printf("0x%08x\n",buffstatic); //打印靜態(tài)變量的內(nèi)存地址 

            void main() 

            func(); 
            return; 

            程序執(zhí)行結(jié)果為: 

            0x004107d0 
            0x0012ff04 
            0x004068c0 

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

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

            當(dāng)進程初始化時,系統(tǒng)會自動為進程創(chuàng)建一個默認堆,這個堆默認所占內(nèi)存的大小為1M。堆對象由系統(tǒng)進行管理,它在內(nèi)存中以鏈式結(jié)構(gòu)存在。通過下面的代碼可以通過堆動態(tài)申請內(nèi)存空間: 

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

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

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

            _CRTIMP int (__cdecl *printf)(const char *, ...); //定義STL函數(shù)printf 
            /*--------------------------------------------------------------------------- 
            寫到這里,我們順便來復(fù)習(xí)一下前面所講的知識: 
            (*注)printf函數(shù)是C語言的標(biāo)準函數(shù)庫中函數(shù),VC的標(biāo)準函數(shù)庫由msvcrt.dll模塊實現(xiàn)。 
            由函數(shù)定義可見,printf的參數(shù)個數(shù)是可變的,函數(shù)內(nèi)部無法預(yù)先知道調(diào)用者壓入的參數(shù)個數(shù),函數(shù)只能通過分析第一個參數(shù)字符串的格式來獲得壓入?yún)?shù)的信息,由于這里參數(shù)的個數(shù)是動態(tài)的,所以必須由調(diào)用者來平衡堆棧,這里便使用了__cdecl調(diào)用規(guī)則。BTW,Windows系統(tǒng)的API函數(shù)基本上是__stdcall調(diào)用形式,只有一個API例外,那就是wsprintf,它使用__cdecl調(diào)用規(guī)則,同printf函數(shù)一樣,這是由于它的參數(shù)個數(shù)是可變的緣故。 
            ---------------------------------------------------------------------------*/ 
            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); 

            執(zhí)行結(jié)果為: 

            0x00130000 
            0x00133100 
            0x00133118 

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

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

            #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編譯后的執(zhí)行結(jié)果: 
            0x0012ff7c 
            0x0012ff7b 
            0x0012ff80 
            變量在內(nèi)存中的順序:b(1字節(jié))-a(4字節(jié))-c(4字節(jié))。 

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

            這是用lcc編譯后的執(zhí)行結(jié)果: 
            0x0012ff6c 
            0x0012ff6b 
            0x0012ff64 
            變量在內(nèi)存中的順序:同上。 

            三個編譯器都做到了數(shù)據(jù)對齊,但是后兩個編譯器顯然沒VC“聰明”,讓一個char占了4字節(jié),浪費內(nèi)存哦。 


            基礎(chǔ)知識: 
            堆棧是一種簡單的數(shù)據(jù)結(jié)構(gòu),是一種只允許在其一端進行插入或刪除的線性表。允許插入或刪除操作的一端稱為棧頂,另一端稱為棧底,對堆棧的插入和刪除操作被稱為入棧和出棧。有一組CPU指令可以實現(xiàn)對進程的內(nèi)存實現(xiàn)堆棧訪問。其中,POP指令實現(xiàn)出棧操作,PUSH指令實現(xiàn)入棧操作。CPU的ESP寄存器存放當(dāng)前線程的棧頂指針,EBP寄存器中保存當(dāng)前線程的棧底指針。CPU的EIP寄存器存放下一個CPU指令存放的內(nèi)存地址,當(dāng)CPU執(zhí)行完當(dāng)前的指令后,從EIP寄存器中讀取下一條指令的內(nèi)存地址,然后繼續(xù)執(zhí)行。 


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



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

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

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

            在程序中,使用堆來動態(tài)分配和釋放對象。在下列情況下,調(diào)用堆操作: 

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


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


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

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

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

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

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

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

            在圖表的底部是“虛擬內(nèi)存分配程序”,操作系統(tǒng)使用它來保留和提交頁。所有分配程序使用虛擬內(nèi)存進行數(shù)據(jù)的存取。

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

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

            當(dāng)應(yīng)用程序或 DLL 創(chuàng)建私有堆時,這些堆存在于進程空間,并且在進程內(nèi)是可訪問的。從給定堆分配的數(shù)據(jù)將在同一個堆上釋放。(不能從一個堆分配而在另一個堆釋放。)

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

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

            Knowledge Base 文章 Q10758,“用 calloc() 和 malloc() 管理內(nèi)存” (搜索文章編號), 包含了有關(guān)這些主題的更多背景知識。另外,有關(guān)堆實現(xiàn)和設(shè)計的詳細討論也可在下列著作中找到:“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 的實現(xiàn)(Windows NT 版本 4.0 和更新版本) 使用了 127 個大小從 8 到 1,024 字節(jié)的 8 字節(jié)對齊塊空閑列表和一個“大塊”列表。“大塊”列表(空閑列表[0]) 保存大于 1,024 字節(jié)的塊。空閑列表容納了用雙向鏈表鏈接在一起的對象。默認情況下,“進程堆”執(zhí)行收集操作。(收集是將相鄰空閑塊合并成一個大塊的操作。)收集耗費了額外的周期,但減少了堆塊的內(nèi)部碎片。

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

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

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


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


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

            堆破壞造成的速度減慢。造成堆破壞的原因是應(yīng)用程序?qū)Χ褖K的不正確使用。通常情形包括釋放已釋放的堆塊或使用已釋放的堆塊,以及塊的越界重寫等明顯問題。(破壞不在本文討論范圍之內(nèi)。有關(guān)內(nèi)存重寫和泄漏等其他細節(jié),請參見 Microsoft Visual C++(R) 調(diào)試文檔 。)


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

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

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

            如何減少使用堆操作?通過利用數(shù)據(jù)結(jié)構(gòu)內(nèi)的位置可減少堆操作的次數(shù)。請考慮下列實例:

            struct ObjectA {
               // objectA 的數(shù)據(jù) 
            }

            struct ObjectB {
               // objectB 的數(shù)據(jù) 
            }

            // 同時使用 objectA 和 objectB

            //
            // 使用指針 
            //
            struct ObjectB {
               struct ObjectA * pObjA;
               // objectB 的數(shù)據(jù) 
            }

            //
            // 使用嵌入
            //
            struct ObjectB {
               struct ObjectA pObjA;
               // objectB 的數(shù)據(jù) 
            }

            //
            // 集合 – 在另一對象內(nèi)使用 objectA 和 objectB
            //

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

            避免使用指針關(guān)聯(lián)兩個數(shù)據(jù)結(jié)構(gòu)。如果使用指針關(guān)聯(lián)兩個數(shù)據(jù)結(jié)構(gòu),前面實例中的對象 A 和 B 將被分別分配和釋放。這會增加額外開銷—我們要避免這種做法。


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


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


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


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

            正確使用 _amblksiz。C 運行時 (CRT) 有它的自定義前端分配程序,該分配程序從后端(Win32 堆)分配大小為 _amblksiz 的塊。將 _amblksiz 設(shè)置為較高的值能潛在地減少對后端的調(diào)用次數(shù)。這只對廣泛使用 CRT 的程序適用。
            使用上述技術(shù)將獲得的好處會因?qū)ο箢愋汀⒋笮〖肮ぷ髁慷兴煌5偰茉谛阅芎涂缮s性方面有所收獲。另一方面,代碼會有點特殊,但如果經(jīng)過深思熟慮,代碼還是很容易管理的。

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

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

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


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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


            改進您的代碼,以使用較少的堆調(diào)用:分析關(guān)鍵路徑和固定數(shù)據(jù)結(jié)構(gòu)。


            在實現(xiàn)自定義的包裝程序之前使用量化堆調(diào)用成本的方法。


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


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


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



            http://community.csdn.net/Expert/FAQ/FAQ_Index.asp?id=172835
            我在學(xué)習(xí)對象的生存方式的時候見到一種是在堆棧(stack)之中,如下  
            CObject  object;  
            還有一種是在堆(heap)中  如下  
            CObject*  pobject=new  CObject();  
             
            請問  
            (1)這兩種方式有什么區(qū)別?  
            (2)堆棧與堆有什么區(qū)別??  
             
             
            ---------------------------------------------------------------  
             
            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)棧內(nèi)存分配運算內(nèi)置于處理器的指令集中,效率很高,但是分配的內(nèi)存容量有限。  
            4)堆上分配的內(nèi)存可以有我們自己決定,使用非常靈活。  

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

            Ansi、Unicode、UTF8字符串之間的轉(zhuǎn)換和寫入文本文件

            轉(zhuǎn)載請注明出處http://www.shnenglu.com/greatws/archive/2008/08/31/60546.html

            最近有人問我關(guān)于這個的問題,就此寫一篇blog

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

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

            Ansi轉(zhuǎn)Unicode
            介紹2種方法


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

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


                
            //方法2
                
            //設(shè)置當(dāng)前地域信息,不設(shè)置的話,使用這種方法,中文不會正確顯示
                
            //需要#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轉(zhuǎn)Ansi
            也是2種方法

            void CConvertDlg::OnBnClickedButtonUnicodeToAnsi()
            {
                
            // unicode to ansi
                wchar_t* wszString = L"abcd1234你我他";
                
            //預(yù)轉(zhuǎn)換,得到所需空間的大小,這次用的函數(shù)和上面名字相反
                int ansiLen = ::WideCharToMultiByte(CP_ACP, NULL, wszString, wcslen(wszString), NULL, 0, NULL, NULL);
                
            //同上,分配空間要給'\0'留個空間
                char* szAnsi = new char[ansiLen + 1];
                
            //轉(zhuǎn)換
                
            //unicode版對應(yīng)的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();
                
            //寫入內(nèi)容
                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轉(zhuǎn)UTF8

            void CConvertDlg::OnBnClickedButtonUnicodeToU8()
            {
                
            // unicode to UTF8
                wchar_t* wszString = L"abcd1234你我他";
                
            //預(yù)轉(zhuǎn)換,得到所需空間的大小,這次用的函數(shù)和上面名字相反
                int u8Len = ::WideCharToMultiByte(CP_UTF8, NULL, wszString, wcslen(wszString), NULL, 0, NULL, NULL);
                
            //同上,分配空間要給'\0'留個空間
                
            //UTF8雖然是Unicode的壓縮形式,但也是多字節(jié)字符串,所以可以以char的形式保存
                char* szU8 = new char[u8Len + 1];
                
            //轉(zhuǎn)換
                
            //unicode版對應(yīng)的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();
                
            //寫B(tài)OM,同樣低位寫在前
                cFile.Write("\xef\xbb\xbf"3);
                
            //寫入內(nèi)容
                cFile.Write(szU8, u8Len * sizeof(char));
                cFile.Flush();
                cFile.Close();
                delete[] szU8;
                szU8 
            =NULL;

            }

            UTF8轉(zhuǎn)UNICODE

            void CConvertDlg::OnBnClickedButtonU8ToUnicode()
            {
                
            //UTF8 to Unicode
                
            //由于中文直接復(fù)制過來會成亂碼,編譯器有時會報錯,故采用16進制形式
                char* szU8 = "abcd1234\xe4\xbd\xa0\xe6\x88\x91\xe4\xbb\x96\x00";
                
            //預(yù)轉(zhuǎn)換,得到所需空間的大小
                int wcsLen = ::MultiByteToWideChar(CP_UTF8, NULL, szU8, strlen(szU8), NULL, 0);
                
            //分配空間要給'\0'留個空間,MultiByteToWideChar不會給'\0'空間
                wchar_t* wszString = new wchar_t[wcsLen + 1];
                
            //轉(zhuǎn)換
                ::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轉(zhuǎn)換utf8和utf8轉(zhuǎn)換Ansi就是上面2個的結(jié)合,把unicode作為中間量,進行2次轉(zhuǎn)換即可

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

            讀寫ini文件

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

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


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

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

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

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

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

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

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

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

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

            如使用以下語句寫入了年齡信息:
            ::WriteProfileString("Test","age","25");  
            //在win.ini中創(chuàng)建一個Test節(jié),并在該節(jié)中創(chuàng)建一個鍵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) 讀取整數(shù)

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

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

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

            三、 刪除鍵值或節(jié)

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

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

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

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

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

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


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


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

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

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

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

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

            return count;
            }

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

            Window API函數(shù)大全(2)

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

            11. API之進程和線程函數(shù)

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

            12. API之控件與消息函數(shù)

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

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

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


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

            Window API函數(shù)大全(3)

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

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

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

            Window API函數(shù)大全(一)

            1. API之網(wǎng)絡(luò)函數(shù)

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

            2. API之消息函數(shù)

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

            3. API之文件處理函數(shù)

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

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

            5. API之文本和字體函數(shù)

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

            6. API之菜單函數(shù)

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

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

            windows消息說明

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

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

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

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

            MFC共享DLL模塊狀態(tài)的切換AFX_MODULE_STATE

            在DLL中使用資源(一)

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

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

            1.Win32 DLL
            在Win32 DLL中使用對話框很簡單,你只需要在你的DLL中添加對話框資源,而且可以在對話框上面設(shè)置你所需要的控件。然后使用DialogBox或者CreateDialog這兩個函數(shù)(或相同作用的其它函數(shù))來創(chuàng)建對話框,并定義你自己的對話框回調(diào)函數(shù)處理對話框收到的消息。下面通過一個具體實例來學(xué)習(xí)如何在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。


            接下來我們建立調(diào)用此DLL的應(yīng)用程序,其步驟如下:



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


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


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


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

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


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


            5)編譯生成Use.exe。


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


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


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


            在EXE中,我們使用隱式鏈接的方法來調(diào)用DLL,并使用DLL中導(dǎo)出的ShowDlg函數(shù)來調(diào)用DLL中的對話框。


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

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


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


            模塊的狀態(tài)數(shù)據(jù)包含在結(jié)構(gòu)中,并且總是可以通過指向該結(jié)構(gòu)的指針使用。當(dāng)代碼在執(zhí)行時進入了某一個模塊時,只有此模塊的狀態(tài)為“當(dāng)前”或“有效”狀態(tài)時,MFC才能正確的區(qū)分此模塊并執(zhí)行正確的操作。


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


            CString str;

            str.LoadString(IDS_MYSTRING);


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



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

            1、靜態(tài)鏈接到MFC的DLL

            靜態(tài)鏈接到MFC的規(guī)則DLL與MFC庫靜態(tài)鏈接,則此時MFC庫不能共享,所以MFC總是使用它所鏈接的DLL的模塊狀態(tài)。這樣也就不存在管理模塊狀態(tài)的問題。但使用這種方法的缺點是DLL程序?qū)兇螅視诔绦蛑辛粝轮貜?fù)代碼。下面給出的例子驗證了這一點。本例可以按照以下步驟來完成:


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


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


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


            void ShowDlg()

            {

            CDialog dlg(IDD_ABOUTBOX);

            dlg.DoModal();

            }


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


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


            繼續(xù)使用上一節(jié)中的Use工程,將前面生成的DLLStatic.dll和DLLStatic.lib兩個文件復(fù)制到工程的Debug目錄內(nèi),并將


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

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


            這兩行改為:


            void ShowDlg();

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


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


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

             
            2、動態(tài)鏈接到MFC的DLL

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



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



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



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



            void ShowDlg()

            {

            CDialog dlg(IDD_ABOUTBOX);

            dlg.DoModal();

            }



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



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



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



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

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



            這兩行改為:



            void ShowDlg();

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



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



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



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



            AfxGetStaticModuleState:這是一個函數(shù),其函數(shù)原型為:



            AFX_MODULE_STATE* AFXAPI AfxGetStaticModuleState( );



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



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



            AFX_MANAGE_STATE( AFX_MODULE_STATE* pModuleState )



            該宏用于將pModuleState(指向包含模塊全局數(shù)據(jù)也就是模塊狀態(tài)的AFX_MODULE_STATE結(jié)構(gòu)的指針)設(shè)置為當(dāng)前的即時作用空間中(the remainder of the immediate containing scope)的有效模塊狀態(tài)。在離開包含該宏的作用空間時,前一個有效的模塊狀態(tài)自動還原。



            AfxGetResourceHandle:這個函數(shù)的原型為:



            HINSTANCE AfxGetResourceHandle( );



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



            AfxSetResourceHandle:這個函數(shù)的原型為:



            void AfxSetResourceHandle( HINSTANCE hInstResource );



            該函數(shù)將hInstResource所代表的模塊設(shè)置為具有當(dāng)前狀態(tài)的模塊。



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



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



            AFX_MANAGE_STATE(AfxGetStaticModuleState());



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



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



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



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



            HINSTANCE save_hInstance = AfxGetResourceHandle();

            AfxSetResourceHandle(theApp.m_hInstance);



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



            AfxSetResourceHandle(save_hInstance);



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



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


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



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



            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());才能正確切換當(dāng)前模塊狀態(tài)。



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



            當(dāng)然,對于動態(tài)鏈接到MFC的DLL,也可以在調(diào)用該DLL的MFC應(yīng)用程序中使用AfxGetResourceHandle和AfxSetResourceHandle兩個函數(shù)來切換當(dāng)前狀態(tài)模塊。該DLL模塊的句柄可以用GetModuleHandle函數(shù)來獲得。在此不再贅述。  

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

            讓對話框?qū)PDATE_COMMAND_UI生效

             問題:一般情況下我們用UPDATE_COMMAND_UI來修改菜單的狀態(tài)(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程序中都能起作用,在對話框中卻沒有效果,根本沒有調(diào)用這個函數(shù)。
            }
             
            原因分析:當(dāng)顯示一個下拉的菜單的時候,在顯示菜單前會發(fā)送WM_INITMENUPOPUP消息。而CFrameWnd::OnInitMenuPopup函數(shù)會刷新這個菜單項,同時如果有UPDATE_COMMAND_UI響應(yīng)函數(shù),則調(diào)用它。通過它來更新反應(yīng)每個菜單的外觀效果(enabled/disabled, checked/unchecked).
            在一個基于對話框的程序中,因為沒有OnInitMenuPopup函數(shù),所以不會調(diào)用UPDATE_COMMAND_UI響應(yīng)函數(shù),而是使用了CWnd類的默認處理, 這種處理沒有調(diào)用UPDATE_COMMAND_UI響應(yīng)函數(shù)。

            解決方法如下:
            第一步:
            在對話框類的.cpp文件,添加一個ON_WM_INITMENUPOPUP入口到消息映射里面
            BEGIN_MESSAGE_MAP(CTestDlg, CDialog)
            //}}AFX_MSG_MAP
            ON_WM_INITMENUPOPUP()
            END_MESSAGE_MAP()
            第二步:
            在對話框類的.h文件添加消息函數(shù)聲明。
            // Generated message map functions
            //{{AFX_MSG(CDisableDlgMenuDlg)
            afx_msg void OnInitMenuPopup(CMenu *pPopupMenu, UINT nIndex,BOOL bSysMenu);
            //}}AFX_MSG
            DECLARE_MESSAGE_MAP()
            第三步:
            在對話框類的.cpp文件添加如下函數(shù)代碼(大部分代碼取自WinFrm.cpp文件的函數(shù)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 深邃者 閱讀(198) | 評論 (0)編輯 收藏

            字符類型數(shù)據(jù)轉(zhuǎn)換

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

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

            僅列出標(biāo)題
            共5頁: 1 2 3 4 5 
            狠狠色婷婷久久综合频道日韩| 婷婷五月深深久久精品| 日韩欧美亚洲国产精品字幕久久久| 中文字幕精品无码久久久久久3D日动漫 | 欧美久久天天综合香蕉伊| 亚洲Av无码国产情品久久| 亚洲人成网亚洲欧洲无码久久| 久久精品www人人爽人人| 91麻豆国产精品91久久久| 激情五月综合综合久久69| 久久久国产精品亚洲一区| 久久精品9988| 久久久久久久亚洲Av无码| 成人国内精品久久久久影院VR| 久久亚洲精精品中文字幕| 国产精品久久久久久久app| 久久久久人妻精品一区| 久久九九久精品国产免费直播| 久久综合久久综合久久| 91精品国产高清久久久久久io| 亚洲午夜久久久影院| 国产激情久久久久影院老熟女| 久久亚洲AV无码精品色午夜| 国产精品久久毛片完整版| 武侠古典久久婷婷狼人伊人| 国产精品免费看久久久| 2021精品国产综合久久| 四虎亚洲国产成人久久精品| 国产精品99久久精品| 免费精品久久天干天干| 久久国产V一级毛多内射| 久久夜色精品国产噜噜噜亚洲AV | 久久久久亚洲精品日久生情 | 国产精品内射久久久久欢欢| 亚洲国产另类久久久精品| 久久久久国产精品三级网| 久久精品国产亚洲欧美| 无码超乳爆乳中文字幕久久| 一本综合久久国产二区| 精品无码久久久久久久久久| 99久久精品午夜一区二区|