青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

春暖花開(kāi)
雪化了,花開(kāi)了,春天來(lái)了
posts - 149,comments - 125,trackbacks - 0
轉(zhuǎn)自:http://www.cic.tsinghua.edu.cn/jdx/book4/dlz.htm

 

第六章 Windows高級(jí)程序設(shè)計(jì)

       本節(jié)介紹Windows高級(jí)程序設(shè)計(jì)技術(shù),掌握了這些技術(shù),用戶(hù)就可以開(kāi)發(fā)較為大型的應(yīng)用程序,并且能夠使用Windows標(biāo)準(zhǔn)的網(wǎng)絡(luò)程序接口進(jìn)行網(wǎng)絡(luò)程序設(shè)計(jì)了。本節(jié)主要介紹下面幾方面內(nèi)容:

·         Windows內(nèi)存管理;

·         動(dòng)態(tài)連接庫(kù)(DLL);

·         Windows Sockets網(wǎng)絡(luò)程序設(shè)計(jì)。

6.1 Windows內(nèi)存管理

       內(nèi)存管理對(duì)于編寫(xiě)出高效率的Windows程序是非常重要的,這是因?yàn)?/span>Windows是多任務(wù)系統(tǒng),它的內(nèi)存管理和單任務(wù)的DOS相比有很大的差異。DOS是單任務(wù)操作系統(tǒng),應(yīng)用程序分配到內(nèi)存后,如果它不主動(dòng)釋放,系統(tǒng)是不會(huì)對(duì)它作任何改變的;但Windows卻不然,它在同一時(shí)刻可能有多個(gè)應(yīng)用程序共享內(nèi)存,有時(shí)為了使某個(gè)任務(wù)更好地執(zhí)行,Windows系統(tǒng)可能會(huì)對(duì)其它任務(wù)分配的內(nèi)存進(jìn)行移動(dòng),甚至刪除。因此,我們?cè)?/span>Windows應(yīng)用程序中使用內(nèi)存時(shí),要遵循Windows內(nèi)存管理的一些約定,以盡量提高Windows內(nèi)存的利用率。

6.1.1 Windows內(nèi)存對(duì)象

       Windows應(yīng)用程序可以申請(qǐng)分配屬于自己的內(nèi)存塊,內(nèi)存塊是應(yīng)用程序操作內(nèi)存的單位,它也稱(chēng)作內(nèi)存對(duì)象,在Windows中通過(guò)內(nèi)存句柄來(lái)操作內(nèi)存對(duì)象。內(nèi)存對(duì)象根據(jù)分配的范圍可分為全局內(nèi)存對(duì)象和局部?jī)?nèi)存對(duì)象;根據(jù)性質(zhì)可分為固定內(nèi)存對(duì)象,可移動(dòng)內(nèi)存對(duì)象和可刪除內(nèi)存對(duì)象。

       固定內(nèi)存對(duì)象,特別是局部固定內(nèi)存對(duì)象和DOS的內(nèi)存塊很類(lèi)似,它一旦分配,就不會(huì)被移動(dòng)或刪除,除非應(yīng)用程序主動(dòng)釋放它。并且對(duì)于局部固定內(nèi)存對(duì)象來(lái)說(shuō),它的內(nèi)存句柄本身就是內(nèi)存對(duì)象的16位近地址,可供應(yīng)用程序直接存取,而不必象其它類(lèi)型的內(nèi)存對(duì)象那樣要通過(guò)鎖定在內(nèi)存某固定地址后才能使用。

       可移動(dòng)內(nèi)存對(duì)象沒(méi)有固定的地址,Windows系統(tǒng)可以隨時(shí)把它們移到一個(gè)新地址。內(nèi)存對(duì)象的可移動(dòng)使得Windows能有效地利用自由內(nèi)存。例如,如果一個(gè)可移動(dòng)的內(nèi)存對(duì)象分開(kāi)了兩個(gè)自由內(nèi)存對(duì)象,Windows可以把可移動(dòng)內(nèi)存對(duì)象移走,將兩個(gè)自由內(nèi)存對(duì)象合并為一個(gè)大的自由內(nèi)存對(duì)象,實(shí)現(xiàn)內(nèi)存的合并與碎片回收。

       可刪除內(nèi)存對(duì)象與可移動(dòng)內(nèi)存對(duì)象很相似,它可以被Windows移動(dòng),并且當(dāng)Windows需要大的內(nèi)存空間滿(mǎn)足新的任務(wù)時(shí),它可以將可刪除內(nèi)存對(duì)象的長(zhǎng)度置為0,丟棄內(nèi)存對(duì)象中的數(shù)據(jù)。

       可移動(dòng)內(nèi)存對(duì)象和可刪除內(nèi)存對(duì)象在存取前必須使用內(nèi)存加鎖函數(shù)將其鎖定,鎖定了的內(nèi)存對(duì)象不能被移動(dòng)和刪除。因此,應(yīng)用程序在使用完內(nèi)存對(duì)象后要盡可能快地為內(nèi)存對(duì)象解鎖。內(nèi)存需要加鎖和解鎖增加了程序員的負(fù)擔(dān),但是它卻極大地改善了Windows內(nèi)存利用的效率,因此Windows鼓勵(lì)使用可移動(dòng)和可刪除的內(nèi)存對(duì)象,并且要求應(yīng)用程序在非必要時(shí)不要使用固定內(nèi)存對(duì)象。

       不同類(lèi)型的對(duì)象在它所處的內(nèi)存堆中的位置是不一樣的,圖6.2說(shuō)明內(nèi)存對(duì)象在堆中的位置:固定對(duì)象位于堆的底部;可移動(dòng)對(duì)象位于固定對(duì)象之上;可刪除對(duì)象從堆的頂部開(kāi)始分配。

6.1 內(nèi)存對(duì)象分配位置示意圖

 

6.1.2 局部?jī)?nèi)存對(duì)象管理

       局部?jī)?nèi)存對(duì)象在局部堆中分配,局部堆是應(yīng)用程序獨(dú)享的自由內(nèi)存,它只能由應(yīng)用程序的特定實(shí)例訪問(wèn)。局部堆建立在應(yīng)用程序的數(shù)據(jù)段中,因此,用戶(hù)可分配的局部?jī)?nèi)存對(duì)象的最大內(nèi)存空間不能超過(guò)64K。局部堆由Windows應(yīng)用程序在模塊定義文件中用HEAPSIZE語(yǔ)句申請(qǐng),HEAPSIZE指定以字節(jié)為單位的局部堆初始空間尺寸。Windows提供了一系列函數(shù)來(lái)操作局部?jī)?nèi)存對(duì)象。

6.1.2.1 分配局部?jī)?nèi)存對(duì)象

       LocalAlloc函數(shù)用來(lái)分配局部?jī)?nèi)存,它在應(yīng)用程序局部堆中分配一個(gè)內(nèi)存塊,并返回內(nèi)存塊的句柄。LocalAlloc函數(shù)可以指定內(nèi)存對(duì)象的大小和特性,其中主要特性有固定的(LMEM_FIXED),可移動(dòng)的(LMEM_MOVEABLE)和可刪除的(LMEM_DISCARDABLE)。如果局部堆中無(wú)法分配申請(qǐng)的內(nèi)存,則LocalAlloc函數(shù)返回NULL。下面的代碼用來(lái)分配一個(gè)固定內(nèi)存對(duì)象,因?yàn)榫植抗潭▋?nèi)存對(duì)象的對(duì)象句柄其本身就是16位內(nèi)存近地址,因此它可以被應(yīng)用程序直接存取。代碼如下:

char NEAR * pcLocalObject;

if (pcLocalObject = LocalAlloc(LMEM_FIXED, 32)) {

/* Use pcLocalObject as the near address of the Locally allocated object, It is not necessary to lock

   and unlock the fixed local object */

         .…..

}      

else {

         /* The 32 bytes cannot be allocated .React accordingly. */

}

6.1.2.2 加鎖與解鎖

       上面程序段分配的固定局部?jī)?nèi)存對(duì)象可以由應(yīng)用程序直接存取,但是,Windows并不鼓勵(lì)使用固定內(nèi)存對(duì)象。因此,在使用可移動(dòng)和可刪除內(nèi)存對(duì)象時(shí),就要經(jīng)常用到對(duì)內(nèi)存對(duì)象的加鎖與解鎖。

       不管是可移動(dòng)對(duì)象還是可刪除對(duì)象,在它分配后其內(nèi)存句柄是不變的,它是內(nèi)存對(duì)象的恒定引用。但是,應(yīng)用程序無(wú)法通過(guò)內(nèi)存句柄直接存取內(nèi)存對(duì)象,應(yīng)用程序要存取內(nèi)存對(duì)象還必須獲得它的近地址,這通過(guò)調(diào)用LocalLock函數(shù)實(shí)現(xiàn)。LocalLock函數(shù)將局部?jī)?nèi)存對(duì)象暫時(shí)固定在局部堆的某一位置,并返回該地址的近地址值,此地址可供應(yīng)用程序存取內(nèi)存對(duì)象使用,它在應(yīng)用程序調(diào)用 LocalUnlock函數(shù)解鎖此內(nèi)存對(duì)象之前有效。怎樣加鎖與解鎖可移動(dòng)內(nèi)存對(duì)象,請(qǐng)看如下代碼:

HLOCAL hLocalObject;

char NEAR *pcLocalObject;

if (hLocalObject = LocalAlloc(LMEM_MOVEABLE, 32)) {

         if (pcLocalObject = LocalLock(hLocalObject)) {

                   /*Use pcLocalObject as the near address of the locally allocated object */

                   .…..

                   LocalUnlock(hLocalObject);

         }

         else {

                   /* The lock failed. React accordingly. */

         }

}

else {

         /* The 32 bytes cannot be allocated. React accordingly. */

}

       應(yīng)用程序在使用完內(nèi)存對(duì)象后,要盡可能早地為它解鎖,這是因?yàn)?/span>Windows無(wú)法移動(dòng)被鎖住了的內(nèi)存對(duì)象。當(dāng)應(yīng)用程序要分配其它內(nèi)存時(shí),Windows不能利用被鎖住對(duì)象的區(qū)域,只能在它周?chē)鷮ふ遥@會(huì)降低Windows內(nèi)存管理的效率。

6.1.2.3 改變局部?jī)?nèi)存對(duì)象

       局部?jī)?nèi)存對(duì)象分配之后,還可以調(diào)用LocalReAlloc函數(shù)進(jìn)行修改。LocalReAlloc函數(shù)可以改變局部?jī)?nèi)存對(duì)象的大小而不破壞其內(nèi)容:如果比原來(lái)的空間小,則Windows將對(duì)象截?cái)啵蝗绻仍瓉?lái)大,則Windows將增加區(qū)域填0(使用LMEM_ZEROINIT選項(xiàng)),或者不定義該區(qū)域內(nèi)容。另外,LocalReAlloc函數(shù)還可以改變對(duì)象的屬性,如將屬性從LMEM_MOVEABLE改為LMEM_DISCARDABLE,或反過(guò)來(lái),此時(shí)必須同時(shí)指定LMEM_MODIFY選項(xiàng)。但是,LocalReAlloc函數(shù)不能同時(shí)改變內(nèi)存對(duì)象的大小和屬性,也不能改變具有LMEM_FIXED屬性的內(nèi)存對(duì)象和把其它屬性的內(nèi)存對(duì)象改為LMEM_FIXED屬性。如何將一個(gè)可移動(dòng)內(nèi)存對(duì)象改為可刪除的,請(qǐng)看下面的例子:

hLocalObject = LocalAlloc(32, LMEM_MOVEABLE);

.…..

hLocalObject = LocalReAlloc(hLocalObject, 32, LMEM_MODIFY| LMEM_KISCARDABLE);

6.1.2.4 釋放與刪除

       分配了的局部?jī)?nèi)存對(duì)象可以使用LocalDiscardLocalFree函數(shù)來(lái)刪除和釋放,刪除和釋放只有在內(nèi)存對(duì)象未鎖住時(shí)才有效。

       LocalFree函數(shù)用來(lái)釋放局部?jī)?nèi)存對(duì)象,當(dāng)一個(gè)局部?jī)?nèi)存對(duì)象被釋放時(shí),其內(nèi)容從局部堆移走,并且其句柄也從有效的局部?jī)?nèi)存表中移走,原來(lái)的內(nèi)存句柄變?yōu)椴豢捎谩?/span>LocalDiscard 函數(shù)用來(lái)刪除局部?jī)?nèi)存對(duì)象,它只移走對(duì)象的內(nèi)容,而保持其句柄有效,用戶(hù)在需要時(shí),還可以使用此內(nèi)存句柄用LocalReAlloc函數(shù)重新分配一塊內(nèi)存。

       另外,Windows還提供了函數(shù)LocalSize用于檢測(cè)對(duì)象所占空間;函數(shù)LocalFlags用于檢測(cè)內(nèi)存對(duì)象是否可刪除,是否已刪除,及其鎖計(jì)數(shù)值;函數(shù)LocalCompact用于確定局部堆的可用內(nèi)存。

6.1.3 全局內(nèi)存對(duì)象管理

       全局內(nèi)存對(duì)象在全局堆中分配,全局堆包括所有的系統(tǒng)內(nèi)存。一般來(lái)說(shuō),應(yīng)用程序在全局堆中進(jìn)行大型內(nèi)存分配(約大于1KB),在全局堆還可以分配大于64K的巨型內(nèi)存,這將在后面介紹。

6.1.3.1 分配全局內(nèi)存對(duì)象

       全局內(nèi)存對(duì)象使用GlobalAlloc函數(shù)分配,它和使用LocalAlloc分配局部?jī)?nèi)存對(duì)象很相似。使用GlobalAlloc的例子我們將和GlobalLock一起給出。

6.1.3.2 加鎖與解鎖

       全局內(nèi)存對(duì)象使用GlobalLock函數(shù)加鎖,所有全局內(nèi)存對(duì)象在存取前都必須加鎖。GlobalLock將對(duì)象鎖定在內(nèi)存固定位置,并返回一個(gè)遠(yuǎn)指針,此指針在調(diào)用GlobalUnlock之前保持有效。

       GlobalLockLocalLock稍有不同,因?yàn)槿謨?nèi)存對(duì)象可能被多個(gè)任務(wù)使用,因此在使用GlobalLock加鎖某全局內(nèi)存對(duì)象時(shí),對(duì)象可能已被鎖住,為了處理這種情況,Windows增加了一個(gè)鎖計(jì)數(shù)器。當(dāng)使用GlobalLock加鎖全局內(nèi)存對(duì)象時(shí),鎖計(jì)數(shù)器加1;使用GlobalUnlock解鎖對(duì)象時(shí),鎖計(jì)數(shù)器減1,只有當(dāng)鎖計(jì)數(shù)器為0時(shí),Windows才真正解鎖此對(duì)象。GlobalAllocGlobalLock的使用見(jiàn)如下的例子:

HGLOBAL hGlobalObject;

char FAR * lpGlobalObject;

if (hGlobalObject = GlobalAlloc(GMEM_MOVEABLE, 1024)) {

         if (lpGlobalObject = GlobalLock(hGlobalObject)) {

                   /* Use lpGlobalObject as the far address of the globally allocated object. */

                   .…..

                   GlobalUnlock (hGlobalObject);

         }

         else {

                   /* The lock failed .React accordingly. */

         }

}

else {

         /* The 1024 bytes cannot be allocated. React accordingly. */

}

6.1.3.3 修改全局內(nèi)存對(duì)象

       修改全局內(nèi)存對(duì)象使用GlobalReAlloc函數(shù),它和LocalReAlloc函數(shù)很類(lèi)似,這里不再贅述。修改全局內(nèi)存對(duì)象的特殊之處在于巨型對(duì)象的修改上,這一點(diǎn)我們將在后面講述。

6.1.3.4 內(nèi)存釋放及其它操作

       全局內(nèi)存對(duì)象使用GlobalFree函數(shù)和GlobalDiscard來(lái)釋放與刪除,其作用與LocalFreeLocalDiscard類(lèi)似。GlobalSize函數(shù)可以檢測(cè)內(nèi)存對(duì)象大小;GlobalFlags函數(shù)用來(lái)檢索對(duì)象是否可刪除,是否已刪除等信息;GlobalCompact函數(shù)可以檢測(cè)全局堆可用內(nèi)存大小。

6.1.3.5 巨型內(nèi)存對(duì)象

       如果全局內(nèi)存對(duì)象的大小為64KB或更大,那它就是一個(gè)巨型內(nèi)存對(duì)象,使用GlobalLock函數(shù)加鎖巨型內(nèi)存對(duì)象將返回一個(gè)巨型指針。分配一個(gè)128KB的巨型內(nèi)存對(duì)象,使用下面的代碼段:

HGLOBAL hGlobalObject;

char huge * hpGlobalObject;

if (hGlobalObject = GlobalAlloc(GMEM_MOVEABLE, 0x20000L)) {

         if (hpGlobalObject = (char huge *)GlobalLock(hGlobalObject)) {

                   /* Use hpGlobalObject as the far address of the globally allocated object. */

                   ...

                   GlobalUnlock (hGlobalObject);

         }

         else {

                   /* The lock failed. React accordingly. */

         }

}

else {

         /* The 128K cannot be allocated. React accordingly. */

}

       巨型內(nèi)存對(duì)象的修改有一點(diǎn)特殊性,當(dāng)對(duì)象大小增加并超過(guò)64K的倍數(shù)時(shí),Windows可能要為重新分配的內(nèi)存對(duì)象返回一個(gè)新的全局句柄,因此,巨型內(nèi)存對(duì)象的修改應(yīng)采用下面的形式:

if (hTempHugeObject = GlobalReAlloc(hHugeObject,0x20000L,GMEM_MOVEABLE)){

         hHugeObject = hTempObject;

}

else {

         /* The object could not be Reallocated. React accordingly. */

}

6.1.4 

       Windows采用段的概念來(lái)管理應(yīng)用程序的內(nèi)存,段有代碼段和數(shù)據(jù)段兩種,一個(gè)應(yīng)用程序可有多個(gè)代碼段和數(shù)據(jù)段。代碼段和數(shù)據(jù)段的數(shù)量決定了應(yīng)用程序的內(nèi)存模式,圖6.2說(shuō)明了內(nèi)存模式與應(yīng)用程序代碼段和數(shù)據(jù)段的關(guān)系。

 

 

代碼段數(shù)

單段

多段

數(shù)據(jù)段數(shù)

單段

小內(nèi)存模式

中內(nèi)存模式

多段

壓縮內(nèi)存模式

大內(nèi)存模式

6.2 內(nèi)存模式圖

 

       段的管理和全局內(nèi)存對(duì)象的管理很類(lèi)似,段可以是固定的,可移動(dòng)的和可刪除的,其屬性在應(yīng)用程序的模塊定義文件中指定。段在全局內(nèi)存中分配空間,Windows鼓勵(lì)使用可移動(dòng)的代碼段和數(shù)據(jù)段,這樣可以提高其內(nèi)存利用效率。使用可刪除的代碼段可以進(jìn)一步減小應(yīng)用程序?qū)?nèi)存的影響,如果代碼段是可刪除的,在必要時(shí)Windows將其刪除以滿(mǎn)足對(duì)全局內(nèi)存的請(qǐng)求。被刪除的段由Windows監(jiān)控,當(dāng)應(yīng)用程序利用該代碼段時(shí),Windows自動(dòng)地將它們重新裝入。

6.1.4.1 代碼段

       代碼段是不超過(guò)64K字節(jié)的機(jī)器指令,它代表全部或部分應(yīng)用程序指令。代碼段中的數(shù)據(jù)是只讀的,對(duì)代碼段執(zhí)行寫(xiě)操作將引起通用保護(hù)(GP)錯(cuò)誤。

       每個(gè)應(yīng)用程序都至少有一個(gè)代碼段,例如我們前面幾章的例子都只有一個(gè)代碼段。用戶(hù)也可以生成有多個(gè)代碼段的應(yīng)用。實(shí)際上,多數(shù)Windows應(yīng)用程序都有多個(gè)代碼段。通過(guò)使用多代碼段,用戶(hù)可以把任何給定代碼段的大小減少到完成某些任務(wù)所必須的幾條指令。這樣,可通過(guò)使某些段可刪除,來(lái)優(yōu)化應(yīng)用程序?qū)?nèi)存的使用。

       中模式和大模式的應(yīng)用程序都使用多代碼段,這些應(yīng)用程序的每一個(gè)段都有一個(gè)或幾個(gè)源文件。對(duì)于多個(gè)源文件,將它們分開(kāi)各自編譯,為編譯過(guò)的代碼所屬的每個(gè)段命名,然后連接。段的屬性在模塊定義文件中定義,Windows使用SEGMENTS語(yǔ)句來(lái)完成此任務(wù),如下面的代碼定義了四個(gè)段的屬性:

SEGMENTS

MEMORY_MAIN           PRELOAD    MOVEABLE

MEMORY_INIT              LOADONCALL MOVEABLE DISCARDABLE

MEMORY_WNDPROC PRELOAD    MOVEABLE

MEMORY_ABOUT         LOADONCALL MOVEABLE DISCARDABLE

       用戶(hù)也可以在模塊定義文件中用CODE語(yǔ)句為所有未顯式定義過(guò)的代碼段定義缺省屬性。例如,要將未列在SEGMENTS語(yǔ)句中的所有段定義為可刪除的,可用下面的語(yǔ)句:

CODE MOVEABLE DISCARDABLE

6.1.4.2 數(shù)據(jù)段

       每個(gè)應(yīng)用程序都有一個(gè)數(shù)據(jù)段,數(shù)據(jù)段包含應(yīng)用程序的堆棧、局部堆、靜態(tài)數(shù)據(jù)和全局?jǐn)?shù)據(jù)。一個(gè)數(shù)據(jù)段的長(zhǎng)度也不能超過(guò)64K。數(shù)據(jù)段可以是固定的或可移動(dòng)的,但不能是可刪除的。如果數(shù)據(jù)段是可移動(dòng)的,Windows在將控制轉(zhuǎn)向應(yīng)用程序前自動(dòng)為其加鎖,當(dāng)應(yīng)用程序分配全局內(nèi)存,或試圖在局部堆中分配超過(guò)當(dāng)前可分的內(nèi)存時(shí),可移動(dòng)數(shù)據(jù)段可能被移動(dòng),因此在數(shù)據(jù)段中不要保留指向變量的長(zhǎng)指針,當(dāng)數(shù)據(jù)段移動(dòng)時(shí),此長(zhǎng)指針將失效。

       在模塊定義文件中用DATA語(yǔ)句定義數(shù)據(jù)段的屬性,屬性的缺省值為MOVEABLEMULTIPLEMULTIPLE屬性使Windows為應(yīng)用程序的每一個(gè)實(shí)例拷貝一個(gè)應(yīng)用程序數(shù)據(jù)段,這就是說(shuō)每個(gè)應(yīng)用程序?qū)嵗袛?shù)據(jù)段的內(nèi)容都是不同的。

6.1.5 內(nèi)存管理程序示例Memory

       應(yīng)用程序Memory示例了部分內(nèi)存管理,它是一個(gè)使用了可刪除代碼段的中模式Windows應(yīng)用程序。Memory程序有四個(gè)C語(yǔ)言源程序,在模塊定義文件中顯示定義了四個(gè)代碼段,相應(yīng)地模塊定義文件和makefile文件有地些修改,讀者可通過(guò)比較Memory程序和5.1.2節(jié)的例子來(lái)體會(huì)它們之間的不同。另外,讀者在編譯和連接應(yīng)用程序Memory后,可用Visual C++提供的Windows Heap Walker (HEAPWALK.EXE)來(lái)觀察Memory運(yùn)行時(shí)的各個(gè)段。

 

//模塊1MEMORY_MAIN

#include "windows.h"

#include "memory.h"

HANDLE hInst;

/****************************************************************************

    MODULE: memory1.c

    FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)

    PURPOSE: calls initialization function, processes message loop

****************************************************************************/

int PASCAL WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)

HANDLE hInstance;

HANDLE hPrevInstance;

LPSTR lpCmdLine;

int nCmdShow;

{

    MSG msg;

 

    if (!hPrevInstance)

                   if (!InitApplication(hInstance))

                       return (FALSE);

    if (!InitInstance(hInstance, nCmdShow))

        return (FALSE);

    while (GetMessage(&msg, NULL, NULL, NULL)) {

                   TranslateMessage(&msg);

                   DispatchMessage(&msg);

    }

    return (msg.wParam);

}

 

//模塊2MEMORY_INIT

#include "windows.h"

#include "memory.h"

/****************************************************************************

    MODULE: memory2.c

    FUNCTION: InitApplication(HANDLE)

    PURPOSE: Initializes window data and registers window class

****************************************************************************/

BOOL InitApplication(hInstance)

HANDLE hInstance;

{

    WNDCLASS wc;

 

    wc.style = NULL;

    wc.lpfnWndProc = MainWndProc;

    wc.cbClsExtra = 0;

    wc.cbWndExtra = 0;

    wc.hInstance = hInstance;

    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

    wc.hCursor = LoadCursor(NULL, IDC_ARROW);

    wc.hbrBackground = COLOR_WINDOW+1;

    wc.lpszMenuName = "MemoryMenu";

    wc.lpszClassName = "MemoryWClass";

 

    return (RegisterClass(&wc));

}

 

/****************************************************************************

    MODULE: memory2.c

    FUNCTION: InitInstance(HANDLE, int)

    PURPOSE: Saves instance handle and creates main window

****************************************************************************/

BOOL InitInstance(hInstance, nCmdShow)

    HANDLE          hInstance;

    int             nCmdShow;

{

    HWND            hWnd;

 

    hInst = hInstance;

    hWnd = CreateWindow("MemoryWClass", "Memory Sample Application",

        WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,

        CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL );

 

         if (!hWnd)

        return (FALSE);

    ShowWindow(hWnd, nCmdShow);

    UpdateWindow(hWnd);

    return (TRUE);

}

 

//模塊3MEMORY_WNDPROC

#include "windows.h"

#include "memory.h"

/****************************************************************************

    MODULE: memory3.c

    FUNCTION: MainWndProc(HWND, UINT, WPARAM, LPARAM)

    PURPOSE: Processes messages

    MESSAGES:

                   WM_COMMAND    - application menu (About dialog box)

                   WM_DESTROY    - destroy window

****************************************************************************/

long FAR PASCAL __export MainWndProc(hWnd, message, wParam, lParam)

HWND hWnd;

UINT message;

WPARAM wParam;

LPARAM lParam;

{

    FARPROC lpProcAbout;

 

    switch (message) {

                   case WM_COMMAND:

                       if (wParam == IDM_ABOUT) {

                                     lpProcAbout = MakeProcInstance(About, hInst);

                                     DialogBox(hInst, "AboutBox", hWnd, lpProcAbout);

                                     FreeProcInstance(lpProcAbout);

                                     break;

                        }

                        else

                                     return (DefWindowProc(hWnd, message, wParam, lParam));

 

                   case WM_DESTROY:

                       PostQuitMessage(0);

             break;

 

                   default:

                       return (DefWindowProc(hWnd, message, wParam, lParam));

    }

   return (NULL);

}

 

//模塊4MEMORY_ABOUT

#include "windows.h"

#include "memory.h"

/****************************************************************************

    MODULE: memory4.c

    FUNCTION: About(HWND, unsigned, WORD, LONG)

    PURPOSE: Processes messages for "About" dialog box

    MESSAGES:

                   WM_INITDIALOG - initialize dialog box

                   WM_COMMAND    - Input received

****************************************************************************/

BOOL FAR PASCAL __export About(hDlg, message, wParam, lParam)

HWND hDlg;

unsigned message;

WORD wParam;

LONG lParam;

{

    switch (message) {

                   case WM_INITDIALOG:

                       return (TRUE);

                   case WM_COMMAND:

             if (wParam == IDOK || wParam == IDCANCEL) {

                                     EndDialog(hDlg, TRUE);

                                     return (TRUE);

             }

             break;

    }

    return (FALSE);

}

       下面是模塊定義文件中的一小段,它在編譯每個(gè)模塊時(shí),使用/NT選項(xiàng)為每個(gè)段進(jìn)行命名。

MEMORY1.OBJ:       MEMORY1.C $(MEMORY1_DEP)

         $(CC) $(CFLAGS) $(CCREATEPCHFLAG) /c /NT MEMORY_MAIN MEMORY1.C

MEMORY2.OBJ:       MEMORY2.C $(MEMORY2_DEP)

         $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c /NT MEMORY_INIT MEMORY2.C

MEMORY3.OBJ:       MEMORY3.C $(MEMORY3_DEP)

         $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c /NT MEMORY_WNDPROC MEMORY3.C

MEMORY4.OBJ:       MEMORY4.C $(MEMORY4_DEP)

         $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c /NT MEMORY_ABOUT MEMORY4.C

 

6.2 動(dòng)態(tài)連接庫(kù)DLL

       使用動(dòng)態(tài)連接庫(kù)是Windows的一個(gè)很重要的特點(diǎn),它使得多個(gè)Windows應(yīng)用程序可以共享函數(shù)代碼、數(shù)據(jù)和硬件,這可以大大提高Windows內(nèi)存的利用率。

       動(dòng)態(tài)連接庫(kù)是一個(gè)可執(zhí)行模塊,它包含的函數(shù)可以由Windows應(yīng)用程序調(diào)用執(zhí)行,為應(yīng)用程序提供服務(wù)。它和我們以前用的C函數(shù)庫(kù)相比,在功能上是很類(lèi)似的,其主要區(qū)別是動(dòng)態(tài)連接庫(kù)在運(yùn)行是連接,C函數(shù)庫(kù)(靜態(tài)連接庫(kù))是在生成可執(zhí)行文件時(shí)由連接器(LINK)連接。靜態(tài)連接庫(kù)中的代碼在應(yīng)用程序生成以后已經(jīng)連接到應(yīng)用程序模塊之中,但動(dòng)態(tài)連接庫(kù)中的代碼只有在應(yīng)用程序要用到該代碼段時(shí)才動(dòng)態(tài)調(diào)入DLL中的相應(yīng)代碼。為了讓?xiě)?yīng)用程序在執(zhí)行時(shí)能夠調(diào)入DLL中正確的代碼,Windows提供了動(dòng)態(tài)連接庫(kù)的引入庫(kù)。Windows在連接生成應(yīng)用程序時(shí),如果使用動(dòng)態(tài)連接庫(kù)函數(shù),連接器并不拷貝DLL中的任何代碼,它只是將引入庫(kù)中指定所需函數(shù)在DLL中位置的信息拷貝在應(yīng)用程序模塊中,當(dāng)應(yīng)用程序運(yùn)行時(shí),這些定位信息在可執(zhí)行應(yīng)用程序和動(dòng)態(tài)連接庫(kù)之間建立動(dòng)態(tài)連接。靜態(tài)庫(kù)、引入庫(kù)和動(dòng)態(tài)庫(kù)之間的區(qū)別如表6.1所示。

        

6.1 靜態(tài)庫(kù)、引入庫(kù)和動(dòng)態(tài)庫(kù)之間的區(qū)別

庫(kù)類(lèi)型

連接時(shí)間

范例庫(kù)

函數(shù)范例

說(shuō)明

靜態(tài)庫(kù)

連接時(shí)

MLIBCEW.LIB

strcpy

函數(shù)代碼

引入庫(kù)

連接時(shí)

LIBW.LIB

TextOut

定位信息

動(dòng)態(tài)庫(kù)

運(yùn)行時(shí)

GDI.EXE

TextOut

函數(shù)代碼

      

DLL不能獨(dú)立執(zhí)行,也不能使用消息循環(huán)。每個(gè)DLL都有一個(gè)入口點(diǎn)和一個(gè)出口點(diǎn),具有自己的實(shí)例句柄、數(shù)據(jù)段和局部堆,但DLL沒(méi)有堆棧,它使用調(diào)用程序的堆棧。DLL也包括有.C文件,.H文件,.RC文件和.DEF文件,另外,在連接時(shí)一般要加入SDK庫(kù)中的LIBENTRY.OBJ文件。

6.2.1 創(chuàng)建動(dòng)態(tài)連接庫(kù)

       要?jiǎng)?chuàng)建動(dòng)態(tài)連接庫(kù),至少有三個(gè)文件:

·         C語(yǔ)言源文件;

·         一個(gè)模塊定義文件(.DEF)

·         makefile文件。

       有了這些文件后,就可以運(yùn)行Microsoft的程序維護(hù)機(jī)制(NMAKE),編譯并連接源代碼文件,生成DLL文件。

6.2.1.1 創(chuàng)建C語(yǔ)言源文件

       和其它C應(yīng)用程序一樣,動(dòng)態(tài)連接庫(kù)可包含多個(gè)函數(shù),每個(gè)函數(shù)要在被其它應(yīng)用程序或庫(kù)使用之前用FAR聲明,并且在庫(kù)的模塊定義文件中用EXPORTS語(yǔ)句引出。下面給出一個(gè)完整的C語(yǔ)言源文件:

 

 

/****************************************************************************

    PROGRAM: Dlldraw.c

    PURPOSE: Contains library routines for drawing

*******************************************************************************/

#include "windows.h"        

#include "stdlib.h"

#include "dlldraw.h"

/****************************************************************************

   FUNCTION: LibMain(HANDLE, WORD, WORD, LPSTR)

   PURPOSE: Is called by LibEntry. LibEntry is called by Windows when the DLL is loaded.

   The LibEntry routine is provided in the LIBENTRY.OBJ in the SDK Link Libraries

   disk. (The source LIBENTRY.ASM is also provided.) 

   LibEntry initializes the DLL's heap, if a HEAPSIZE value is specified in the DLL's DEF file.

   Then LibEntry calls LibMain. The LibMain function below satisfies that call.

   The LibMain function should perform additional initialization tasks required by the DLL. 

   In this example, no initialization tasks are required. LibMain should return a value of 1

   if the initialization is successful.

*******************************************************************************/

int FAR PASCAL LibMain(hModule, wDataSeg, cbHeapSize, lpszCmdLine)

HANDLE hModule;

WORD    wDataSeg;

WORD    cbHeapSize;

LPSTR   lpszCmdLine;

{

    return 1;

}

 

/****************************************************************************

    FUNCTION: WEP(int)

         PURPOSE: Performs cleanup tasks when the DLL is unloaded. WEP() is called

automatically by Windows when the DLL is unloaded (no remaining tasks still have

the DLL loaded). It is strongly recommended that a DLL have a WEP() function,

even if it does nothing but returns success (1), as in this example.

*******************************************************************************/

int FAR PASCAL __export _WEP (bSystemExit)

int bSystemExit;

{

    return(1);

}

 

/****************************************************************************

    FUNCTION: RandRect(RECT *) - Get a Rand Rectangle position

****************************************************************************/

void RandRect(rc)

RECT FAR *rc;

{

    rc->top = rand() % 400;

    rc->left = rand() % 600;

    rc->bottom = rand() % 400;

    rc->right = rand() % 600;

}

 

/****************************************************************************

    FUNCTION: DrawBox(HWND, HPEN, HBRUSH) - Draw a Box

    PURPOSE: Draw a box with specified pen and brush.

****************************************************************************/

void FAR PASCAL __export DrawBox(hWnd, hPen, hBrush)

HWND hWnd;

HPEN hPen;

HBRUSH hBrush;

{

         HDC hDC;

    HPEN hOldPen;

    HBRUSH hOldBrush;

    RECT rc;

   

    RandRect((RECT FAR *)&rc);                                 

         hDC = GetDC(hWnd);

         hOldPen = SelectObject(hDC, hPen);

    hOldBrush = SelectObject(hDC, hBrush);

    Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);

         SelectObject(hDC, hOldPen);

         SelectObject(hDC, hOldBrush);

         ReleaseDC(hWnd, hDC);

}

 

/****************************************************************************

    FUNCTION: DrawCircle(HWND, HPEN, HBRUSH) - Draw a Circle

    PURPOSE: Draw a circle with specified pen.

****************************************************************************/

void FAR PASCAL __export DrawCircle(hWnd, hPen, hBrush)

HWND hWnd;

HPEN hPen;

HBRUSH hBrush;

{

         HDC hDC;

    HPEN hOldPen;

    RECT rc;

   

    RandRect((RECT FAR *)&rc);                                 

         hDC = GetDC(hWnd);

         hOldPen = SelectObject(hDC, hPen);

         Arc(hDC, rc.left, rc.top, rc.right, rc.bottom, rc.left, rc.top, rc.left, rc.top);

         SelectObject(hDC, hOldPen);

         ReleaseDC(hWnd, hDC);

}

 

/****************************************************************************

    FUNCTION: DrawPie(HWND, HPEN, HBRUSH) - Draw a pie

    PURPOSE: Draw a pie with specified pen and brush.

****************************************************************************/

void FAR PASCAL __export DrawPie(hWnd, hPen, hBrush)

HWND hWnd;

HPEN hPen;

HBRUSH hBrush;

{

         HDC hDC;

    HPEN hOldPen;

    HBRUSH hOldBrush;

    RECT rc;

   

    RandRect((RECT FAR *)&rc);                                 

         hDC = GetDC(hWnd);

         hOldPen = SelectObject(hDC, hPen);

         hOldBrush = SelectObject(hDC, hBrush);

         Pie(hDC, rc.left, rc.top, rc.right, rc.bottom, rc.left, rc.top, rc.right, rc.top);

         SelectObject(hDC, hOldPen);

         SelectObject(hDC, hOldBrush);

         ReleaseDC(hWnd, hDC);

}

       在上面的源代碼中,有兩個(gè)函數(shù)是DLL源代碼所必需的,這就是DLL入口函數(shù)LibMain和出口函數(shù)WEP

       LibMain函數(shù)是DLL的入口點(diǎn),它由DLL 自動(dòng)初始化函數(shù)LibEntry調(diào)用,主要用來(lái)完成一些初始化任務(wù)。LibMain有四個(gè)參數(shù):hint, wDataSeg, cbHeapSizelpszCmdLine。其中hInst是動(dòng)態(tài)連接庫(kù)的實(shí)例句柄;wDataSeg是數(shù)據(jù)段(DS)寄存器的值;cbHeapSize是模塊定義文件定義的堆的尺寸,LibEntry函數(shù)用該值來(lái)初始化局部堆;lpszCmdLine包含命令行的信息。

       WEP函數(shù)是DLL的標(biāo)準(zhǔn)出口函數(shù),它在DLL被卸出之前由Windows調(diào)用執(zhí)行,以完成一些必要的清除工作。WEP函數(shù)只使用一個(gè)參數(shù)nParameter,它用來(lái)指示終止?fàn)顟B(tài)。

       源文件中的其它函數(shù)則是DLL為應(yīng)用程序提供的庫(kù)函數(shù),DLL設(shè)計(jì)者可以給它加入自己所需要的功能,如DrawBoxDrawPieDrawCircle

6.2.1.2 建立DLL模塊定義文件

       每個(gè)DLL必須有一個(gè)模塊定義文件,該文件在使用LINK連接時(shí)用于提供定義庫(kù)屬性的引入信息。下面給出一個(gè)簡(jiǎn)單的模塊定義文件實(shí)例:

LIBRARY   DLLDRAW

EXETYPE   WINDOWS

CODE         PRELOAD MOVEABLE DISCARDABLE

DATA        PRELOAD SINGLE

HEAPSIZE 1024

EXPORTS

      WEP            @1 RESIDENTNAME

    DrawBox      @2

         DrawCircle                 @3

         DrawPie              @4

       關(guān)鍵字LIBRARY用來(lái)標(biāo)識(shí)這個(gè)模塊是一個(gè)動(dòng)態(tài)連接庫(kù),其后是庫(kù)名DRAWDLL,它必須和動(dòng)態(tài)連接庫(kù)文件名相同。

       DATA語(yǔ)句中關(guān)鍵字SINGLE是必須的,它表明無(wú)論應(yīng)用程序訪問(wèn)DLL多少次,DLL均只有單個(gè)數(shù)據(jù)段。

       其它關(guān)鍵字的用法同Windows應(yīng)用程序的模塊定義文件一樣,這在前面已有敘述,請(qǐng)參見(jiàn)5.1.2.3

6.2.1.3 編制Makefile文件

       NMAKEMicrosoft的程序維護(hù)機(jī)制,它控制執(zhí)行文件的創(chuàng)建工作,以保證只有必要的操作被執(zhí)行。有五種工具用來(lái)創(chuàng)建動(dòng)態(tài)連接庫(kù):

CL

Microsoft C優(yōu)化編譯器,它將C語(yǔ)言源文件編譯成目標(biāo)文件.OBJ

LINK

Microsoft 分段可執(zhí)行連接器,它將目標(biāo)文件和靜態(tài)庫(kù)連接生成動(dòng)態(tài)連接庫(kù)。LINK命令行有五個(gè)參數(shù),用逗號(hào)分開(kāi):第一個(gè)參數(shù)列出所有動(dòng)態(tài)連接庫(kù)用到的目標(biāo)文件(.OBJ),如果使用了標(biāo)準(zhǔn)動(dòng)態(tài)連接初始化函數(shù),則必須包括LIBENTRY.OBJ文件;第二個(gè)參數(shù)指示最終可執(zhí)行文件名,一般用.DLL作為擴(kuò)展名;第三個(gè)參數(shù)列出創(chuàng)建動(dòng)態(tài)連接庫(kù)所需要的引入庫(kù)和靜態(tài)庫(kù);第五個(gè)參數(shù)是模塊定義文件。

IMPLIB

Microsoft引入庫(kù)管理器,它根據(jù)動(dòng)態(tài)連接庫(kù)的模塊定義文件創(chuàng)建一個(gè)擴(kuò)展名為.LIB的引入庫(kù)。

RC

Microsoft Windows資源編譯器。所有動(dòng)態(tài)連接庫(kù)都必須用RC編譯,以使它們與Windows 3.1版兼容。

MAPSYM

Microsoft符號(hào)文件生成器,它是可選工具,只用于調(diào)試版本。

下面給出一個(gè)makefile文件的實(shí)例:

# Microsoft Visual C++ generated build script - Do not modify

PROJ = DLLDRAW

DEBUG = 1

PROGTYPE = 1

CALLER =

ARGS =

DLLS =

D_RCDEFINES =

R_RCDEFINES =

ORIGIN = MSVC

ORIGIN_VER = 1.00

PROJPATH = D:\JDX\WINSAMP\DLLDRAW\

USEMFC = 0

CC = cl

CPP = cl

CXX = cl

CCREATEPCHFLAG =

CPPCREATEPCHFLAG =

CUSEPCHFLAG =

CPPUSEPCHFLAG =

FIRSTC = SELECT.C   

FIRSTCPP =            

RC = rc

 

CFLAGS_D_WDLL = /nologo /YX /Zp1 /Od /D "_DEBUG" /D "_WINDOWS" /D "_WINDLL" /G2 /W3 /ASw /FR /GD /Zi

CFLAGS_R_WDLL = /nologo /YX /Zp1 /Ox /D "NDEBUG" /D "_WINDOWS" /D "_WINDLL" /G2 /W3 /ASw /FR /GD /Gs

LFLAGS_D_WDLL = /NOLOGO /NOD /ONERROR:NOEXE /ALIGN:16 /CO

LFLAGS_R_WDLL = /NOLOGO /NOD /ONERROR:NOEXE /ALIGN:16

LIBS_D_WDLL = oldnames libw commdlg shell olecli olesvr sdllcew LIBW

LIBS_R_WDLL = oldnames libw commdlg shell olecli olesvr sdllcew LIBW

RCFLAGS = /NOLOGO 

<span l

posted on 2009-03-12 10:21 Sandy 閱讀(403) 評(píng)論(0)  編輯 收藏 引用 所屬分類(lèi): Window Mobile
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            亚洲午夜羞羞片| 亚洲国内精品在线| 欧美大片va欧美在线播放| 亚洲观看高清完整版在线观看| 日韩视频免费| 午夜精品久久久久久久男人的天堂 | 欧美裸体一区二区三区| 国产精品黄色在线观看| 国产一区二区三区精品欧美日韩一区二区三区 | 国产精品v欧美精品v日本精品动漫| 国产欧美精品在线播放| 亚洲激情视频在线| 亚洲欧美乱综合| 欧美福利影院| 亚洲欧美成人精品| 免费国产自线拍一欧美视频| 国产精品videosex极品| 亚洲福利av| 欧美一区二区| 亚洲国产三级| 欧美永久精品| 欧美视频三区在线播放| 在线看欧美日韩| 午夜精品久久久久久99热软件 | 美女精品在线观看| 一本一本久久| 美女爽到呻吟久久久久| 国产精品资源| 99re6这里只有精品视频在线观看| 久久大逼视频| 亚洲美女电影在线| 久久久水蜜桃av免费网站| 欧美新色视频| 亚洲激情影视| 久久久欧美精品| 亚洲天堂激情| 欧美日韩国产综合网| 在线观看视频亚洲| 欧美一区二区三区在线观看| 亚洲精选大片| 男同欧美伦乱| 欲香欲色天天天综合和网| 欧美一区三区二区在线观看| 99国产精品视频免费观看一公开 | 美玉足脚交一区二区三区图片| 国产日本欧美一区二区三区| 亚洲天堂av在线免费| 亚洲国产另类久久久精品极度| 久久精视频免费在线久久完整在线看| 国产精品视频1区| 亚洲视频在线一区观看| 亚洲激情不卡| 免费亚洲电影在线观看| 一区二区在线观看视频在线观看| 欧美成人精品在线观看| 欧美成人嫩草网站| 欧美国产日韩xxxxx| 久久久亚洲影院你懂的| 久久全球大尺度高清视频| 久久精品论坛| 午夜欧美精品| 亚洲视频久久| 一区二区电影免费观看| 欧美日韩一区二区三区在线观看免 | 亚洲高清资源| 午夜精品一区二区三区在线视| 亚洲国产日韩欧美在线动漫| 亚洲欧美日韩直播| 国产精品av免费在线观看| 欧美日韩免费高清| 国产伦精品一区二区三区高清| 黄色在线一区| 亚洲一区二区三区高清| 久久另类ts人妖一区二区| 欧美一区=区| 久久五月婷婷丁香社区| 欧美精品一区二区三区高清aⅴ| 欧美激情国产精品| 欧美日韩精品一区二区天天拍小说 | 亚洲黄色天堂| 亚洲在线第一页| 欧美不卡三区| 国内精品视频久久| 亚洲欧美99| 一区二区三区高清不卡| 男女激情视频一区| 狠狠色丁香久久综合频道| 亚洲综合精品一区二区| 亚洲精品1区2区| 欧美二区在线| 国内精品久久久久久久影视蜜臀| 亚洲一区二区三区在线看| 亚洲精选一区| 欧美精品一区二区视频| 91久久在线观看| 一本色道久久88亚洲综合88| 99在线热播精品免费| 久久久精品日韩欧美| 午夜亚洲福利| 亚洲欧洲日产国码二区| 亚洲欧美日韩精品在线| 欧美日韩在线第一页| 亚洲日韩视频| 91久久久在线| 国产女同一区二区| 亚洲欧美清纯在线制服| 欧美一区二区三区电影在线观看| 欧美xxx在线观看| 亚洲一区激情| 欧美午夜影院| 国产精品99久久久久久有的能看| 久久久免费av| 欧美一区久久| 亚洲国产精品欧美一二99| 亚洲激情视频在线播放| 欧美日韩在线影院| 欧美亚洲视频一区二区| 久久成人精品视频| 亚洲黄一区二区| 亚洲先锋成人| 黄色欧美日韩| 宅男噜噜噜66一区二区| 国产私拍一区| 亚洲国产精品久久人人爱蜜臀 | 欧美激情一区二区| 国产精品二区在线观看| 久久婷婷蜜乳一本欲蜜臀| 欧美mv日韩mv国产网站| 欧美在线观看网站| 欧美成人蜜桃| 久久视频在线免费观看| 欧美激情亚洲综合一区| 欧美一区二区三区在线| 欧美v国产在线一区二区三区| 亚洲视屏一区| 欧美成人乱码一区二区三区| 欧美一区激情视频在线观看| 久久在线视频在线| 欧美亚洲在线播放| 欧美日韩性生活视频| 欧美激情导航| 韩日精品在线| 亚洲性av在线| 亚洲欧美日韩一区二区三区在线| 免费久久99精品国产自在现线| 欧美自拍偷拍午夜视频| 国产精品高潮呻吟久久av黑人| 亚洲国产精品精华液2区45| 国产无一区二区| 亚洲欧美在线磁力| 欧美中文在线观看| 国产精品永久在线| 亚洲一区在线看| 午夜性色一区二区三区免费视频| 欧美午夜精品久久久久久人妖| 亚洲人成网站精品片在线观看| 亚洲国产精品电影在线观看| 久久综合网络一区二区| 久久阴道视频| 亚洲欧洲日韩在线| 欧美18av| 99av国产精品欲麻豆| 中文国产亚洲喷潮| 国产精品久久久久毛片软件| 在线视频精品一区| 久久av最新网址| 在线观看视频日韩| 久久免费高清视频| 久久综合给合久久狠狠色| 影音先锋成人资源站| 欧美成人一品| 宅男精品导航| 久久综合一区二区| 亚洲人成7777| 欧美性视频网站| 欧美在线亚洲一区| 亚洲第一精品影视| 亚洲综合久久久久| 国产亚洲精品成人av久久ww| 亚洲精品日韩欧美| 在线视频亚洲欧美| 激情久久久久久| 欧美体内谢she精2性欧美| 欧美亚洲视频一区二区| 亚洲三级毛片| 老司机67194精品线观看| 亚洲一区二区av电影| 在线播放豆国产99亚洲| 国产精品丝袜xxxxxxx| 欧美ed2k| 久久精品在线| 亚洲一区二区欧美日韩| 亚洲国产成人av在线| 久久在线91| 欧美在线观看视频一区二区三区 | 一本久久综合| 亚洲国产小视频| 好吊色欧美一区二区三区视频| 欧美午夜美女看片| 欧美精品乱人伦久久久久久|