日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Windbg内核调试(大杂烩)

發(fā)布時間:2023/12/18 编程问答 54 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Windbg内核调试(大杂烩) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Windbg內(nèi)核調(diào)試之三: 調(diào)試驅(qū)動


這次我們通過一個實(shí)際調(diào)試驅(qū)動的例子,來逐步體會Windbg在內(nèi)核調(diào)試中的作用.
由于條件所限,大多數(shù)情況下,很多人都是用VMware+Windbg調(diào)試內(nèi)核(VMware的確是個好東西).但這樣的調(diào)試需要占用大量的系統(tǒng)資源,對于和我一樣急性子的朋友來說這是不可接受的:).利用雙機(jī)調(diào)試就可以讓你一邊喝咖啡一邊輕松的看結(jié)果,而不至于郁悶的等待每次長達(dá)數(shù)分鐘的系統(tǒng)響應(yīng).有關(guān)雙機(jī)調(diào)試的基本設(shè)置,請參考:

本次調(diào)試驅(qū)動所構(gòu)建的環(huán)境如下:

host computer: WinXP+Windbg
Target computer:? Vista SP1
driver object: syscow
connect setting: 1394數(shù)據(jù)線

說明: 1.1394卡在很多機(jī)器上都已經(jīng)沒有了,Vista也取消了1394的數(shù)據(jù)連接協(xié)議(調(diào)試還是可以的),但不可否認(rèn)的是利用1394數(shù)據(jù)線連接調(diào)試要比COM口和USB速率快很多(為什么好用的東西卻得不到支持!).2.本次調(diào)試的driver是公司開發(fā)的某個軟件的驅(qū)動程序,拿來嘗試在Vista SP1下track.由于涉及到商業(yè)機(jī)密,本驅(qū)動源代碼不便公開.3.Vista SP1就沒什么好說的了,前些天才發(fā)布,MS又一個失敗的典型.

OK,Let's go!
該驅(qū)動是一個類型sr.sys(MS的System Restore驅(qū)動)的Filter Driver,屬于文件系統(tǒng)過濾驅(qū)動,加載在文件系統(tǒng)驅(qū)動上層,由Filter Manager負(fù)責(zé)與用戶層和底層通信.連接到目標(biāo)機(jī)后,按下Ctrl+break中斷當(dāng)前狀態(tài).(注:你也可以進(jìn)入到explorer之后再中斷,為了了解驅(qū)動加載時的進(jìn)入點(diǎn),以及系統(tǒng)啟動時內(nèi)核的裝態(tài),我們中斷到這里)

Microsoft (R) Windows Debugger? Version 6.6.0007.5
Copyright (c) Microsoft Corporation. All rights reserved.

Using 1394 for debugging
Opened //./DBG1394_INSTANCE01
Waiting to reconnect...
Connected to Windows Vista 6000 x86 compatible target, ptr64 FALSE
Kernel Debugger connection established.
Symbol search path is: D:/symbolslocal; D:/IR/SystemOK/Restore/Driver/objchk_wlh_x86/i386
Executable search path is:
*** ERROR: Symbol file could not be found.? Defaulted to export symbols for ntkrnlmp.exe -
Windows Vista Kernel Version 6000 MP (1 procs) Free x86 compatible
Built by: 6000.16584.x86fre.vista_gdr.071023-1545
Kernel base = 0x81800000 PsLoadedModuleList = 0x81908ad0
System Uptime: not available
WARNING: Whitespace at start of path element
WARNING: Whitespace at start of path element
Break instruction exception - code 80000003 (first chance)
*******************************************************************************
*??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
*?? You are seeing this message because you pressed either????????????????????????????????????????????????????????
*?????? CTRL+C (if you run kd.exe) or,???????????????????????????????????????
*?????? CTRL+BREAK (if you run WinDBG),?????????????????????????????????????
*?? on your debugger machine's keyboard.?????????????????????????????????????
*????????????????????????????????????????????????????????????????????????????
*?????????????????? THIS IS NOT A BUG OR A SYSTEM CRASH??????????????????????
*????????????????????????????????????????????????????????????????????????????
* If you did not intend to break into the debugger, press the "g" key, then??
* press the "Enter" key now.? This message might immediately reappear.? If it
* does, press "g" and "Enter" again.?????????????????????????????????????????
*????????????????????????????????????????????????????????????????????????????
*******************************************************************************
nt!RtlpBreakWithStatusInstruction:
818355e8 cc????????????? int???? 3

然后,在Command line里鍵入lm,查看當(dāng)前系統(tǒng)加載的模塊和驅(qū)動(會發(fā)現(xiàn)我們的driver列在其中):

kd> lm
start??? end??????? module name
80404000 80412000?? PCIIDEX??? (deferred)????????????
80412000 80419000?? intelide?? (deferred)????????????
80419000 80429000?? mountmgr?? (deferred)????????????
80429000 80438000?? volmgr???? (deferred)????????????
80438000 8045d000?? pci??????? (deferred)????????????
8045d000 80465000?? msisadrv?? (deferred)????????????
80465000 8046e000?? WMILIB???? (deferred)????????????
8046e000 804b1000?? acpi?????? (deferred)????????????
804b1000 804be000?? WDFLDR???? (deferred)????????????
804be000 80539000?? Wdf01000?? (deferred)????????????
80539000 8061a000?? CI???????? (deferred)????????????
8061a000 80655000?? CLFS?????? (deferred)????????????
80655000 8065d000?? BOOTVID??? (pdb symbols)?????????
8065d000 80666000?? PSHED????? (deferred)????????????
80666000 806c6000?? mcupdate_GenuineIntel?? (deferred)????????????
806c6000 806ce000?? kdcom????? (deferred)????????????
81800000 81b95000?? nt???????? (pdb symbols)?????????
81b95000 81bc9000?? hal??????? (pdb symbols)????????
81c06000 81c0e000?? spldr????? (deferred)????????????
81c0e000 81c44000?? volsnap??? (deferred)????????????
81c44000 81cae000?? ksecdd???? (deferred)????????????
81cae000 81db6000?? Ntfs?????? (deferred)????????????
81db6000 81def000?? NETIO????? (deferred)????????????
81def000 81e1a000?? msrpc????? (deferred)????????????
81e1a000 81f1e000?? ndis?????? (deferred)????????????
81f1e000 81f270c0?? PxHelp20?? (deferred)????????????
81f28000 81f4f000?? syscow32v?? (private pdb symbols)?
81f4f000 81f5f000?? fileinfo?? (deferred)????????????
81f5f000 81f90000?? fltmgr???? (deferred)????????????
81f90000 81fae000?? ataport??? (deferred)????????????
81fae000 81fb6000?? atapi????? (deferred)????????????
81fb6000 82000000?? volmgrx??? (deferred)????????????
8234f000 82358000?? crcdisk??? (deferred)????????????
82358000 82368000?? agp440???? (deferred)????????????
82368000 82389000?? CLASSPNP?? (deferred)????????????
82389000 8239a000?? disk?????? (deferred)????????????
8239a000 823bd000?? fvevol???? (deferred)????????????
823bd000 823e2000?? ecache???? (deferred)????????????
823e2000 823f1000?? mup??????? (deferred)????????????
823f1000 82400000?? partmgr??? (deferred)??

注: 若符號文件沒有加載成功,Windbg會提示響應(yīng)的符號找不到,不過一般Windbg會自己尋找符號文件路徑.實(shí)在找不到時,就包含
srv*c:/symbols*http://msdl.microsoft.com/download/symbols, 然后reload一下(!reload).

另外,鍵入lm t n, 我們可以查看更為詳細(xì)的模塊及驅(qū)動信息.
然后,鍵入!thread和Kp,查看當(dāng)前的線程詳細(xì)信息和堆棧(或者Alt+6也可以看stack).注意當(dāng)前thread的ID:

kd> !thread
THREAD 84254ae8? Cid 0004.0008? Teb: 00000000 Win32Thread: 00000000 RUNNING on processor 0
Not impersonating
Owning Process??????????? 84254d90?????? Image:???????? System
Wait Start TickCount????? 0????????????? Ticks: 1 (0:00:00:00.015)
Context Switch Count????? 1????????????
UserTime????????????????? 00:00:00.0000
KernelTime??????????????? 00:00:00.0015
Win32 Start Address nt!Phase1Initialization (0x819433ae)
Stack Init 81c06000 Current 81c05db8 Base 81c06000 Limit 81c03000 Call 0
Priority 31 BasePriority 8 PriorityDecrement 0
ChildEBP RetAddr? Args to Child?????????????
81c05af0 818aa92c 00000001 81867999 0002625a nt!RtlpBreakWithStatusInstruction (FPO: [1,0,0])
81c05af8 81867999 0002625a 00000000 00000001 nt!KdCheckForDebugBreak+0x22 (FPO: [0,0,0])
81c05b18 81836cfd 81928100 000000d1 81c05b9c nt!KeUpdateRunTime+0x270
81c05b18 81ba4130 81928100 000000d1 81c05b9c nt!KeUpdateSystemTime+0xed (FPO: [0,2] TrapFrame @ 81c05b28)
81c05b9c 81ba3fd0 81bb28a0 8181dced 81c05bc8 hal!XmGetCodeByte+0x30 (FPO: [Non-Fpo])
81c05bac 81ba40c5 81bb28a0 0000c000 00001da4 hal!XmEmulateStream+0x88 (FPO: [Non-Fpo])
81c05bc8 81ba374d 00000010 81c05c0c 8181dced hal!XmEmulateInterrupt+0x80 (FPO: [Non-Fpo])
81c05bdc 81ba0a1c 00000010 81c05c0c 00000000 hal!x86BiosExecuteInterruptShadowed+0x43 (FPO: [Non-Fpo])
81c05bf8 81ba0a5b 00000010 81c05c0c 00000000 hal!x86BiosCall+0x22 (FPO: [Non-Fpo])
81c05c2c 80656697 80806ae0 8080f438 00000000 hal!HalpBiosDisplayReset+0x25 (FPO: [Non-Fpo])
81c05c58 81b2cd6d 00000001 81b0ab01 80806ae0 BOOTVID!VidInitialize+0x135 (FPO: [Non-Fpo])
81c05c7c 81b3f098 00000001 80806ae0 00000007 nt!InbvDriverInitialize+0x81
81c05d74 819433bb 81c05dc0 819afbad 80806ae0 nt!Phase1InitializationDiscard+0xd0
81c05d7c 819afbad 80806ae0 81c0e680 00000000 nt!Phase1Initialization+0xd
81c05dc0 8189a346 819433ae 80806ae0 00000000 nt!PspSystemThreadStartup+0x9d
00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16

kd> kp
ChildEBP RetAddr?
81c05af0 818aa92c nt!RtlpBreakWithStatusInstruction
81c05af8 81867999 nt!KdCheckForDebugBreak+0x22
81c05b18 81836cfd nt!KeUpdateRunTime+0x270
81c05b18 81ba4130 nt!KeUpdateSystemTime+0xed
81c05b9c 81ba3fd0 hal!XmGetCodeByte+0x30
81c05bac 81ba40c5 hal!XmEmulateStream+0x88
81c05bc8 81ba374d hal!XmEmulateInterrupt+0x80
81c05bdc 81ba0a1c hal!x86BiosExecuteInterruptShadowed+0x43
81c05bf8 81ba0a5b hal!x86BiosCall+0x22
81c05c2c 80656697 hal!HalpBiosDisplayReset+0x25
81c05c58 81b2cd6d BOOTVID!VidInitialize+0x135
81c05c7c 81b3f098 nt!InbvDriverInitialize+0x81
81c05d74 819433bb nt!Phase1InitializationDiscard+0xd0
81c05d7c 819afbad nt!Phase1Initialization+0xd
81c05dc0 8189a346 nt!PspSystemThreadStartup+0x9d
00000000 00000000 nt!KiThreadStartup+0x16

鍵入!process [PID] 0, 查到當(dāng)前進(jìn)程:

kd> !process 0004.0008 0
PROCESS 84254d90? SessionId: none? Cid: 0004??? Peb: 00000000? ParentCid: 0000
??? DirBase: 00122000? ObjectTable: 830001d0? HandleCount:?? 1.
??? Image: System
??? VadRoot 00000000 Vads 0 Clone 0 Private 0. Modified 0. Locked 0.
??? DeviceMap 00000000
??? Token???????????????????????????? 83003830
??? ElapsedTime?????????????????????? 00:00:00.015
??? UserTime????????????????????????? 00:00:00.000
??? KernelTime??????????????????????? 00:00:00.000
??? QuotaPoolUsage[PagedPool]???????? 0
??? QuotaPoolUsage[NonPagedPool]????? 0
??? Working Set Sizes (now,min,max)? (4, 0, 0) (16KB, 0KB, 0KB)
??? PeakWorkingSetSize??????????????? 0
??? VirtualSize?????????????????????? 0 Mb
??? PeakVirtualSize?????????????????? 0 Mb
??? PageFaultCount??????????????????? 0
??? MemoryPriority??????????????????? BACKGROUND
??? BasePriority????????????????????? 8
??? CommitCharge????????????????????? 0

??????? THREAD 84254ae8? Cid 0004.0008? Teb: 00000000 Win32Thread: 00000000 RUNNING on processor 0

在lm命令列出的信息中,start是模塊的起始地址,通過鍵入"u 驅(qū)動起始地址",我們可以反匯編出它的代碼:

kd> u 81f28000
syscow32v!SysCowAllocatePostCopyWorkItem <PERF> (syscow32v+0x0):
81f28000 4d????????????? dec???? ebp
syscow32v!SysCowAllocatePostCopyWorkItem <PERF> (syscow32v+0x1):
81f28001 5a????????????? pop???? edx
syscow32v!SysCowAllocatePostCopyWorkItem <PERF> (syscow32v+0x2):
81f28002 90????????????? nop
syscow32v!SysCowAllocatePostCopyWorkItem <PERF> (syscow32v+0x3):
81f28003 0003??????????? add???? byte ptr [ebx],al
syscow32v!SysCowAllocatePostCopyWorkItem <PERF> (syscow32v+0x5):
81f28005 0000??????????? add???? byte ptr [eax],al
syscow32v!SysCowAllocatePostCopyWorkItem <PERF> (syscow32v+0x7):
81f28007 000400????????? add???? byte ptr [eax+eax],al
syscow32v!SysCowAllocatePostCopyWorkItem <PERF> (syscow32v+0xa):
81f2800a 0000??????????? add???? byte ptr [eax],al
syscow32v!SysCowAllocatePostCopyWorkItem <PERF> (syscow32v+0xc):
81f2800c ff????????????? ???

逐步查找(enter),最終我們可以發(fā)現(xiàn)driver的入口點(diǎn).這個過程其實(shí)是非常慢的,因?yàn)橄到y(tǒng)內(nèi)核在加載驅(qū)動實(shí)際代碼的過程中進(jìn)行了N多次調(diào)用.如果我們本身有驅(qū)動的代碼,也可以直接包含源代碼路徑,通過在實(shí)際代碼中設(shè)置斷點(diǎn),讓W(xué)indbg自己中斷到相應(yīng)的代碼位置(在實(shí)際調(diào)試內(nèi)核的過程中,這幾乎是不可能的,因?yàn)槟悴粫玫絎indows內(nèi)核或某個驅(qū)動程序的源代碼.Linux系列的某些driver們又另當(dāng)別論).這里為了方便,我包含了syscow的源代碼,增加斷點(diǎn)直接走到DriverEntry例程:

kd> u 81f42780
syscow32v!DriverEntry [隱藏了address]:
81f42780 8bff??????????? mov???? edi,edi
81f42782 55????????????? push??? ebp
81f42783 8bec??????????? mov???? ebp,esp
81f42785 51????????????? push??? ecx
81f42786 c745fc010000c0? mov???? dword ptr [ebp-4],0C0000001h
81f4278d a1749cf481????? mov???? eax,dword ptr [syscow32v!SysCowDbgFlags (81f49c74)]
81f42792 83e004????????? and???? eax,4
81f42795 7424??????????? je????? syscow32v!DriverEntry+0x3b (81f427bb)

其中顯示的匯編代碼,是內(nèi)核調(diào)用驅(qū)動是進(jìn)行的操作,其實(shí)也和實(shí)際代碼相對應(yīng).

在driver代碼中,如果要查看當(dāng)前參數(shù)值,用dv命令:

kd> dv
?? DriverObject = 0x84663730
?? RegistryPath = 0x8084b560
???????? status = 8
?????? dontload = 0

另外,用"dt 參數(shù)名"可以看某個參數(shù)的當(dāng)前值.

kd> dt DriverObject
Local var @ 0x81c05af8 Type _DRIVER_OBJECT*
0x84663730
?? +0x000 Type???????????? : 4
?? +0x002 Size???????????? : 168
?? +0x004 DeviceObject???? : (null)
?? +0x008 Flags??????????? : 2
?? +0x00c DriverStart????? : 0x81f28000
?? +0x010 DriverSize?????? : 0x27000
?? +0x014 DriverSection??? : 0x84230a68
?? +0x018 DriverExtension? : 0x846637d8 _DRIVER_EXTENSION
?? +0x01c DriverName?????? : _UNICODE_STRING "/FileSystem/SysCow"
?? +0x024 HardwareDatabase : 0x81af6ed8 _UNICODE_STRING "/REGISTRY/MACHINE/HARDWARE/DESCRIPTION/SYSTEM"
?? +0x028 FastIoDispatch?? : (null)
?? +0x02c DriverInit?????? : 0x81f4a005???? syscow32v!GsDriverEntry+0
?? +0x030 DriverStartIo??? : (null)
?? +0x034 DriverUnload???? : (null)
?? +0x038 MajorFunction??? : [28] 0x8189a5c1???? nt!IopInvalidDeviceRequest+0

這樣,我們就可以通過上述的這些命令,逐步分析一個驅(qū)動程序在加載和執(zhí)行過程中的情況。另外,Windbg還有眾多內(nèi)核調(diào)試命令,如!irp可以查看一個對象的數(shù)據(jù)結(jié)構(gòu),!devobj可以查看設(shè)備對象等,在今后的操作系統(tǒng)內(nèi)核學(xué)習(xí)過程中會不斷用到這些命令。

從上面的操作過程我們可以看出,利用Windbg調(diào)試驅(qū)動程序,或者說進(jìn)行內(nèi)核調(diào)試,是非常方便的。如果我們對Windows內(nèi)核有一定的了解,同時擁有一定匯編語言的功底,就可以有Windbg進(jìn)行簡單的系統(tǒng)排障(比如系統(tǒng)加載是出現(xiàn)藍(lán)屏,或是某個系統(tǒng)模塊出現(xiàn)問題)、驅(qū)動學(xué)習(xí)等。同時,這個過程也可以讓我們更深入的理解操作系統(tǒng)原理。另外,Windbg也可以進(jìn)行系統(tǒng)服務(wù)(service)的調(diào)試,這就是User mode的調(diào)試過程了。


posted @ 2008-03-27 21:16 Da Vinci 閱讀(138) | 評論 (1) |?編輯

2008年3月25日

.NET下午茶之三: 淺析CLR應(yīng)用程序域


CLR(公共語言運(yùn)行庫)可以說是整個.NET平臺的核心元素.基本上托管應(yīng)用程序所有的操作都是需要CLR的監(jiān)管和處理.這些操作包括進(jìn)程內(nèi)應(yīng)用程序的加載, IL語言轉(zhuǎn)換為機(jī)器語言, 異常管理, 垃圾回收,加載程序集等等.

CLR執(zhí)行托管代碼前,實(shí)際上會創(chuàng)建三個應(yīng)用程序域, 它們是系統(tǒng)域(System Domain),共享域(Shared Domain)和缺省應(yīng)用程序域(Default AppDomain).其中系統(tǒng)域和共享域?qū)τ谕泄艽a和CLR的宿主程序(如控制臺程序,ASP.NET等)不可見.域可以通過AppDomain.CreateDomain方法創(chuàng)建(下文會詳細(xì)敘述這個過程),在非托管的代碼中,可以使用一個ICORRutimeHost的接口創(chuàng)建(這個接口我也沒用過,一般都是托管的代碼).對于復(fù)雜的宿主程序,由網(wǎng)站根據(jù)應(yīng)用程序的數(shù)目來建立域。



???????????????????? 圖1.CLR啟動程序創(chuàng)建的域
?????????? (轉(zhuǎn)載自Inside .NET Framework:CLR runtime object)

注:下文中的AppDomain特指應(yīng)用程序域,對于系統(tǒng)域和共享域保持原來的指定

要注意的是,這里的域不同于C#類中域的概念.C#類中域指類和對象相關(guān)的變量,這里的域其實(shí)是一種輕量的進(jìn)程,你完全可以把它當(dāng)成進(jìn)程來理解.有人也許不明白既然有了進(jìn)程的概念,干嗎又搞出一個域來.其實(shí)這是為了實(shí)現(xiàn)在服務(wù)器中加載多個應(yīng)用程序才提出的.AppDomain與進(jìn)程相比優(yōu)勢在于:
???? 1.所需系統(tǒng)資源更少
???? 2.進(jìn)程之間的AppDomain可以共享資源
但是千萬別把AppDomain和線程搞混,這二者之間沒有從屬的關(guān)系.地球人都知道線程是進(jìn)程的孩子,但是線程和AppDomain的關(guān)系類似于正交關(guān)系.同一時間內(nèi)多個線程可以運(yùn)行在一個AppDomain中,多個AppDomain也可以跨越一個線程.

下面, 我們先大致看一下系統(tǒng)域和共享域的一些基本概念,再著重探討AppDomain的知識.

系統(tǒng)域(System Domain):系統(tǒng)域初始化共享域和AppDomain。它在共享域中加載了mscorlib.dll()。同時,系統(tǒng)域產(chǎn)生了進(jìn)程的接口ID(不知道這樣描述對不對),以此來創(chuàng)建AppDomain的接口虛擬表映射的接口。在進(jìn)程中,系統(tǒng)域監(jiān)控所有的域,加載(load)和卸載(unload)AppDomain也有它完成。可以認(rèn)為系統(tǒng)域是CLR中的基礎(chǔ)域。

共享域(Shared Domain):在.NET中,不是所有的代碼都屬于某個域,但這些代碼卻是用戶代碼所必需的。共享域就負(fù)責(zé)加載這部分代碼。比如大家熟悉的Object,Array,String,delegate等。這些代碼在CLR啟動程序時先被加載到共享域中.共享域中有一個程序集映射表,通過這個表來查找共享的程序集的依賴關(guān)系. 可以認(rèn)為共享域是CLR中的帶有索引的共享文件夾.

注:用戶代碼和控制臺程序也可以加載到共享域.(具體方法?..不急..以后告訴你)

AppDomain

終于說到學(xué)習(xí).NET要接觸最多的AppDomain了.其實(shí)CLR啟動應(yīng)用程序時,就會自動創(chuàng)建一個AppDomain的實(shí)例,叫默認(rèn)的AppDomain(Default AppDomain).大部分的應(yīng)用程序在運(yùn)行期間只創(chuàng)建一個域.有些應(yīng)用程序有多個AppDomain,它們之間由.NET Remoting通信。AppDomain之間是彼此獨(dú)立的。一個AppDomain無法訪問其他的AppDomain的程序集和對象(并不意味著不能相互通信),并且可以定義自己的程序集訪問策略。
通過System.AppDomain類的實(shí)例,可以獲得進(jìn)程中一個AppDomain的引用:

??? using System;
??? using System.Threading;
??? class MyAppDomain
?? {
??????? static void Main()
?????? {
?????????? Thread.CurrentThread.Name = "MyDomain";
?????????? AppDomainSetup info = new AppDomainSetup();
?????????? AppDomain myAppDomain = AppDomain.CurrentDomain;
?????????? AppDomain newDomain = AppDomain.CreateDomain("MyDomain", null, info);
???
?????????? myAppDomain.ExecuteAssembly("AssemblyName.exe"); // load your assembly name
?????????? AppDomain.unload(myDomain);
??????? }
??? }

上面代碼創(chuàng)建了一個AppDomain的實(shí)例,并調(diào)用ExecuteAssembly方法加載一個程序集。其中AppDomainSetup為CLR定位信息。unload為AppDomain類的卸載域方法。注意:這段代碼中ExecuteAssembly()方法的線程調(diào)用了AssemblyName.exe程序集的線程。這樣一個線程就跨越了兩個AppDomain。這也說明了AppDomain和線程之間沒有包含關(guān)系。

到這里,我們已經(jīng)對CLR中域的概念,各種域的結(jié)構(gòu)有了一個大概的認(rèn)識。AppDomain是CLR中最基本的概念,在CLR的整個框架中都與它有著千絲萬縷的聯(lián)系,掌握它是學(xué)習(xí)CLR的基礎(chǔ)。有關(guān)AppDomain間通信的方法,我們在.NET Remoting篇中再詳細(xì)闡述。


posted @ 2008-03-25 22:25 Da Vinci 閱讀(32) | 評論 (2) |?編輯

2008年3月22日

Windbg內(nèi)核調(diào)試之二: 常用命令


運(yùn)用Windbg進(jìn)行內(nèi)核調(diào)試, 熟練的運(yùn)用命令行是必不可少的技能. 但是面對眾多繁瑣的命令, 實(shí)在是不可能全部的了解和掌握. 而了解Kernel正是需要這些命令的指引, 不斷深入理解其基本的內(nèi)容. 下面, 將介紹最常用的一些指令, 使初學(xué)Kernel調(diào)試的朋友們能有一個大致的了解. 至于如何熟練的運(yùn)用它們, 還需要實(shí)際的操作過程中進(jìn)行反復(fù)的琢磨.

Windbg能夠方便的進(jìn)行遠(yuǎn)程調(diào)試和本地進(jìn)程調(diào)試(只限于User模式), 遠(yuǎn)程調(diào)試又分User mode和Kernel mode兩種. 個人認(rèn)為用Windbg進(jìn)行遠(yuǎn)程的User mode調(diào)試還不如用Visual Studio來的方便, 畢竟要一些相應(yīng)的配置才行, 而Visual Studio只需要遠(yuǎn)程機(jī)器的IP地址即可.(當(dāng)然, 如果你在LiveCD或WinPE下進(jìn)行User mode的調(diào)試, 這時候沒有VS, Windbg就是不二之選了). Windbg的優(yōu)勢就在于遠(yuǎn)程的Kernel模式的調(diào)試, 這是VS所做不到的.

1. 首先是設(shè)置符號路徑(無論在User mode下還是Kernel mode下都需要), 在Windbg的符號路徑對話框中, 輸入以下符號路徑:

????? srv*c:/symbols*http://msdl.microsoft.com/download/symbols

這個符號路徑是自動在微軟給定的symbols路徑下進(jìn)行自動搜索, 一些常用的符號都可以利用這個鏈接自動找到. 當(dāng)然, 如果你的本地已經(jīng)有相應(yīng)的符號路徑, 包含本地的也可以.

2. 等Windbg的聯(lián)機(jī)狀態(tài)設(shè)置好后, 按下Ctrl+break, 中斷當(dāng)前的Kernel狀態(tài), 在Windbg的命令行窗口輸入"?", 則會輸出幫助菜單, 在這個Menu中會顯示一些常用的命令:

? (1)斷點(diǎn)指令 ???? B[C|D|E] [<bps>] ???? clear|disable|enable breakpoints ???? BL ???? list breakpoints ???? BP <address> ???? set soft breakpoints ???? BA <access> <size> <addr> ???? break on access ? (2)數(shù)據(jù)查看指令 ???? D[type][<range>] ???? dump memory ???? DT [-n|y] [[mod!]name] [[-n|y]fields][address] [-l list] [-a[]|c|i|o|r[#]|v] ???? dump using type information ???? DV [<name>]? ???? dump local variables ? (3)數(shù)據(jù)修改指令 ???? E[type] <address> [<values>] ???? enter memory values ? (4)運(yùn)行 ???? G[H|N] [=<address> [<address>...]]? ???? go ???? P [=<addr>] [<value>] ???? step over ? (5)堆棧操作 ???? K[b|p|P|v] ? (6)顯示加載的模塊列表 ???? LM ???? list modules ? (7)寄存器操作 ???? R [[<reg> [= <expr>]]] ???? view or set registers ? (8)Search指令 ???? S[<opts>] <range> <values> ???? search memory ? (9)跟蹤指令T,TA,TB,TC,WT,P,PA,PC ? (10)退出 ????? Q

? (11)反匯編
????? U[<range>]

其中最常用的就是反匯編操作和顯示模塊操作. LM命令顯示當(dāng)前加載的模塊. 當(dāng)你連接過程中, Windbg提示相應(yīng)的module找不到時, 就可以運(yùn)用這個命令進(jìn)行查看. LM的一個擴(kuò)展命令是"lm t n", 這個命令顯示當(dāng)前所有加載的驅(qū)動信息(過去的命令是!driver),在調(diào)試內(nèi)核驅(qū)動的過程中非常有用,可以找到相應(yīng)驅(qū)動的起始地址。反匯編命令u, 可以在相應(yīng)的地址中逐步的解析代碼,這在內(nèi)核調(diào)試中是最常用的一種查看代碼的方式。

除上面的一些基本命令之外,還有一些非常有用的指令:
?
? (1)K[KB|KP]
???? 顯示當(dāng)前的堆棧,當(dāng)然也可以用alt+6直接調(diào)出窗口顯示
?
? (2)!process
???? 顯示當(dāng)前的進(jìn)程EXPROCESS狀態(tài),!process 0 0 顯示所有的進(jìn)程狀態(tài)

? (3)!thread
???? 顯示當(dāng)前的線程狀態(tài),dt nt!_ethread顯示ETHREAD結(jié)構(gòu)

? (4)!drvobj [path]
???? 列出當(dāng)前的驅(qū)動程序在驅(qū)動對象中的例程,其中path是驅(qū)動的設(shè)備路徑,例如: !drvobj /filesystem/fat 2 列出FAT文件系統(tǒng)驅(qū)動的例程

? (5)dt nt!_*
???? 查看內(nèi)核的數(shù)據(jù)結(jié)構(gòu)
?
? (6)!stack 0
???? 顯示線程當(dāng)前地址
?
? (7)!ioapic
???? 查看I/O的中斷控制器

? (8)!irql
???? 查看CPU的IRQL,這在CPU中斷調(diào)試中非常有用

? (9)!exqueue
???? 可以看系統(tǒng)輔助的線程列表

? (10)!reg viewlist
???? 注冊表的存儲顯示,!reg hivelist顯示注冊表一個存儲的內(nèi)存使用量

? (11)!vm
???? 顯示系統(tǒng)的內(nèi)存池信息

? (12)dt _TOKEN
???? 顯示內(nèi)部訪問令牌

? (13)!object /device
???? 顯示設(shè)備對象信息,用winobj工具也可以看到

以上命令是一些常用的內(nèi)核調(diào)試命令, 還有非常多的命令技巧, 不可能全部一一解釋. 在今后的文章, 還會結(jié)合具體的調(diào)試過程分析進(jìn)行逐步介紹. 內(nèi)核的調(diào)試無非是處理器, 系統(tǒng)設(shè)備, 內(nèi)存, 進(jìn)程線程, 注冊表, 驅(qū)動這幾大類的信息, 每一類都有很多的命令, 需要我們在實(shí)際的調(diào)試過程中不斷認(rèn)識和體會它的用法. 當(dāng)然, 理解這些命令用法的前提是需要我們對操作系統(tǒng)內(nèi)部構(gòu)造有一個清晰的認(rèn)識. 這需要不斷的學(xué)習(xí), 對OS有一個全局的把握, 這樣才能更深入的理解它. 而通過Windbg的內(nèi)核調(diào)試, 我們有一種更好的方式直接與Kernel打交道, 這對我們深入理解和認(rèn)識操作系統(tǒng)有很大的幫助. 下一節(jié)我們將通過一個實(shí)際的調(diào)試driver的例子, 來進(jìn)一步認(rèn)識Windbg在內(nèi)核調(diào)試中的作用.


posted @ 2008-03-22 12:30 Da Vinci 閱讀(102) | 評論 (0) |?編輯

2008年3月20日

Windbg內(nèi)核調(diào)試之一: Vista Boot Config設(shè)置


Windbg進(jìn)行內(nèi)核調(diào)試,需要一些基本的技巧和設(shè)置,在這個系列文章中,我將使用Windbg過程中所遇到的一些問題和經(jīng)驗(yàn)記錄下來,算是對Kernel調(diào)試的一個總結(jié),同時也是學(xué)習(xí)Windows系統(tǒng)內(nèi)核的另一種過程。

很多人說Windbg不如SoftIce好用, 但是我使用過程中還是覺得Windbg能更好的反映系統(tǒng)狀態(tài), 而且相比SoftIce, Windbg更穩(wěn)定(雖然它的部分操作略顯復(fù)雜), 下面介紹Windbg的Kernel模式調(diào)試第一部分: 雙機(jī)連接設(shè)置.

Vista和XP不同, 沒有boot.ini文件, 需要用bcdedit進(jìn)行啟動設(shè)置。(關(guān)于啟動數(shù)據(jù)配置編輯器BCD的具體設(shè)置, 參見另一篇文章: (From MS)Vista: 啟動配置數(shù)據(jù)編輯器(BCD))

在administrator權(quán)限下, 進(jìn)入command line模式,? 鍵入bcdedit命令, 會出現(xiàn)以下界面:

?

然后, 設(shè)置端口COM1, baudrate為115200 (除COM1外, 也可以用1394或USB. 1394用起來比COM口快多了, 當(dāng)然前提是你需要有1394卡及其驅(qū)動. 很惡心的是Vista不再支持1394的文件傳輸協(xié)議, 但是用windbg雙機(jī)調(diào)試還是可以的)
命令為:
bcdedit?/dbgsettings?{serial?[baudrate:value][debugport:value]?| 1394?[channel:value]?|?usb?}



接著, 我們需要復(fù)制一個開機(jī)選項(xiàng), 以進(jìn)入OS的debug模式
命令為:
bcdedit?/copy?{current}?/d?DebugPoint
DebugPoint為選項(xiàng)名稱, 名字可以自己定義. 然后復(fù)制得到的ID號.



接著增加一個新的選項(xiàng)到引導(dǎo)菜單
bcdedit?/displayorder?{current}?{ID}
這里的{ID}的ID值是剛生成的ID值.



激活DEBUG : bcdedit?/debug?{ID}?ON
這里的{ID}?的ID值還是剛才的ID值.



命令執(zhí)行成功后, 重新啟動機(jī)器.

選擇DebugPoint登錄,開啟Windbg

連接成功, 則顯示如下:
Microsoft?(R)?Windows?Debugger??Version?6.6.0007.5
Copyright?(c)?Microsoft?Corporation.?All?rights?reserved.

Opened?//./pipe/com_1
Waiting?to?reconnect...
Connected?to?Windows?Vista?6000?x86?compatible?target,?ptr64?FALSE
Kernel?Debugger?connection?established.
Symbol?search?path?is:?symsrv*symsrv.dll*F:/symbols*http://msdl.microsoft.com/download/symbols
Executable?search?path?is:?
Windows?Vista?Kernel?Version?6000?MP?(1?procs)?Free?x86?compatible
Built?by:?6000.16386.x86fre.vista_rtm.061101-2205
Kernel?base?=?0x81800000?PsLoadedModuleList?=?0x81911db0
System?Uptime:?not?available
Break?instruction?exception?-?code?80000003?(first?chance)
*******************************************************************************
*????????????????????????????????????????????????????????????????????????????
*???You?are?seeing?this?message?because?you?pressed?either???????????????????
*???????CTRL+C?(if?you?run?kd.exe)?or,???????????????????????????????????????
*???????CTRL+BREAK?(if?you?run?WinDBG),??????????????????????????????????????
*???on?your?debugger?machine's?keyboard.?????????????????????????????????????
*????????????????????????????????????????????????????????????????????????????
*???????????????????THIS?IS?NOT?A?BUG?OR?A?SYSTEM?CRASH??????????????????????
*????????????????????????????????????????????????????????????????????????????
*?If?you?did?not?intend?to?break?into?the?debugger,?press?the?"g"?key,?then??
*?press?the?"Enter"?key?now.??This?message?might?immediately?reappear.??If?it
*?does,?press?"g"?and?"Enter"?again.?????????????????????????????????????????
*????????????????????????????????????????????????????????????????????????????
*******************************************************************************
nt!RtlpBreakWithStatusInstruction:
81881760?cc??????????????int?????3

總結(jié): 雖然利用VMware虛擬機(jī)能更方便的設(shè)置雙機(jī)的調(diào)試環(huán)境, 而且這種模擬環(huán)境也是大多數(shù)人使用的(方便), 但是如果有雙機(jī)條件的話,? 還是希望大家能夠使用兩臺機(jī)器, 因?yàn)橛锰摂M機(jī)進(jìn)行Kernel調(diào)試, 真不是一般的慢! 基本就等于死機(jī). 即時你的主機(jī)內(nèi)存2G, 分給VMware1G, 還是會相當(dāng)卡(Kerenl模式與User模式不同).

posted @ 2008-03-20 14:38 Da Vinci 閱讀(71) | 評論 (0) |?編輯

Windows程序設(shè)計:文件操作(轉(zhuǎn)載)


操作文件基本上是每個應(yīng)用程序都必須做的事情。除了必要的配置信息外,用戶的工作最終都要以文件的形式保存到磁盤上。保存和獲取這些信息可以使用獨(dú)立的磁盤文件,也可以使用系統(tǒng)自帶的數(shù)據(jù)庫——注冊表。

本章首先介紹底層操作文件的API函數(shù)和MFC中對應(yīng)的CFile類;然后介紹一些與操作文件相關(guān)的邏輯驅(qū)動器和目錄方面的知識,包括驅(qū)動器的格式化和卷標(biāo)設(shè)置、目錄的創(chuàng)建和刪除等;接著,本章介紹使用 API函數(shù)和ATL庫中的CRegKey類操作注冊表的方法;本章還重點(diǎn)討論了內(nèi)存映射文件在讀寫磁盤文件和建立共享內(nèi)存方面的應(yīng)用;本章最后介紹一個多線程的文件分割系統(tǒng)的開發(fā)過程。

8.1? 文件操作

文件的輸入輸出(I/O)服務(wù)是操作系統(tǒng)的重要部分。Windows提供了一類API函數(shù)來讀、寫和管理磁盤文件。MFC將這些函數(shù)轉(zhuǎn)化為一個面向?qū)ο蟮念悺狢File,它允許將文件視為可以由 CFile成員函數(shù)操作的對象,如Read和Write等。CFile類實(shí)現(xiàn)了程序開發(fā)者執(zhí)行底層文件I/O需要的大部分功能。

并不是在任何時候使用CFile類都是方便的,特別是要與底層設(shè)備(如COM口、設(shè)備驅(qū)動)進(jìn)行交互的時候,所以本節(jié)主要討論管理文件的API函數(shù)。事實(shí)上,了解這些函數(shù)之后,自然就會使用CFile類了。

8.1.1? 創(chuàng)建和讀寫文件

使用API函數(shù)讀寫文件時,首先要使用 CreateFile函數(shù)創(chuàng)建文件對象(即打開文件),調(diào)用成功會返回文件句柄;然后以此句柄為參數(shù)調(diào)用ReadFile和WriteFile函數(shù),進(jìn)行實(shí)際的讀寫操作;最后調(diào)用CloseHandle函數(shù)關(guān)閉不再使用的文件對象句柄。

1.打開和關(guān)閉文件

CreateFile是一個功能相當(dāng)強(qiáng)大的函數(shù),Windows下的底層設(shè)備差不多都是由它打開的。它可以創(chuàng)建或打開文件、目錄、物理磁盤、控制臺緩沖區(qū)、郵槽和管道等。調(diào)用成功后,函數(shù)返回能夠用來訪問此對象的句柄,其原型如下:

HANDLE CreateFile (

? LPCTSTR lpFileName,?? ???????????????????????????????????????????? // 要創(chuàng)建或打開的對象的名稱

? DWORD dwDesiredAccess, ??????????????????????????????? // 文件的存取方式

? DWORD dwShareMode, ?????????????????????????????????????????????? // 共享屬性

? LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 安全屬性

??DWORD dwCreationDisposition, ??????????????????????????????? // 文件存在或不存在時系統(tǒng)采取的行動

? DWORD dwFlagsAndAttributes,?????????????????????????????????? // 新文件的屬性

? HANDLE hTemplateFile ?????????????????????????????????????????????? // 一個文件模板的句柄

);

各參數(shù)含義如下。

(1)lpFileName參數(shù)是要創(chuàng)建或打開的對象的名稱。如果打開文件,直接在這里指定文件名稱即可;如果操作對象是第一個串口,則要指定“COM1”為文件名,然后就可以像操作文件一樣操作串口了;如果要打開本地電腦上的一個服務(wù),要以“"""."服務(wù)名稱"”為文件名,其中的“.”代表本地機(jī)器;也可以使用CreateFile打開網(wǎng)絡(luò)中其他主機(jī)上的文件,此時的文件名應(yīng)該是“""主機(jī)名"共享目錄名"文件名”。

(2)dwDesiredAcces參數(shù)是訪問方式,它指定了要對打開的對象進(jìn)行何種操作。指定GENERIC_READ標(biāo)志表示以只讀方式打開;指定GENERIC_WRITE標(biāo)志表示以只寫方式打開;指定這兩個值的組合,表示要同時對打開的對象進(jìn)行讀寫操作。

(3)dwShareMode參數(shù)指定了文件對象的共享模式,表示文件打開后是否允許其他代碼以某種方式再次打開這個文件,它可以是下列值的一個組合:

l????????? 0???????????????????????????????????????????? 不允許文件再被打開。C語言中的fopen函數(shù)就是這樣打開文件的

l????????? FILE_SHARE_DELETE????? 允許以后的程序代碼對文件刪除文件(Win98系列的系統(tǒng)不支持這????? ???????? ???????? ???????? ???????? 個標(biāo)志)

l????????? FILE_SHARE_READ????????? 允許以后的程序代碼以讀方式打開文件

l????????? FILE_SHARE_WRITE???????? 允許以后的程序代碼以寫方式打開文件

(4)dwCreationDisposition參數(shù)指定了當(dāng)文件已存在或者不存在時系統(tǒng)采取的動作。在這里設(shè)置不同的標(biāo)志就可以決定究竟是要打開文件,還是要創(chuàng)建文件。參數(shù)的可能取值如下:

l????????? CREATE_ALWAYS??? 創(chuàng)建新文件。如果文件存在,函數(shù)會覆蓋這個文件,清除存在的屬性

l????????? CREATE_NEW??????????????????? 創(chuàng)建新文件。如果文件存在,函數(shù)執(zhí)行失敗

l????????? OPEN_ALWAYS????????????????? 如果文件已經(jīng)存在,就打開它,不存在則創(chuàng)建新文件

l????????? OPEN_EXISTING?????????????? 打開存在的文件。如果文件不存在,函數(shù)執(zhí)行失敗

l????????? TRUNCATE_EXISTING??? 打開文件并將文件截斷為零,當(dāng)文件不存在時函數(shù)執(zhí)行失敗

(5)dwFlagsAndAttributes參數(shù)用來指定新建文件的屬性和標(biāo)志。文件屬性可以是下面這些值的組合:

l????????? FILE_ATTRIBUTE_ARCHIVE?? ???????? 標(biāo)記歸檔屬性

l????????? FILE_ATTRIBUTE_HIDDEN ???????????? 標(biāo)記隱藏屬性

l????????? FILE_ATTRIBUTE_READONLY ?????? 標(biāo)記只讀屬性

l????????? FILE_ATTRIBUTE_READONLY ?????? 標(biāo)記系統(tǒng)屬性

l????????? FILE_ATTRIBUTE_TEMPORARY ?? 臨時文件。操作系統(tǒng)會盡量把所有文件的內(nèi)容保持在內(nèi)?????? ???????? ???????? ???????? ???????? ???????? ???????? 存中以加快存取速度。使用完后要盡快將它刪除

此參數(shù)還可同時指定對文件的操作方式,下面是一些比較常用的方式:

l????????? FILE_FLAG_DELETE_ON_CLOSE ?? 文件關(guān)閉后系統(tǒng)立即自動將它刪除

l????????? FILE_FLAG_OVERLAPPED ????????????? 使用異步讀寫文件的方式

l????????? FILE_FLAG_WRITE_THROUGH ???? 系統(tǒng)不會對文件使用緩存,文件的任何改變都會被系統(tǒng)?????? ???????? ???????? ???????? ???????? ???????? ???????? 立即寫入硬盤

(6)hTemplateFile參數(shù)指定了一個文件模板句柄。系統(tǒng)會復(fù)制該文件模板的所有屬性到當(dāng)前創(chuàng)建的文件中。Windows 98系列的操作系統(tǒng)不支持它,必須設(shè)為NULL。

打開或創(chuàng)建文件成功時,函數(shù)返回文件句柄,失敗時返回INVALID_HANDLE_VALUE(-1)。如果想再詳細(xì)了解失敗的原因,可以繼續(xù)調(diào)用GetLastError函數(shù)。

用不同的參數(shù)組合調(diào)用CreateFile函數(shù)可以完成不同的功能,例如,下面的代碼為讀取數(shù)據(jù)打開了一個存在的文件。

???????? HANDLE hFile;

???????? hFile = ::CreateFile("myfile.txt",??? ???????? // 要打開的文件

?????????????????? GENERIC_READ,???????????????????????? // 要讀這個文件

?????????????????? FILE_SHARE_READ,?????????????????? ???????? // 允許其他程序已只讀形式再次打開它

?????????????????? NULL,?????????????????????????????????? ???????? // 默認(rèn)安全屬性

?????????????????? OPEN_EXISTING,??????????????????????? // 僅僅打開存在的文件(如果不存不創(chuàng)建)

?????????????????? FILE_ATTRIBUTE_NORMAL,? // 普通文件

?????????????????? NULL);????????????????????????????????? ???????? // 沒有模板

???????? if(hFile == INVALID_HANDLE_VALUE)

???????? { ?????????????? ……// 不能夠打開文件?????? }

僅當(dāng)當(dāng)前目錄中存在名稱為myfile.txt的文件時,上面的CreateFile才能執(zhí)行成功。由于為dwCreationDisposition參數(shù)指定了OPEN_EXISTING,所以當(dāng)要打開的文件不存在時,CreateFile返回INVALID_HANDLE_VALUE,而不會創(chuàng)建這個文件。如果想創(chuàng)建一個文件以便向里面寫入數(shù)據(jù),可以使用下面的代碼:

???????? HANDLE hFile;

???????? hFile = CreateFile("myfile.txt",????? ?????????????????? // 要創(chuàng)建的文件

?????????????????? GENERIC_WRITE,?????????????????????? // 要寫這個文件

?????????????????? 0,???????????????????????????????????????????????????? // 不共享

?????????????????? NULL,??????????????????????????????????????????? // 默認(rèn)安全屬性

?????????????????? CREATE_ALWAYS,????????????????????? // 如果存在就覆蓋

?????????????????? FILE_ATTRIBUTE_NORMAL,? // 普通文件

?????????????????? NULL);?????????????????????????????????????????? // 沒有模板

???????? if(hFile == INVALID_HANDLE_VALUE)

???????? { ?????????????? ……// 不能夠打開文件?????? }

要關(guān)閉打開的文件,直接以CreateFile返回的文件句柄調(diào)用CloseHandle函數(shù)即可。

2.移動文件指針

系統(tǒng)為每個打開的文件維護(hù)一個文件指針,指定對文件的下一個讀寫操作從什么位置開始。隨著數(shù)據(jù)的讀出或?qū)懭?#xff0c;文件指針也隨之移動。當(dāng)文件剛被打開時,文件指針處于文件的頭部。有時候需要隨機(jī)讀取文件內(nèi)容,這就需要先調(diào)整文件指針,SetFilePointer函數(shù)提供了這個功能,原型如下:

DWORD SetFilePointer (

? ?????????????? HANDLE hFile,? ???????????????????????? // 文件句柄

? ?????????????? LONG lDistanceToMove, ? ???????? // 要移動的距離

? ?????????????? PLONG lpDistanceToMoveHigh,? ?????? // 移動距離的高32位,一般設(shè)置為NULL

? ?????????????? DWORD dwMoveMethod ???????? // 移動的模式

?????????????????? );

dwMoveMethod參數(shù)指明了從什么地方開始移動,可以是下面的一個值:

l????????? FILE_BEGIN?????????????? 開始移動位置為0,即從文件頭部開始移動

l????????? FILE_CURRENT??????? 開始移動位置是文件指針的當(dāng)前值

l????????? FILE_END??????????????????????????? 開始移動位置是文件的結(jié)尾,即從文件尾開始移動

函數(shù)執(zhí)行失敗返回-1,否則返回新的文件指針的位置。

文件指針也可以移動到所有數(shù)據(jù)的后面,比如現(xiàn)在文件的長度是100 KB,但還是可以成功的將文件指針移動到1000 KB的位置。這樣做可以達(dá)到擴(kuò)展文件長度的目的。

SetEndOfFile函數(shù)可以截斷或者擴(kuò)展文件。該函數(shù)移動指定文件的結(jié)束標(biāo)志(end-of-file,EOF)到文件指針指向的位置。如果文件擴(kuò)展,舊的EOF位置和新的EOF位置間的內(nèi)容是未定義的。SetEndOfFile函數(shù)的用法如下:

BOOL SetEndOfFile(HANDLE hFile );

截斷或者擴(kuò)展文件時,要首先調(diào)用SetFilePointer移動文件指針,然后再調(diào)用SetFilePointer函數(shù)設(shè)置新的文件指針位置為EOF。

3.讀寫文件

讀寫文件的函數(shù)是ReadFile和WriteFile,這兩個函數(shù)既可以同步讀寫文件,又可以異步讀寫文件。而函數(shù)ReadFileEx和WriteFileEx只能異步讀寫文件。

從文件讀取數(shù)據(jù)的函數(shù)是ReadFile,向文件寫入數(shù)據(jù)的函數(shù)是WriteFile,操作的開始位置由文件指針指定。這兩個函數(shù)的原型如下:

BOOL ReadFile(

? ???? HANDLE hFile,?????????????????????????????????????? // 文件句柄

? ???? LPVOID lpBuffer,?????? ?????????????????????????????????? // 指向一個緩沖區(qū),函數(shù)會將讀出的數(shù)據(jù)返回到這里

? ???? DWORD nNumberOfBytesToRead, ???? // 要求讀入的字節(jié)數(shù)

? ???? LPDWORD lpNumberOfBytesRead, ?? // 指向一個DWORD類型的變量,

?????????????????????????????????????????????????????????????????????????? // 用于返回實(shí)際讀入的字節(jié)數(shù)

? ???? LPOVERLAPPED lpOverlapped ?????????????????? // 以便設(shè)為NULL

);

BOOL WriteFile (hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped);

當(dāng)用WriteFile寫文件時,寫入的數(shù)據(jù)通常被Windows暫時保存在內(nèi)部的高速緩存中,等合適的時候再一并寫入磁盤。如果一定要保證所有的數(shù)據(jù)都已經(jīng)被傳送,可以強(qiáng)制使用FlushFileBuffers函數(shù)來清空數(shù)據(jù)緩沖區(qū),函數(shù)的惟一參數(shù)是要操作的文件句柄。

BOOL FlushFileBuffers (HANDLE hFile );

4.鎖定文件

當(dāng)對文件數(shù)據(jù)的一致性要求較高時,為了防止程序在寫入的過程中其他進(jìn)程剛好在讀取寫入?yún)^(qū)域的內(nèi)容,可以對已打開文件的某個部分進(jìn)行加鎖,這就可以防止其他進(jìn)程對該區(qū)域進(jìn)行讀寫。加鎖和解鎖的函數(shù)是LockFile和UnlockFile,它們的原型如下:

BOOL? LockFile(

??? HANDLE hFile, ????????????????????????????????????????????? // 文件句柄

??? DWORD dwFileOffsetLow, ?????????????????????????? // 加鎖的開始位置

??? DWORD dwFileOffsetHigh,

??? DWORD nNumberOfBytesToLockLow, ?????? // 加鎖的區(qū)域的大小

??? DWORD nNumberOfBytesToLockHigh

??? );

UnlockFile ( hFile, dwFileOffsetLow, dwFileOffsetHigh,

?????????????????? nNumberOfBytesToUnlockLow, nNumberOfBytesToUnlockHigh);

dwFileOffsetLow和 dwFileOffsetHigh參數(shù)組合起來指定了加鎖區(qū)域的開始位置,nNumberOfBytesToLockLow和 nNumberOfBytesToLockHigh參數(shù)組合起來指定了加鎖區(qū)域的大小。這兩個參數(shù)都指定了一個64位的值,在Win32中,只使用32位就夠了。

如果加鎖文件的進(jìn)程終止,或者文件關(guān)閉時還未解鎖,操作系統(tǒng)會自動解除對文件的鎖定。但是,操作系統(tǒng)解鎖文件花費(fèi)的時間取決于當(dāng)前可用的系統(tǒng)資源。因此,進(jìn)程終止時最好顯式地解鎖所有已鎖定的文件,以免造成這些文件無法訪問。

8.1.2? 獲取文件信息

1.獲取文件類型

Windows下的許多對象都稱之為文件,如果想知道一個文件句柄究竟對應(yīng)什么對象,可以使用GetFileType函數(shù),原型如下:

DWORD GetFileType(HANDLE hFile);

函數(shù)的返回值說明了文件類型,可以是下面的一個值:

l????????? FILE_TYPE_CHAR???????????? 指定文件是字符文件,通常是LPT設(shè)備或控制臺

l????????? FILE_TYPE_DISK?????????????? 指定文件是磁盤文件

l????????? FILE_TYPE_PIPE??????????????? 指定文件是套節(jié)字,一個命名的或未命名的管道

l????????? FILE_TYPE_UNKNOWN 不能識別指定文件,或者函數(shù)調(diào)用失敗

2.獲取文件大小

如果確定操作的對象是磁盤文件,還可以使用GetFileSize函數(shù)取得這個文件的長度。

DWORD GetFileSize(

? HANDLE hFile,??????????????? // 文件句柄

? LPDWORD lpFileSizeHigh?????? // 用于返回文件長度的高字。可以指定這個參數(shù)為NULL

);

函數(shù)執(zhí)行成功將返回文件大小的低雙字,如果lpFileSizeHigh參數(shù)不是NULL,函數(shù)將文件大小的高雙字放入它指向的DWORD變量中。

如果函數(shù)執(zhí)行失敗,并且 lpFileSizeHigh是NULL,返回值將是INVALID_FILE_SIZE;如果函數(shù)執(zhí)行失敗,但lpFileSizeHigh不是 NULL,返回值是INVALID_FILE_SIZE,進(jìn)一步調(diào)用GetLastError會返回不為NO_ERROR的值。

如果返回值是INVALID_FILE_SIZE,應(yīng)用程序必須調(diào)用GetLastError來確定函數(shù)調(diào)用是否成功。原因是,當(dāng)lpFileSizeHigh不為NULL或者文件大小為 0xffffffff時,函數(shù)雖然調(diào)用成功了,但依然會返回INVALID_FILE_SIZE。這種情況下,GetLastError會返回 NO_ERROR來響應(yīng)成功。

3.獲取文件屬性

如果要查看文件或者目錄的屬性,可以使用GetFileAttributes函數(shù),它會返回一系列FAT風(fēng)格的屬性信息。

DWORD GetFileAttributes(LPCTSTR lpFileName);??????? // lpFileName指定了文件或者目錄的名稱

函數(shù)執(zhí)行成功,返回值包含了指定文件或目錄的屬性信息,可以是下列取值的組合:

l????????? ?FILE_ATTRIBUTE_ARCHIVE ??????????????? 文件包含歸檔屬性

l????????? ?FILE_ATTRIBUTE_COMPRESSED ?????? 文件和目錄被壓縮

l????????? ?FILE_ATTRIBUTE_DIRECTORY ?????????? 這是一個目錄

l????????? ?FILE_ATTRIBUTE_HIDDEN ?????????????????????????? 文件包含隱含屬性

l????????? ?FILE_ATTRIBUTE_NORMAL ??????????????? 文件沒有其他屬性

l????????? ?FILE_ATTRIBUTE_READONLY ??????????? 文件包含只讀屬性

l????????? ?FILE_ATTRIBUTE_SYSTEM ????????????????? 文件包含系統(tǒng)屬性

l????????? ?FILE_ATTRIBUTE_TEMPORARY T ???? 文件是一個臨時文件

這些屬性對目錄也同樣適用。INVALID_FILE_ATTRIBUTES(0xFFFFFFFF)是函數(shù)執(zhí)行失敗后的返回值。

下面是快速檢查某個文件或目錄是否存在的自定義函數(shù),可以將它用在自己的工程中。

BOOL FileExists(LPCTSTR lpszFileName, BOOL bIsDirCheck)

{?????? // 試圖取得文件的屬性

?????????????????? DWORD dwAttributes = GetFileAttributes(lpszFileName);

??? ?if(dwAttributes == 0xFFFFFFFF)

??????? return FALSE;

???????? if ((dwAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)

???????? {?????? if (bIsDirCheck)

??????????????????????????? return TRUE;

?????????????????? else

??????????????????????????? return FALSE;

???????? }

???????? else

???????? {?????? if (!bIsDirCheck)

??????????????????????????? return TRUE;

?????????????????? else

??????????????????????????? return FALSE;

???????? }

}

第2個參數(shù)bIsDirCheck指定要檢查的對象是目錄還是文件。

與GetFileAttributes相對應(yīng)的函數(shù)是SetFileAttributes,這個函數(shù)用來設(shè)置文件屬性。

BOOL SetFileAttributes(

? LPCTSTR lpFileName,????????????? // 目標(biāo)文件名稱

? DWORD dwFileAttributes???????????????? // 要設(shè)置的屬性值

);

8.1.3? 常用文件操作

1.拷貝文件

拷貝文件的函數(shù)是CopyFile和CopyFileEx,其作用都是復(fù)制一個存在的文件到一個新文件中。CopyFile函數(shù)的用法如下:

BOOL CopyFile(

? LPCTSTR lpExistingFileName, // 指定已存在的文件的名稱

? LPCTSTR lpNewFileName,??????????????? // 指定新文件的名稱

? BOOL bFailIfExists ?????????????????? // 如果指定的新文件存在是否按出錯處理

);

CopyFileEx函數(shù)的附加功能是允許指定一個回調(diào)函數(shù),在拷貝過程中,函數(shù)每拷貝完一部分?jǐn)?shù)據(jù),就會調(diào)用回調(diào)函數(shù)。用戶在回調(diào)函數(shù)中可以指定是否停止拷貝,還可以顯示進(jìn)度條來指示拷貝的進(jìn)度。

2.刪除文件

刪除文件的函數(shù)是DeleteFile,僅有的參數(shù)是要刪除文件的名稱。

BOOL DeleteFile(LPCTSTR lpFileName);

如果應(yīng)用程序試圖刪除不存在的文件,DeleteFile將執(zhí)行失敗。如果目標(biāo)文件是只讀的,函數(shù)也會執(zhí)行失敗,出錯代碼為ERROR_ACCESS_DENIED。為了刪除只讀文件,先要去掉其只讀屬性。

DeleteFile函數(shù)可以標(biāo)識一個文件為“關(guān)閉時刪除”。因此,直到最后一個到此文件的句柄關(guān)閉之后,文件才會被刪除。

下面的自定義函數(shù)RecursiveDelete示例了如何刪除指定目錄下的所有文件和子目錄。

void RecursiveDelete(CString szPath)

{?????? CFileFind ff;?????? // MFC將查找文件的API封裝到了CFileFind類。讀者可參考下面的框架使用這個類

???????? CString strPath = szPath;

???????? // 說明要查找此目錄下的所有文件

???????? if(strPath.Right(1) != """")

?????????????????? strPath += """";

???????? strPath += "*.*";

???????? BOOL bRet;

???????? if(ff.FindFile(strPath))

???????? {?????? do

?????????????????? {?????? bRet = ff.FindNextFile();

??????????????????????????? if(ff.IsDots())? // 目錄為“.”或者“..”?

???????????????????????????????????? continue;

??????????????????????????? strPath = ff.GetFilePath();

??????????????????????????? if(!ff.IsDirectory())

??????????????????????????? {?????? // 刪除此文件

???????????????????????????????????? ::SetFileAttributes(strPath, FILE_ATTRIBUTE_NORMAL);

???????????????????????????????????? ::DeleteFile(strPath);

??????????????????????????? }

??????????????????????????? else

??????????????????????????? {?????? // 遞歸調(diào)用

???????????????????????????????????? RecursiveDelete(strPath);

???????????????????????????????????? // 刪除此目錄(RemoveDirectory只能刪除空目錄)

???????????????????????????????????? ::SetFileAttributes(strPath, FILE_ATTRIBUTE_NORMAL);

???????????????????????????????????? ::RemoveDirectory(strPath);

??????????????????????????? }

?????????????????? }

?????????????????? while(bRet);

???????? }

}

用DeleteFile函數(shù)刪除的文件不會被放到回收站,它們將永遠(yuǎn)丟失,所以請小心使用RecursiveDelete函數(shù)。

3.移動文件

移動文件的函數(shù)是MoveFile和MoveFileEx函數(shù)。它們的主要功能都是用來移動一個存在的文件或目錄。MoveFile函數(shù)用法如下:

BOOL MoveFile(

? LPCTSTR lpExistingFileName, // 存在的文件或目錄

? LPCTSTR lpNewFileName???????????????? // 新的文件或目錄

);

當(dāng)需要指定如何移動文件時,請使用MoveFileEx函數(shù)。

BOOL MoveFileEx(LPCTSTR lpExistingFileName, LPCTSTR lpNewFileName, DWORD dwFlags);

dwFlags參數(shù)可以是下列值的組合:

l????????? MOVEFILE_DELAY_UNTIL_REBOOT ???? 函數(shù)并不馬上執(zhí)行,而是在操作系統(tǒng)下一此重新啟動時才移動文件。在AUTOCHK執(zhí)行之后,系統(tǒng)立即移動文件,這是在創(chuàng)建任何分頁文件之前進(jìn)行的。因此,這個值使函數(shù)能夠刪除上一次運(yùn)行時使用的分頁文件。只有擁有管理員權(quán)限的用戶才可以使用這個值

l????????? MOVEFILE_REPLACE_EXISTING ??????????? 如果目標(biāo)文件已存在的話,就將它替換掉

l????????? MOVEFILE_WRITE_THROUGH???????????????? 直到文件實(shí)際從磁盤移除之后函數(shù)才返回

如果指定了MOVEFILE_DELAY_UNTIL_REBOOT標(biāo)記,lpNewFileName參數(shù)可以指定為NULL,這種情況下,當(dāng)系統(tǒng)下一次啟動時,操作系統(tǒng)會刪除lpExistingFileName參數(shù)指定的文件。

8.1.4 ?檢查PE文件有效性的例子

PE文件格式是任何可執(zhí)行模塊或者DLL的文件格式,PE文件以64字節(jié)的DOS文件頭(IMAGE_DOS_HEADER結(jié)構(gòu))開始,之后是一小段DOS程序,然后是248字節(jié)的NT文件頭(IMAGE_NT_HEADERS結(jié)構(gòu))。NT文件頭的偏移地址由IMAGE_DOS_HEADER結(jié)構(gòu)的e_lfanew成員給出。

檢查文件是不是有效PE文件的一個方法是檢查IMAGE_DOS_HEADER和IMAGE_NT_HEADERS結(jié)構(gòu)是否有效。IMAGE_DOS_HEADER結(jié)構(gòu)定義如下:

typedef struct _IMAGE_DOS_HEADER {?????

??? WORD?? e_magic;???????????????????? ? // DOS可執(zhí)行文件標(biāo)記,為“MZ”。依此識別DOS頭是否有效

???????? ...????????????????????????????????????????????????????????????? // 其他成員,沒什么用途

??? LONG?? e_lfanew;??????????????????? ?? // IMAGE_NT_HEADERS結(jié)構(gòu)的地址

? } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

IMAGE_NT_HEADERS結(jié)構(gòu)定義如下:

typedef struct _IMAGE_NT_HEADERS {?

???????? ???????? ???????? DWORD Signature;? ?????????? // PE文件標(biāo)識,為“PE"0"0”。依此識別NT文件頭是否有效

???????? ???????? ???????? IMAGE_FILE_HEADER FileHeader;?

???????? ???????? ???????? IMAGE_OPTIONAL_HEADER OptionalHeader;

???????? ???????? } IMAGE_NT_HEADERS,

為了編程方便,Windows為DOS文件標(biāo)記和PE文件標(biāo)記都定義了宏標(biāo)識。

#define IMAGE_DOS_SIGNATURE???????????????? 0x5A4D????? // MZ

#define IMAGE_NT_SIGNATURE????????????????? 0x00004550? ?? // PE00

檢查文件是否為PE文件的步驟如下:

(1)檢驗(yàn)文件頭部第一個字的值是否等于IMAGE_DOS_SIGNATURE,是則說明DOS MZ頭有效。

(2)一旦證明文件的DOS頭有效后,就可用e_lfanew來定位PE頭了。

(3)比較PE頭的第一個字是否等于IMAGE_NT_SIGNATURE。如果這個值也匹配,那么就認(rèn)為該文件是一個有效的PE文件。

下面是驗(yàn)證PE文件有效性的代碼,在配套光盤的08ValidPE工程下。

BOOL CMyApp::InitInstance()

{?????? // 彈出選擇文件對話框

???????? CFileDialog dlg(TRUE);

???????? if(dlg.DoModal() != IDOK)

?????????????????? return FALSE;

???????? // 打開檢查的文件

???????? HANDLE hFile = ::CreateFile(dlg.GetFileName(), GENERIC_READ,

?????????????????? FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

???????? if(hFile == INVALID_HANDLE_VALUE)

???????? ?????????????????? MessageBox(NULL, "無效文件!", "ValidPE", MB_OK);

???????? // 定義PE文件中的DOS頭和NT頭

???????? IMAGE_DOS_HEADER dosHeader;

???????? IMAGE_NT_HEADERS32 ntHeader;

???????? // 驗(yàn)證過程

???????? BOOL bValid = FALSE;

???????? DWORD dwRead;

???????? // 讀取DOS頭

???????? ::ReadFile(hFile, &dosHeader, sizeof(dosHeader), &dwRead, NULL);

???????? if(dwRead == sizeof(dosHeader))

???????? {?????? if(dosHeader.e_magic == IMAGE_DOS_SIGNATURE) // 是不是有效的DOS頭?

?????????????????? {?????? // 定位NT頭

??????????????????????????? if(::SetFilePointer(hFile, dosHeader.e_lfanew, NULL, FILE_BEGIN) != -1)

??????????????????????????? {?????? // 讀取NT頭

???????????????????????????????????? ::ReadFile(hFile, &ntHeader, sizeof(ntHeader), &dwRead, NULL);

???????????????????????????????????? if(dwRead == sizeof(ntHeader))

???????????????????????????????????? {?????? if(ntHeader.Signature == IMAGE_NT_SIGNATURE)????? // 是不是有效的NT頭

??????????????????????????????????????????????????????? bValid = TRUE;

???????????????????????????????????? }

??????????????????????????? }

?????????????????? }

???????? }

???????? // 顯示結(jié)果

???????? if(bValid)

?????????????????? MessageBox(NULL, "是一個PE格式的文件!", "ValidPE", MB_OK);

???????? else

?????????????????? MessageBox(NULL, "不是一個PE格式的文件!", "ValidPE", MB_OK);

???????? ::CloseHandle(hFile);

???????? return FALSE;

}

上述代碼簡單明確,先利用Windows定義的宏 IMAGE_DOS_SIGNATURE判斷DOS頭,比較DOS頭的e_magic字段;再通過DOS頭的e_lfanew字段定位到NT頭;最后檢查 NT頭的Signature字段是不是IMAGE_NT_SIGNATURE(即“PE"0"0”)。

8.1.5? MFC的支持(CFile類)

CFile是一個相當(dāng)簡單的封裝了一部分文件I/O 處理函數(shù)的類。它的成員函數(shù)用于打開和關(guān)閉文件、讀寫文件數(shù)據(jù)、刪除和重命名文件、取得文件信息。它的公開成員變量m_hFile保存了與CFile對象關(guān)聯(lián)的文件的文件句柄。一個受保護(hù)的CString類型的成員變量m_strFileName保存了文件的名稱。成員函數(shù)GetFilePath、 GetFileName和GetFileTitle能夠用來提取整個或者部分文件名。比如,如果完整的文件名是“C:"MyWork" File.txt”,GetFilePath返回整個字符串,GetFileName返回“File.txt”,GetFileTitle返回 “File”。

但是詳述這些函數(shù)就會忽略CFile類的特色,這就是用來寫數(shù)據(jù)到磁盤和從磁盤讀數(shù)據(jù)的函數(shù)。下面簡單介紹CFile類用法。

1.打開和創(chuàng)建文件

使用CFile類打開文件有兩種方法。

(1)構(gòu)造一個未初始化的CFile對象,調(diào)用CFile::Open函數(shù)。下面的部分代碼使用這個技術(shù)以讀寫權(quán)限打開一個名稱為File.txt的文件。

CFile file;

if(file.Open(_T ("File.txt"), CFile::modeReadWrite))

{?????? // 打開文件成功}

CFile::Open函數(shù)的返回值是BOOL類型的變量。如果打開文件出錯,還想進(jìn)一步了解出錯的原因,可以創(chuàng)建一個CFileException對象,傳遞它的地址到Open函數(shù)的第3個參數(shù)。

CFile file;

CFileException e;

if (file.Open(_T ("File.txt"), CFile::modeReadWrite, &e))

{?????? // 打開文件成功}

else

{?????? // 打開文件失敗,告訴用戶原因

???????? e.ReportError();

}

如果打開失敗,CFile::Open函數(shù)會使用描述失敗原因的信息初始化一個CFileException對象。ReportError成員函數(shù)基于這個信息顯示一個出錯對話框。可以通過檢查 CFileException類的公有成員m_cause找到導(dǎo)致這個錯誤的原因。

(2)使用CFile類的構(gòu)造函數(shù)。可以將創(chuàng)建文件對象和打開文件合并成一步,如下面代碼所示。

CFile file(_T ("File.txt"), CFile::modeReadWrite);

如果文件不能被打開,CFile的構(gòu)造函數(shù)會拋出一個CFileException異常。因此,使用CFile::CFile函數(shù)打開文件的代碼通常使用try和catch塊來捕獲錯誤。

try

{?????? CFile file(_T ("File.txt"), CFile::modeReadWrite);

}

catch(CFileException* e)

{?????? // 出錯了!

???????? e->ReportError();

???????? e->Delete();

}

刪除MFC拋出的異常是程序?qū)懽髡叩呢?zé)任,所以在程序中處理完異常之后要調(diào)用異常對象的Delete函數(shù)。

為了創(chuàng)建一個文件而不是打開一個存在的文件,要在CFile::Open或者CFile構(gòu)造函數(shù)的第二個參數(shù)中包含上CFile::modeCreate標(biāo)記,如下代碼所示。

CFile file(_T("File.txt"), CFile::modeReadWrite | CFile::modeCreate);

如果以這種方式創(chuàng)建的文件存在,它的長度會被截為0。為了在文件不存在時創(chuàng)建它,存在的時候僅打開而不截去,應(yīng)再包含上CFile::modeNoTruncate標(biāo)記,如下面代碼所示。

CFile file(_T("File.txt"), CFile::modeReadWrite | CFile::modeCreate | CFile::modeNoTruncate);

默認(rèn)情況下,由CFile::Open或 CFile::CFile打開的文件使用的是獨(dú)占模式,即CreateFile API中的第3個參數(shù)dwShareMode被設(shè)為了0。如果需要,在打開文件時也可以指定一個共享模式,以明確同意其他訪問此文件的操作。這里是4個可以選擇的共享模式:

l????????? CFile::shareDenyNone ??????? 不獨(dú)占這個文件

l????????? CFile::shareDenyRead ???????? 拒絕其他代碼對這個文件進(jìn)行讀操作

l????????? CFile::shareDenyWrite ??????? 拒絕其他代碼對這個文件進(jìn)行寫操作

l????????? CFile::shareExclusive ?????????? 拒絕其他代碼對這個文件進(jìn)行讀和寫操作(默認(rèn))

另外,還可以指定下面3個對象訪問類型中的一個:

l????????? CFile::modeReadWrite ??????? 請求讀寫訪問

l????????? CFile::modeRead ???????????????? 僅請求讀訪問

l????????? CFile::modeWrite ??????????????? 僅請求寫訪問

常用的做法是允許其他程序以只讀方式打開文件,但是拒絕它們寫入數(shù)據(jù)。

CFile file(_T("File.txt"), CFile::modeReadWrite | CFile::modeCreate | CFile::modeNoTruncate);

如果在上面的代碼執(zhí)行之前,文件已經(jīng)以可寫的方式打開了,這個調(diào)用將會失敗,CFile類會拋出CFileException異常,異常對象的m_cause成員等于CFileException::sharingViolation。

CFile類的成員函數(shù)Close會調(diào)用 CloseHandle API關(guān)閉應(yīng)用程序打開的文件對象句柄。如果句柄沒有關(guān)閉,類的析構(gòu)函數(shù)也會調(diào)用Close函數(shù)關(guān)閉它。顯式調(diào)用Close函數(shù)一般都是為了關(guān)閉當(dāng)前打開的文件,以便使用同樣的CFile對象打開另一個文件。

2.讀寫文件

CFile類中從文件中讀取數(shù)據(jù)的成員函數(shù)是Read。例如,下面的代碼申請了一塊4KB大小的文件I/O緩沖區(qū),每次從文件讀取4KB大小的數(shù)據(jù)。

BYTE buffer[4096];

CFile file (_T("File.txt"), CFile::modeRead);

DWORD dwBytesRemaining = file.GetLength();

while(dwBytesRemaining)

{?????? UINT nBytesRead = file.Read(buffer, sizeof(buffer));

???????? dwBytesRemaining -= nBytesRead;

}

文件中未讀取的字節(jié)數(shù)保存在 dwBytesRemaining變量里,此變量由CFile::GetLength返回的文件長度初始化。每次調(diào)用Read之后,從文件中讀取的字節(jié)數(shù)(nBytesRead)會從dwBytesRemaining變量里減去。直到dwBytesRemaining為0整個while循環(huán)才結(jié)束。

CFile類還提供了Write成員函數(shù)向文件寫入數(shù)據(jù),Seek成員函數(shù)移動文件指針,它們都和相關(guān)API一一對應(yīng)。可以通過跟蹤程序的執(zhí)行來查看這些函數(shù)的實(shí)現(xiàn)代碼。

posted @ 2008-03-20 08:44 Da Vinci 閱讀(191) | 評論 (0) |?編輯

.NET下午茶之二: 值類型與引用類型的區(qū)別(轉(zhuǎn)載)

使用值類型還是引用類型?結(jié)構(gòu)體(structs)還是類(class)?什么情況下兩者都可以使用?這并不是C++,你可以為任何類型的對象建立指針來引用他們。這也不是java,任何類型都自動聲明為引用類型。你需要想清楚你將要定義的類型會有怎樣的行為。首次能否選擇對是至關(guān)重要的,一旦你決定使用哪種類型,你就要承擔(dān)相應(yīng)的后果,因?yàn)槿绻愫竺嫘薷牧四阒岸x的類型將會給你的代碼帶來潛在的不連貫性。使用struct或class關(guān)鍵字來創(chuàng)建你的類型是件簡單的事情,但是在后面更新或修改這些你所定義的類型將需要做更多的工作。

選擇正確的類型而不是你所偏好的類型并不是件簡單的事情。正確的選擇取決于你期望如何使用你所定義的新類型。值類型不支持多態(tài)。它更傾向于用在為你的應(yīng)用程序來存儲數(shù)據(jù)上。引用類型支持多態(tài)而且應(yīng)該用在定義你程序的行為上。考慮以上你所定義的新類型將會用于哪種用途,然后根據(jù)正確的用途來決定你將要創(chuàng)建的類型。結(jié)構(gòu)體(struct)用于存儲數(shù)據(jù)。類(class)用于定義行為(beahavior)

值類型和引用類型之間的區(qū)別被加進(jìn).Net和C#中是因?yàn)樵贑++和Java中這些問題很普遍。在C++中,所有的參數(shù)和返回類型都是以值類型的方式傳遞的。通過值類型來傳遞具有不錯的效率,但它卻有一個問題:部分拷貝(partial copying)也有人稱作對象的切片(slicing the object)。你期望的是使用一個派生的對象,然而只有基類的部分被拷貝了。也就是你丟失了所有派生類對象中所保存的信息。甚至你對虛函數(shù)的調(diào)用也是基類的版本。

Java語言的對策是從語言中或多或少的消除值類型。所有用戶定義的類型都是引用類型。在Java語言中所有參數(shù)和返回類型都是以引用方式傳遞的。這種策略具有一致性的優(yōu)點(diǎn),但是它卻犧牲了效率,因?yàn)橛行╊愋透緵]有必要是多態(tài)的。因此Java程序員需要在堆上分配空間和最終對每個變量進(jìn)行垃圾回收。它們同樣需要為每個非引用類型而消耗額外的時間。所有的變量都是引用類型的。在C#中,你通過使用struct和class關(guān)鍵字來聲明你的類型是值類型還是引用類型。值類型應(yīng)是當(dāng)更小,更輕量的類型。引用類型組成了你的類的層次。這段例子分別使用值類型和引用類型來幫助你理解這兩者之間的區(qū)別。

在一開始,這個類型使用一個方法返回的值。

private MyData _myData; public MyData Foo() { return _myData; } ? // call it: MyData v = Foo(); TotalSum += v.Value;

如果MyData是值類型,它給v返回了一份值的拷貝。更多的,v是在棧上的。然而,如果MyData是一個引用類型,你傳遞了引用,也就是你把內(nèi)部數(shù)據(jù)輸出給外部變量。這樣會違反封裝的原則。

或者我們來考慮做個變化:

private MyData _myData; public MyData Foo() { return _myData.Clone( ) as MyData; } ? // call it: MyData v = Foo(); TotalSum += v.Value;

現(xiàn)在,v就是_myData原版的一個拷貝。作為一個引用類型,它在堆上創(chuàng)建了兩個對象。這樣你就不會再有暴露內(nèi)部數(shù)據(jù)的問題了。作為替代,你在堆上建立了額外的對象。如果v是一個局部變量,那么它很快就會變?yōu)槔槠铱寺?#xff08;clone)會強(qiáng)制進(jìn)行運(yùn)行時檢查。總的來說,這是一種沒有效率可言的做法。

用于從公共方法或?qū)傩詠慝@得數(shù)據(jù)的類型應(yīng)當(dāng)被定義為值類型。但是這并不等同于任何從公共方法返回的類型就是值類型。前面的代碼段假設(shè)MyData存儲了值,那么它的職責(zé)就是存儲這些值。

但是,考慮下面的代碼段。

private MyType _myType; public IMyInterface Foo() { return _myType as IMyInterface; } ? // call it: IMyInterface iMe = Foo(); iMe.DoWork( );

_myType變量依然是通過Foo方法獲得的返回值。但是這次不同的是,這次沒有訪問返回值的數(shù)據(jù),而是訪問了定義在接口中的方法。你訪問的并不是MyType的數(shù)據(jù)內(nèi)容,而是它的行為。它的行為是通過ImyInterface這個接口所體現(xiàn)的,也就是說它可以表現(xiàn)成其他不同的行為。在這個例子中,MyType應(yīng)當(dāng)是一個引用類型而不是值類型。MyType的職責(zé)包含了它的行為,而不是它的數(shù)據(jù)成員。

上面的代碼段向你展示了它們之間的區(qū)別:值類型存儲數(shù)據(jù)而引用類型定義行為。現(xiàn)在我們更深入的觀察它們在內(nèi)存中的存儲方式以及相關(guān)存儲模型所帶來性能上的問題。考慮這個類:

public class C { ? private MyType _a = new MyType( ); ? private MyType _b = new MyType( ); ? ? // Remaining implementation removed. } ? C var = new C();

上面的代碼里有多少個對象被創(chuàng)建了?它們分別是多大?它視情況而定,如果MyType是一個值類型,你只需要進(jìn)行一次分配。這次分配的大小就是 MyType大小的兩倍。然而,如果MyType是一個引用類型,你需要做3次分配:一次是為c對象來分配空間,大小是8字節(jié)(假設(shè)指針的大小是 32bit),剩下兩次則是為包含在c對象中的MyType對象。結(jié)果會有差別是因?yàn)橹殿愋褪且詢?nèi)聯(lián)(inline)方式存儲在對象內(nèi)的,而引用類型不是。每個引用類型的變量只保留一個對象的引用,而實(shí)際的存儲則是需要額外的空間。

為了徹底的闡明以上觀點(diǎn),現(xiàn)在來考慮一下這種情況的空間分配:

MyType [] var = new MyType[ 100 ];

如果MyType是值類型,一次空間分配將會產(chǎn)生MyType對象大小的一百倍空間。然而,如果MyType是引用類型,則只會發(fā)生一次空間分配。這個數(shù)組中的每一個元素都是null。在堆上分配大量的引用類型變量會使堆變得凌亂瑣碎而且降低運(yùn)行速度。如果你只是為了存儲數(shù)據(jù)的話,那么值類型將會是正確的選擇。

對使用值類型還是引用類型所做的討論是很有意義的,將一個值類型轉(zhuǎn)換成引用類型將會進(jìn)入更深層次的研究。考慮下面的情況:

public struct Employee { ? private string? _name; ? private int???? _ID; ? private decimal _salary; ? ? // Properties elided ? ? public void Pay( BankAccount b ) ? { ??? b.Balance += _salary; ? } }

上面的簡單類型包含了一個可以讓你支付自己員工的方法。一段時間過去了,系統(tǒng)運(yùn)行的相當(dāng)不錯,但是此后你開始定義不同級別的員工:推銷員得到傭金,而經(jīng)理得到獎金。你決定把Employee由值類型改為引用類型。

public class Employee { ? private string? _name; ? private int???? _ID; ? private decimal _salary; ? ? // Properties elided ? ? public virtual void Pay( BankAccount b ) ? { ??? b.Balance += _salary; ? } }

這個改變破壞了你的用戶所使用的大部分代碼。返回值變成了返回引用。以前參數(shù)傳遞的是值而現(xiàn)在傳遞的是引用。下面這一小段的代碼行為被徹底的改變了:

Employee e1 = Employees.Find( "CEO" ); e1.Salary += Bonus; // Add one time bonus. e1.Pay( CEOBankAccount );

每一次加獎金都將是永久的增加。以前通過值拷貝而如今被引用所替代。編譯器很樂于為你做這些事情。CEO肯定也很高興。但另一方面CFO會匯報這個bug。當(dāng)面對這樣的實(shí)際情況時你就不能產(chǎn)生使用引用類型來代替值類型的想法了:因?yàn)樗淖兞诵袨椤?

之所以會產(chǎn)生這樣的問題時因?yàn)镋mployee類型不再遵循使用值類型的原則。你所定義的Employee類型不僅儲存了有關(guān)員工的數(shù)據(jù)信息,而且還定義了它的行為,在這個例子中它具有支付員工的行為。這些職責(zé)是屬于類(class)的范疇。類可以使得實(shí)現(xiàn)公共職能多態(tài)化更加簡單;而結(jié)構(gòu)體就無法做到,它應(yīng)當(dāng)被限制為只用于數(shù)據(jù)存儲。

.Net的支持文檔里推薦根據(jù)類型的大小來考慮使用值類型或是引用類型。在實(shí)際中,值類型善于用在類型易于構(gòu)造或用于攜帶數(shù)據(jù)的情況下。在某些方面值類型更加利于內(nèi)存的管理:產(chǎn)生更少的堆碎片、更少的垃圾。最重要的是當(dāng)值類型從方法或?qū)傩苑祷貢r使用的是拷貝。這就避免了暴露內(nèi)部數(shù)據(jù)結(jié)構(gòu)的風(fēng)險。但是你也將會為這些特點(diǎn)付出一定的代價。值類型在支持面相對象的技術(shù)方面有很大的限制,你無法通過值類型來簡直具有層次的對象,你只能把所有的值類型的對象看成封閉(sealed)的來考慮。你可以用值類型來實(shí)現(xiàn)接口,但是那需要采用拆裝箱技術(shù),這將會帶來性能上的損失。考慮值類型只作為存儲數(shù)據(jù)的容器,而不具有面相對象的意義。

你還是會更加常用到引用類型。如果你能肯定的回答以下的這些問題,那么你就可以放心的使用值類型。比較之前的Employee的例子來考慮下面這些問題:

1. 你所要聲明的類型是否只承擔(dān)存儲數(shù)據(jù)的責(zé)任。

2. 你所要聲明的類型的數(shù)據(jù)訪問接口是否全部定義為了屬性(properties)。

3. 你是否確定它永遠(yuǎn)都不會有派生類。

4. 你是否確定它永遠(yuǎn)都不會被看作多態(tài)對待。

構(gòu)建低等級的數(shù)據(jù)存儲類型時使用值類型。構(gòu)建具有行為的程序時使用引用類型。你從你創(chuàng)建的類的對象中安全獲得數(shù)據(jù)拷貝。以棧作為基礎(chǔ)并使用內(nèi)聯(lián)的值存儲方式更加有益與內(nèi)存的利用,你可以利用面相對象的標(biāo)準(zhǔn)技術(shù)來建立你引用程序的邏輯。當(dāng)你疑惑該使用哪種類型時,使用引用類型吧!

參考:http://blog.csdn.net/knight94/archive/2006/07/01/861383.aspx

posted @ 2008-03-20 08:37 Da Vinci 閱讀(19) | 評論 (3) |?編輯

(From MS)Vista: 啟動配置數(shù)據(jù)編輯器(BCD)

什么是 BCD 存儲?

啟動配置數(shù)據(jù) (BCD) 存儲包含啟動配置參數(shù),并控制 Microsoft? Windows?Vista? 操作系統(tǒng)和代號為“Longhorn”的 Microsoft? Windows?Server? 操作系統(tǒng)的啟動方式。這些參數(shù)以前位于 Boot.ini 文件(在基于 BIOS 的操作系統(tǒng)中)或穩(wěn)定 RAM (NVRAM) 條目中(在基于可擴(kuò)展固件接口的操作系統(tǒng)中)。您可以使用 Bcdedit.exe 命令行工具在 BCD 存儲中添加、刪除、編輯和追加條目,以影響在預(yù)操作系統(tǒng)環(huán)境中運(yùn)行的 Windows? 代碼。Bcdedit.exe 位于 Windows?Vista 分區(qū)的 "Windows"System32 目錄下。

??注意

雖然本文檔主要介紹 Windows?Vista 相關(guān)內(nèi)容,但此信息同樣適用于 Windows?Server“Longhorn”。

??注意

要在命令提示符下獲得詳細(xì)的命令和選項(xiàng)信息,請鍵入 bdedit.exe /?命令。例如,鍵入 bcdedit.exe /?CREATESTORE

為什么從 Boot.ini 改為 BCD?

創(chuàng)建 BCD 的目的是為描述啟動配置數(shù)據(jù)提供一種改進(jìn)機(jī)制。隨著新固件模型的不斷涌現(xiàn)(例如,可擴(kuò)展固件接口 (EFI)),人們需要一種可擴(kuò)展和可互操作的接口來實(shí)現(xiàn)基礎(chǔ)固件的抽象化。這一全新設(shè)計為 Windows?Vista 中的各種新功能(例如,啟動修復(fù)工具和多用戶安裝快捷方式)提供了支持基礎(chǔ)。

BCD 文件位于注冊表中的哪一位置?

  • 基于 BIOS 的操作系統(tǒng)。BCD 注冊表文件位于活動分區(qū)的 "Boot"Bcd 目錄下。
  • 基于 EFI 的操作系統(tǒng)。BCD 注冊表文件位于 EFI 系統(tǒng)分區(qū)中。

是否任何用戶都可以修改 BCD?

否。您需要提供管理憑據(jù)才能修改 BCD。

可以通過哪些方式修改 BCD?

根據(jù)您要更改的內(nèi)容,可以使用下列工具修改 BCD:

  • 啟動和故障恢復(fù)。如果您的計算機(jī)上安裝了多個操作系統(tǒng),則通過“啟動和故障恢復(fù)”。
  • 系統(tǒng)配置實(shí)用程序 (Msconfig.exe)。Msconfig.exe 是一款更高級的工具,其功能包含下列選項(xiàng):/debug/safeboot/bootlog/noguiboot/basevideo/numproc
  • BCD WMI 提供程序。BCD Windows Management Instrumentation (WMI) 提供程序是一個管理接口,可用于編寫修改 BCD 的實(shí)用程序腳本。這是唯一可用于 BCD 的編程接口。有關(guān)詳細(xì)信息,請參閱 Microsoft 網(wǎng)站上的“啟動配置數(shù)據(jù) (BCD)”(http://go.microsoft.com/fwlink/?LinkId=56792)。
  • BCDEdit.exe。BCDEdit.exe 是 Windows?Vista 中取代 Bootcfg.exe 的命令行實(shí)用程序。有關(guān)詳細(xì)信息,請參閱使用 Bcdedit.exe 可執(zhí)行哪些操作?

為什么在 EFI 啟動管理器中看不到任何 Windows 條目?為什么有兩個啟動管理器?

所有 Windows 條目都存儲在 BCD 存儲中。在基于 EFI 的操作系統(tǒng)中,EFI 固件啟動管理器中只有一個名為“Windows 啟動管理器”的條目。此文件位于 "EFI"Microsoft"Boot"Bootmgfw.efi。如果使用 EFI 啟動管理器啟動 Windows 啟動管理器,則基于 EFI 的操作系統(tǒng)和基于 PC/AT 的操作系統(tǒng)將提供相同的外觀和用戶體驗(yàn)。例如,高級啟動選項(xiàng)菜單均可供使用。EFI 啟動管理器的默認(rèn)超時值為 2 秒,以便能夠在 Windows Server?2003 (Service Pack?1) 與 Windows?Vista 之間更輕松地進(jìn)行啟動切換。

返回頁首

多重引導(dǎo)環(huán)境

是否可以在已經(jīng)包含某個操作系統(tǒng)的計算機(jī)上安裝 Windows Vista?

可以。您可以將 Windows?Vista 安裝在另一個分區(qū)上。最好在安裝舊版操作系統(tǒng)之后安裝 Windows?Vista。舊版操作系統(tǒng)將繼續(xù)使用 Boot.ini 來進(jìn)行啟動配置。

在 Windows Vista 中,是否可以將過去使用 Boot.ini 的代碼替換為現(xiàn)在使用 BCD?

不可以。您需要將代碼改為針對舊版操作系統(tǒng)使用 Boot.ini,而針對 Windows?Vista 使用 BCD。

在多重引導(dǎo)環(huán)境中,在 Windows Vista 之前的操作系統(tǒng)上修改 BCD 是否會修改啟動配置?

不會。您需要修改 BCD 以更改 Windows?Vista 的啟動配置。但要更改舊版操作系統(tǒng)的啟動配置,則還需要修改 Boot.ini(如果是基于 BIOS 的操作系統(tǒng))或 NVRAM(如果是基于 EFI 的操作系統(tǒng))。

如果不引導(dǎo)到 Windows Vista,是否可以完全禁用 BCD?

不可以。因?yàn)槭紫葧\(yùn)行 Windows?Vista 的啟動管理器以確定要啟動哪個操作系統(tǒng)。因此,如果希望引導(dǎo)到舊版操作系統(tǒng),則必須在 BCD 存儲中將默認(rèn)順序設(shè)置為舊版操作系統(tǒng)。有關(guān)詳細(xì)信息,請參閱如何更改默認(rèn)操作系統(tǒng)條目

返回頁首

BCDedit.exe

什么是 Bcdedit.exe?

您可以使用 Bcdedit.exe 在 BCD 存儲中添加、刪除、編輯和追加條目,以修改在預(yù)操作系統(tǒng)環(huán)境中運(yùn)行的 Windows 代碼。Bcdedit.exe 位于 Windows?Vista 分區(qū)的 "Windows"System32 目錄下。

使用 Bcdedit.exe 可以執(zhí)行哪些操作?

Bcdedit.exe 目前使您能夠執(zhí)行下列操作:

  • 為稍后安裝 Windows?Server“Longhorn”創(chuàng)建一個 BCD 存儲。
  • 向現(xiàn)有 BCD 存儲中添加條目。
  • 修改 BCD 存儲中的現(xiàn)有條目。
  • 刪除 BCD 存儲中的條目。
  • 將條目導(dǎo)出到 BCD 存儲。
  • 導(dǎo)入來自 BCD 存儲的條目。
  • 列出當(dāng)前處于活動狀態(tài)的設(shè)置。
  • 查詢特定類型的條目。
  • (向所有條目)應(yīng)用全局更改。
  • 更改默認(rèn)超時值。

當(dāng)運(yùn)行 bcdedit /enum 時,為什么會得到一個 Windows 啟動管理器條目、若干 Windows 啟動加載器條目和一個舊條目?

啟動環(huán)境分為兩個類別:Windows 啟動管理器和在啟動環(huán)境中運(yùn)行的各種啟動應(yīng)用程序。Windows 啟動管理器實(shí)質(zhì)上是一個微型操作系統(tǒng),可控制您的啟動體驗(yàn)并使您能夠選擇要運(yùn)行的啟動應(yīng)用程序。啟動應(yīng)用程序有很多種(例如 Windows 啟動加載器),并且每種啟動應(yīng)用程序所執(zhí)行的任務(wù)都有所不同。例如,Windows 啟動加載器應(yīng)用程序?qū)⒓虞d Windows。

如果指定 /enum 時,您將獲得以下內(nèi)容:

  • 一個 Windows 啟動管理器條目(因?yàn)橹挥幸粋€啟動管理器)。
  • 適用于計算機(jī)上安裝的每個 Windows?Vista 操作系統(tǒng)的 Windows 啟動加載器應(yīng)用程序。例如,如果您在不同分區(qū)上安裝了兩個不同版本的 Windows?Vista,就會看到兩個 Windows 啟動加載器條目。
  • 一 個舊條目。此條目并不是啟動應(yīng)用程序,但它使用 NTLDR 和 Boot.ini 引導(dǎo)至 Windows?Vista 之前的操作系統(tǒng)。您可以使用此條目引導(dǎo)至 Windows Server?2003、Windows XP 或其他早期操作系統(tǒng)(如果計算機(jī)上安裝了該操作系統(tǒng))。

Bcdedit.exe 是否具有命令行幫助?

有。要在命令提示符下獲得詳細(xì)的命令和選項(xiàng)信息,請鍵入 bdedit.exe /?bdedit.exe /?命令。例如,鍵入 bcdedit.exe /?CREATESTORE

返回頁首

執(zhí)行熟悉任務(wù)的新方式

如何更改全局 zf 設(shè)置

在命令提示符下鍵入:

bcdedit /dbgsettingsDebugType[debugport:Port] [baudrate:Baud]

[channel:Channel] [targetname:TargetName]

?
選項(xiàng)說明

DebugType

指定調(diào)試器的類型。DebugType 可以是 SERIAL、1394 或 USB 之一。其余選項(xiàng)

取決于所選的調(diào)試器類型。

Port

用于 SERIAL 調(diào)試,指定用作調(diào)試端口的串行端口。

Baud

用于 SERIAL 調(diào)試,指定調(diào)試使用的波特率。

Channel

用于 1394 調(diào)試,指定調(diào)試使用的 1394 通道。

TargetName

用于通用串行總線 (USB) 調(diào)試,指定調(diào)試使用的 USB 目標(biāo)名稱。

示例

以下命令將指定條目設(shè)為默認(rèn)

啟動管理器條目:

bcdedit /default {cbd971bf-b7b8-4885-951a-fa03044f5d71}

以下命令將舊 Windows 加載器 (Ntldr) 設(shè)為

默認(rèn)條目:{466f5a88-0af2-4f76-9038-095b170dc21c} 是 Ntldr 的預(yù)定義 GUID。

bcdedit /default {466f5a88-0af2-4f76-9038-095b170dc21c}

如何更改下一次重新啟動的啟動順序

在命令提示符下鍵入:

bcdedit /bootsequence {ID} {ID} {ID} …

?
選項(xiàng)說明

ID

指定構(gòu)成下一次重新啟動的啟動順序的 GUID。 在此一次性啟動過后,它將還原為默認(rèn)啟動順序。

示例

以下命令在啟動管理器顯示順序中設(shè)置三個操作系統(tǒng)條目:

Bcdedit.exe /displayorder {c84b751a-ff09-11d9-9e6e-0030482375e6} {c74b751a-ff09-11d9-9e6e-0030482375e4} {c34b751a-ff09-11d9-9e6e-0030482375e7}

以下命令在啟動管理器顯示順序中設(shè)置兩個操作系統(tǒng)條目和舊 Windows 加載器:

bcdedit /displayorder {802d5e32-0784-11da-bd33-000476eba25f}

以下命令在啟動菜單顯示順序的最后添加由 GUID 表示的條目:

bcdedit.exe /displayorder {c84b751a-ff09-11d9-9e6e-0030482375e6}-addlast

如何刪除啟動項(xiàng)目

在命令提示符下鍵入:

bcdedit /delete ID [/f]

?
選項(xiàng)說明

ID

指定要刪除的啟動項(xiàng)目的 GUID。如果不指定 ID,則刪除當(dāng)前啟動項(xiàng)目 ID。

如果指定一個已知 GUID,則必須通過指定 /f 強(qiáng)制進(jìn)行刪除。例如:

bcdedit /delete {default} /f

示例

以下命令將列出所有操作系統(tǒng)加載器啟動項(xiàng)目:

bcdedit /enum osloader

以下命令將列出所有啟動管理器條目:

bcdedit /enum bootmgr

在運(yùn)行 Windows Vista 的計算機(jī)上安裝舊版 Windows 時如何修改 BCD

要在運(yùn)行 Windows?Vista 的計算機(jī)上安裝舊版 Windows 操作系統(tǒng),請使用以下過程。

在運(yùn)行 Windows Vista 的計算機(jī)上安裝舊版 Windows

  • 安裝舊版 Windows。
  • 登錄到舊版操作系統(tǒng),并通過運(yùn)行以下命令還原最新的啟動管理器。Fixntfs.exe 將位于活動分區(qū) fixntfs /lh 的 "boot 目錄下。
  • 通過指定以下內(nèi)容,為舊版操作系統(tǒng)創(chuàng)建一個 BCD 條目。Bcdedit.exe 位于 Windows?Vista 分區(qū)的 "Windows"System32 目錄下。Description 是對舊版操作系統(tǒng)中新條目的描述。

    Bcdedit /create {legacy} /d “Description

    Bcdedit /set {legacy} device boot

    Bcdedit /set {legacy} path "ntldr

    Bcdedit /displayorder {legacy} /addlast

  • 重新啟動計算機(jī)以使更改生效。
  • 如何創(chuàng)建一個可從硬盤啟動 WIM 映像的條目

    要創(chuàng)建一個可啟動 Windows 映像格式 (WIM) 映像的條目,您需要創(chuàng)建一個 OSloader 類型的條目,并帶有指向啟動分區(qū)的 RAMDISK 選項(xiàng)。為此,請使用以下過程。在此過程中,arcpath multi(0)disk(0)rdisk(0)partition(1) 是指計算機(jī)上的 C: 驅(qū)動器,Boot.wim 是一個常規(guī) Boot.wim,其中 Winload.exe 位于該 WIM 映像的 System32 文件夾中。

    創(chuàng)建一個可從硬盤啟動 WIM 映像的條目

  • 通過指定以下內(nèi)容,在您的 BCD 存儲中創(chuàng)建 {ramdisktoptions} 對象。Drive 應(yīng)是包含該映像的驅(qū)動器。

    bcdedit /create {ramdiskoptions} /d "Ramdisk options"

    bcdedit /set {ramdiskoptions} ramdisksdidevice partition=Drive

    bcdedit /set {ramdiskoptions} ramdisksdipath "boot"boot.sdi

  • 通過指定以下內(nèi)容,創(chuàng)建新的啟動應(yīng)用程序條目:

    bcdedit /create /d "Boot from WIM" /application OSLOADER

  • 這將為新創(chuàng)建的條目返回一個標(biāo)識符 (GUID)。此過程的其他部分將使用 {GUID} 指代該新條目。接下來指定以下內(nèi)容:

    bcdedit /set {GUID} device ramdisk=[c:]"sources"boot.wim,{ramdiskoptions}

    bcdedit /set {GUID} path "windows"system32"winload.exe

    bcdedit /set {GUID} osdevice ramdisk=[c:]"sources"boot.wim,{ramdiskoptions}

    bcdedit /set {GUID} systemroot "windows

  • 如果要引導(dǎo)到 Windows 預(yù)安裝環(huán)境 (Windows PE),則還需要指定:

    bcdedit /set {GUID} winpe yes

    bcdedit /set {GUID} detecthal yes

  • 繼續(xù)指定以下內(nèi)容,將新條目添加到顯示順序中:

    bcdedit /displayorder {GUID} /addlast

  • 如何更改特定條目的調(diào)試器設(shè)置

    要覆蓋特定調(diào)試器設(shè)置的全局條目,請鍵入以下命令之一。

    ??注意

    此命令不會為特定啟動項(xiàng)目啟用或禁用調(diào)試器。

    • 要設(shè)置串行調(diào)試,請鍵入:

      bcdedit /set {GUID} debugtype:serial

      bcdedit /set {GUID} baudrate:Baudrate

      bcdedit /set {GUID} debugport:Port

    • 要設(shè)置 USB 調(diào)試,請鍵入:

      bcdedit /set {GUID} debugtype:usbbcdedit /set {GUID} targetname:debugging

    • 要設(shè)置 1394 調(diào)試,請鍵入:

      bcdedit /set {GUID} debugtype:1394bcdedit /set {GUID} targetname:32

    示例

    以下命令將 c74b751a-ff09-11d9-9e6e-0030482375e4 的調(diào)試器設(shè)置設(shè)為以 115,200 的波特率在 com1 上串行調(diào)試:

    Bcdedit /set {c74b751a-ff09-11d9-9e6e-0030482375e4} debugtype:serial

    Bcdedit /set {c74b751a-ff09-11d9-9e6e-0030482375e4} baudrate:115200

    Bcdedit /set {c74b751a-ff09-11d9-9e6e-0030482375e4} debugport:1

    posted @ 2008-03-20 08:33 Da Vinci 閱讀(115) | 評論 (0) |?編輯

    Windows: "net use" command introduction

    1)建立空連接:
    net use ""IP"ipc$ "" /user:"" (一定要注意:這一行命令中包含了3個空格)

    2)建立非空連接:
    net use ""IP"ipc$ "密碼" /user:"用戶名" (同樣有3個空格)

    3)映射默認(rèn)共享:
    net use z: ""IP"c$ "密碼" /user:"用戶名" (即可將對方的c盤映射為自己的z盤,其他盤類推)
    如果已經(jīng)和目標(biāo)建立了ipc$,則可以直接用IP+盤符+$訪問,具體命令 net use z: ""IP"c$

    4)刪除一個ipc$連接
    net use ""IP"ipc$ /del

    5)刪除共享映射
    net use c: /del 刪除映射的c盤,其他盤類推
    net use * /del 刪除全部,會有提示要求按y確認(rèn)

    3 查看遠(yuǎn)程主機(jī)的共享資源(但看不到默認(rèn)共享)
    net view ""IP

    4 查看本地主機(jī)的共享資源(可以看到本地的默認(rèn)共享)
    net share

    5 得到遠(yuǎn)程主機(jī)的用戶名列表
    nbtstat -A IP

    6 得到本地主機(jī)的用戶列表
    net user

    7 查看遠(yuǎn)程主機(jī)的當(dāng)前時間
    net time ""IP

    8 顯示本地主機(jī)當(dāng)前服務(wù)
    net start

    9 啟動/關(guān)閉本地服務(wù)
    net start 服務(wù)名 /y
    net stop 服務(wù)名 /y

    10 映射遠(yuǎn)程共享:
    net use z: ""IP"baby
    此命令將共享名為baby的共享資源映射到z盤

    11 刪除共享映射
    net use c: /del 刪除映射的c盤,其他盤類推
    net use * /del /y刪除全部

    12 向遠(yuǎn)程主機(jī)復(fù)制文件
    copy "路徑"srv.exe ""IP"共享目錄名,如:
    copy ccbirds.exe ""*.*.*.*"c 即將當(dāng)前目錄下的文件復(fù)制到對方c盤內(nèi)

    13 遠(yuǎn)程添加計劃任務(wù)
    at ""ip 時間 程序名,如:
    at ""127.0.0.0 11:00 love.exe
    注意:時間盡量使用24小時制;在系統(tǒng)默認(rèn)搜索路徑(比如system32/)下不用加路徑,否則必須加全路徑
    14 開啟遠(yuǎn)程主機(jī)的telnet
    這里要用到一個小程序:opentelnet.exe,各大下載站點(diǎn)都有,而且還需要滿足四個要求:

    1)目標(biāo)開啟了ipc$共享
    2)你要擁有管理員密碼和帳號
    3)目標(biāo)開啟RemoteRegistry服務(wù),用戶就該ntlm認(rèn)證
    4)對WIN2K/XP有效,NT未經(jīng)測試
    命令格式:OpenTelnet.exe ""server account psw NTLM認(rèn)證方式 port
    試?yán)缦?#xff1a;c:">OpenTelnet.exe ""*.*.*.* administrator "" 1 90

    15 激活用戶/加入管理員組
    1 net uesr account /active:yes
    2 net localgroup administrators account /add

    16 關(guān)閉遠(yuǎn)程主機(jī)的telnet
    同樣需要一個小程序:ResumeTelnet.exe
    命令格式:ResumeTelnet.exe ""server account psw
    試?yán)缦?#xff1a;c:">ResumeTelnet.exe ""*.*.*.* administrator ""

    17 刪除一個已建立的ipc$連接
    net use ""IP"ipc$ /del

    posted @ 2008-03-20 08:27 Da Vinci 閱讀(27) | 評論 (0) |?編輯

    2008年3月19日

    .NET下午茶之一: CLR/CTS/CLS

    1. CTS Common Type System:公共類型系統(tǒng)。《Practical .NET2.0 and C#2.0》的解釋是:CTS是.NET平臺的一組類型。這組類型獨(dú)立于編寫它們的源代碼語言。CTS是一種規(guī)范,它描述了每一個能被CLR識別的類型的特征。CTS是定義公共語言運(yùn)行庫在聲明、使用和管理類型時所遵循的規(guī)則的模型。CTS有值類型、引用類型和指針類型組成。
    2. CLS Common Language Specification:公共語言規(guī)范。一組可以以編程方式驗(yàn)證的規(guī)則,這組規(guī)范控制用不同編程語言編寫的類型的交互操作。.NET程序員利用CLS 來保證可從多種編程語言調(diào)用他們的 API。CLS是CLR/CTS的子集,即某些.NET編程語言可以存在滿足CLS定義的部分,也可以包含不滿足CLS定義的部分。例如C#語言的有符號整型包含在CLS中,但無符號整型卻不是CLS的部分。
    3. CLR Common Language Runtime:公共語言運(yùn)行時。CLR是整個.NET平臺架構(gòu)的中心元素,是管理所有.NET程序的軟件層。實(shí)際上CLR是運(yùn)行時駐留在內(nèi)存中的一段代碼,負(fù)責(zé)IL代碼編譯為機(jī)器語言、異常管理、垃圾回收、加載程序集、解析類型等操作。托管就是由它來托管。它類似于Java中的JVM(虛擬機(jī))。

    posted @ 2008-03-19 21:39 Da Vinci 閱讀(13) | 評論 (0) |?編輯

    (轉(zhuǎn)載)C++字符串完全指引之一 —— Win32 字符編碼

    C++字符串完全指引之一 —— Win32 字符編碼

    原著:Michael Du

    原文出處:CodeProject:The Complete Guide to C++ Strings, Part I

    引言

    毫無疑問,我們都看到過像 TCHAR, std::string, BSTR 等各種各樣的字符串類型,還有那些以 _tcs 開頭的奇怪的宏。你也許正在盯著顯示器發(fā)愁。本指引將總結(jié)引進(jìn)各種字符類型的目的,展示一些簡單的用法,并告訴您在必要時,如何實(shí)現(xiàn)各種字符串類型之間的轉(zhuǎn)換。
    在第一部分,我們將介紹3種字符編碼類型。了解各種編碼模式的工作方式是很重要的事情。即使你已經(jīng)知道一個字符串是一個字符數(shù)組,你也應(yīng)該閱讀本部分。一旦你了解了這些,你將對各種字符串類型之間的關(guān)系有一個清楚地了解。
    在第二部分,我們將單獨(dú)講述string類,怎樣使用它及實(shí)現(xiàn)他們相互之間的轉(zhuǎn)換。

    字符基礎(chǔ) -- ASCII, DBCS, Unicode

    所有的 string 類都是以C-style字符串為基礎(chǔ)的。C-style 字符串是字符數(shù)組。所以我們先介紹字符類型。這里有3種編碼模式對應(yīng)3種字符類型。第一種編碼類型是單子節(jié)字符集(single-byte character set or SBCS)。在這種編碼模式下,所有的字符都只用一個字節(jié)表示。ASCII是SBCS。一個字節(jié)表示的0用來標(biāo)志SBCS字符串的結(jié)束。
    第二種編碼模式是多字節(jié)字符集(multi-byte character set or MBCS)。一個MBCS編碼包含一些一個字節(jié)長的字符,而另一些字符大于一個字節(jié)的長度。用在Windows里的MBCS包含兩種字符類型,單字節(jié)字符(single-byte characters)和雙字節(jié)字符(double-byte characters)。由于Windows里使用的多字節(jié)字符絕大部分是兩個字節(jié)長,所以MBCS常被用DBCS代替。
    在DBCS編碼模式中,一些特定的值被保留用來表明他們是雙字節(jié)字符的一部分。例如,在Shift-JIS編碼中(一個常用的日文編碼模式),0x81-0x9f之間和 0xe0-oxfc之間的值表示"這是一個雙字節(jié)字符,下一個子節(jié)是這個字符的一部分。"這樣的值被稱作"leading bytes",他們都大于0x7f。跟隨在一個leading byte子節(jié)后面的字節(jié)被稱作"trail byte"。在DBCS中,trail byte可以是任意非0值。像SBCS一樣,DBCS字符串的結(jié)束標(biāo)志也是一個單字節(jié)表示的0。
    第三種編碼模式是Unicode。Unicode是一種所有的字符都使用兩個字節(jié)編碼的編碼模式。Unicode字符有時也被稱作寬字符,因?yàn)樗葐巫庸?jié)字符寬(使用了更多的存儲空間)。注意,Unicode不能被看作MBCS。MBCS的獨(dú)特之處在于它的字符使用不同長度的字節(jié)編碼。Unicode 字符串使用兩個字節(jié)表示的0作為它的結(jié)束標(biāo)志。
    單字節(jié)字符包含拉丁文字母表,accented characters及ASCII標(biāo)準(zhǔn)和DOS操作系統(tǒng)定義的圖形字符。雙字節(jié)字符被用來表示東亞及中東的語言。Unicode被用在COM及Windows NT操作系統(tǒng)內(nèi)部。
    你一定已經(jīng)很熟悉單字節(jié)字符。當(dāng)你使用char時,你處理的是單字節(jié)字符。雙字節(jié)字符也用char類型來進(jìn)行操作(這是我們將會看到的關(guān)于雙子節(jié)字符的很多奇怪的地方之一)。Unicode字符用wchar_t來表示。Unicode字符和字符串常量用前綴L來表示。例如:

    wchar_t wch = L''1''; // 2 bytes, 0x0031
    wchar_t* wsz = L"Hello"; // 12 bytes, 6 wide characters

    字符在內(nèi)存中是怎樣存儲的

    單字節(jié)字符串:每個字符占一個字節(jié)按順序依次存儲,最后以單字節(jié)表示的0結(jié)束。例如。"Bob"的存貯形式如下:

    426F6200
    BobBOS

    Unicode的存儲形式,L"Bob"

    42 00 6F 0062 0000 00
    BobBOS

    使用兩個字節(jié)表示的0來做結(jié)束標(biāo)志。

    一眼看上去,DBCS 字符串很像 SBCS 字符串,但是我們一會兒將看到 DBCS 字符串的微妙之處,它使得使用字符串操作函數(shù)和永字符指針遍歷一個字符串時會產(chǎn)生預(yù)料之外的結(jié)果。字符串" " ("nihongo")在內(nèi)存中的存儲形式如下(LB和TB分別用來表示 leading byte 和 trail byte)

    93 FA96 7B8C EA00
    LB TBLB TBLB TBEOS
    EOS

    值得注意的是,"ni"的值不能被解釋成WORD型值0xfa93,而應(yīng)該看作兩個值93和fa以這種順序被作為"ni"的編碼。

    使用字符串處理函數(shù)

    我們都已經(jīng)見過C語言中的字符串函數(shù),strcpy(), sprintf(), atoll()等。這些字符串只應(yīng)該用來處理單字節(jié)字符字符串。標(biāo)準(zhǔn)庫也提供了僅適用于Unicode類型字符串的函數(shù),比如wcscpy(), swprintf(), wtol()等。
    微軟還在它的CRT(C runtime library)中增加了操作DBCS字符串的版本。Str***()函數(shù)都有對應(yīng)名字的DBCS版本_mbs***()。如果你料到可能會遇到DBCS 字符串(如果你的軟件會被安裝在使用DBCS編碼的國家,如中國,日本等,你就可能會),你應(yīng)該使用_mbs***()函數(shù),因?yàn)樗麄円部梢蕴幚鞸BCS 字符串。(一個DBCS字符串也可能含有單字節(jié)字符,這就是為什么_mbs***()函數(shù)也能處理SBCS字符串的原因)
    讓我們來看一個典型的字符串來闡明為什么需要不同版本的字符串處理函數(shù)。我們還是使用前面的Unicode字符串 L"Bob":

    42 00 6F 0062 0000 00
    BobBOS

    因?yàn)閤86CPU是little-endian,值0x0042在內(nèi)存中的存儲形式是42 00。你能看出如果這個字符串被傳給strlen()函數(shù)會出現(xiàn)什么問題嗎?它將先看到第一個字節(jié)42,然后是00,而00是字符串結(jié)束的標(biāo)志,于是 strlen()將會返回1。如果把"Bob"傳給wcslen(),將會得出更壞的結(jié)果。wcslen()將會先看到0x6f42,然后是 0x0062,然后一直讀到你的緩沖區(qū)的末尾,直到發(fā)現(xiàn)00 00結(jié)束標(biāo)志或者引起了GPF。
    到目前為止,我們已經(jīng)討論了str***()和wcs***()的用法及它們之間的區(qū)別。Str***()和_mbs**()之間的有區(qū)別區(qū)別呢?明白他們之間的區(qū)別,對于采用正確的方法來遍歷DBCS字符串是很重要的。下面,我們將先介紹字符串的遍歷,然后回到str***()與_mbs***() 之間的區(qū)別這個問題上來。

    正確的遍歷和索引字符串

    因?yàn)槲覀冎写蠖鄶?shù)人都是用著SBCS字符串成長的,所以我們在遍歷字符串時,常常使用指針的++-和-操作。我們也使用數(shù)組下標(biāo)的表示形式來操作字符串中的字符。這兩種方式是用于SBCS和Unicode字符串,因?yàn)樗鼈冎械淖址兄嗤膶挾?#xff0c;編譯器能正確的返回我們需要的字符。
    然而,當(dāng)碰到DBCS字符串時,我們必須拋棄這些習(xí)慣。這里有使用指針遍歷DBCS字符串時的兩條規(guī)則。違背了這兩條規(guī)則,你的程序就會存在DBCS有關(guān)的bugs。

  • 1.在前向遍歷時,不要使用++操作,除非你每次都檢查lead byte;
  • 2.永遠(yuǎn)不要使用-操作進(jìn)行后向遍歷。
  • 我們先來闡述規(guī)則2,因?yàn)檎业揭粋€違背它的真實(shí)的實(shí)例代碼是很容易的。假設(shè)你有一個程序在你自己的目錄里保存了一個設(shè)置文件,你把安裝目錄保存在注冊表中。在運(yùn)行時,你從注冊表中讀取安裝目錄,然后合成配置文件名,接著讀取該文件。假設(shè),你的安裝目錄是C:"Program Files"MyCoolApp,那么你合成的文件名應(yīng)該是C:"Program Files"MyCoolApp"config.bin。當(dāng)你進(jìn)行測試時,你發(fā)現(xiàn)程序運(yùn)行正常。
    現(xiàn)在,想象你合成文件名的代碼可能是這樣的:

    bool GetConfigFileName ( char* pszName, size_t nBuffSize )
    {
    char szConfigFilename[MAX_PATH];

    // Read install dir from registry... we''ll assume it succeeds.

    // Add on a backslash if it wasn''t present in the registry value.
    // First, get a pointer to the terminating zero.
    char* pLastChar = strchr ( szConfigFilename, ''"0'' );

    // Now move it back one character.
    pLastChar--;

    if ( *pLastChar != ''""'' )
    strcat ( szConfigFilename, """" );

    // Add on the name of the config file.
    strcat ( szConfigFilename, "config.bin" );

    // If the caller''s buffer is big enough, return the filename.
    if ( strlen ( szConfigFilename ) >= nBuffSize )
    return false;
    else
    {
    strcpy ( pszName, szConfigFilename );
    return true;
    }
    } 這是一段很健壯的代碼,然而在遇到 DBCS 字符時它將會出錯。讓我們來看看為什么。假設(shè)一個日本用戶使用了你的程序,把它安裝在 C:"。下面是這個名字在內(nèi)存中的存儲形式:
    433A5C83 8883 4583 5283 5C00
    LB TB LB TB LB TB LB TB
    C:"EOS

      當(dāng)使用 GetConfigFileName() 檢查尾部的''""''時,它尋找安裝目錄名中最后的非0字節(jié),看它是等于''""''的,所以沒有重新增加一個''""''。結(jié)果是代碼返回了錯誤的文件名。
    哪里出錯了呢?看看上面兩個被用藍(lán)色高量顯示的字節(jié)。斜杠''""''的值是0x5c。'' ''的值是83 5c。上面的代碼錯誤的讀取了一個 trail byte,把它當(dāng)作了一個字符。
    正確的后向遍歷方法是使用能夠識別DBCS字符的函數(shù),使指針移動正確的字節(jié)數(shù)。下面是正確的代碼。(指針移動的地方用紅色標(biāo)明)

    bool FixedGetConfigFileName ( char* pszName, size_t nBuffSize )
    {
    char szConfigFilename[MAX_PATH];

    // Read install dir from registry... we''ll assume it succeeds.

    // Add on a backslash if it wasn''t present in the registry value.
    // First, get a pointer to the terminating zero.
    char* pLastChar = _mbschr ( szConfigFilename, ''"0'' );

    // Now move it back one double-byte character.
    pLastChar = CharPrev ( szConfigFilename, pLastChar );

    if ( *pLastChar != ''""'' )
    _mbscat ( szConfigFilename, """" );

    // Add on the name of the config file.
    _mbscat ( szConfigFilename, "config.bin" );

    // If the caller''s buffer is big enough, return the filename.
    if ( _mbslen ( szInstallDir ) >= nBuffSize )
    return false;
    else
    {
    _mbscpy ( pszName, szConfigFilename );
    return true;
    }
    }
    上面的函數(shù)使用CharPrev() API使pLastChar向后移動一個字符,這個字符可能是兩個字節(jié)長。在這個版本里,if條件正常工作,因?yàn)閘ead byte永遠(yuǎn)不會等于0x5c。
    讓我們來想象一個違背規(guī)則1的場合。例如,你可能要檢測一個用戶輸入的文件名是否多次出現(xiàn)了'':''。如果,你使用++操作來遍歷字符串,而不是使用CharNext(),你可能會發(fā)出不正確的錯誤警告如果恰巧有一個trail byte它的值的等于'':''的值。
    與規(guī)則2相關(guān)的關(guān)于字符串索引的規(guī)則: 2a. 永遠(yuǎn)不要使用減法去得到一個字符串的索引。

    違背這條規(guī)則的代碼和違背規(guī)則2的代碼很相似。例如,

    char* pLastChar = &szConfigFilename [strlen(szConfigFilename) - 1];

    這和向后移動一個指針是同樣的效果。

    回到關(guān)于str***()和_mbs***()的區(qū)別

    現(xiàn)在,我們應(yīng)該很清楚為什么_mbs***()函數(shù)是必需的。Str***()函數(shù)根本不考慮DBCS字符,而_mbs***()考慮。如果,你調(diào)用strrchr("C:"" ", ''""''),返回結(jié)果可能是錯誤的,然而_mbsrchr()將會認(rèn)出最后的雙字節(jié)字符,返回一個指向真的''""''的指針。
    關(guān)于字符串函數(shù)的最后一點(diǎn):str***()和_mbs***()函數(shù)認(rèn)為字符串的長度都是以char來計算的。所以,如果一個字符串包含3個雙字節(jié)字符,_mbslen()將會返回6。Unicode函數(shù)返回的長度是按wchar_t來計算的。例如,wcslen(L"Bob")返回3。

    Win32 API中的MBCS和Unicode

    兩組 APIs:
    盡管你也許從來沒有注意過,Win32中的每個與字符串相關(guān)的API和message都有兩個版本。一個版本接受MBCS字符串,另一個接受 Unicode字符串。例如,根本沒有SetWindowText()這個API,相反,有SetWindowTextA()和 SetWindowTextW()。后綴A表明這是MBCS函數(shù),后綴W表示這是Unicode版本的函數(shù)。
    當(dāng)你 build 一個 Windows 程序,你可以選擇是用 MBCS 或者 Unicode APIs。如果,你曾經(jīng)用過VC向?qū)Р⑶覜]有改過預(yù)處理的設(shè)置,那表明你用的是MBCS版本。那么,既然沒有 SetWindowText() API,我們?yōu)槭裁纯梢允褂盟?#xff1f;winuser.h頭文件包含了一些宏,例如:

    BOOL WINAPI SetWindowTextA ( HWND hWnd, LPCSTR lpString );
    BOOL WINAPI SetWindowTextW ( HWND hWnd, LPCWSTR lpString );

    #ifdef UNICODE
    #define SetWindowText SetWindowTextW
    #else
    #define SetWindowText SetWindowTextA
    #endif 當(dāng)使用MBCS APIs來build程序時,UNICODE沒有被定義,所以預(yù)處理器看到: #define SetWindowText SetWindowTextA

      這個宏定義把所有對SetWindowText的調(diào)用都轉(zhuǎn)換成真正的API函數(shù)SetWindowTextA。(當(dāng)然,你可以直接調(diào)用SetWindowTextA() 或者 SetWindowTextW(),雖然你不必那么做。)
    所以,如果你想把默認(rèn)使用的API函數(shù)變成Unicode版的,你可以在預(yù)處理器設(shè)置中,把_MBCS從預(yù)定義的宏列表中刪除,然后添加UNICODE和_UNICODE。(你需要兩個都定義,因?yàn)椴煌念^文件可能使用不同的宏。) 然而,如果你用char來定義你的字符串,你將會陷入一個尷尬的境地。考慮下面的代碼:

    HWND hwnd = GetSomeWindowHandle();
    char szNewText[] = "we love Bob!";
    SetWindowText ( hwnd, szNewText );

    在預(yù)處理器把SetWindowText用SetWindowTextW來替換后,代碼變成:

    HWND hwnd = GetSomeWindowHandle();
    char szNewText[] = "we love Bob!";
    SetWindowTextW ( hwnd, szNewText );

      看到問題了嗎?我們把單字節(jié)字符串傳給了一個以Unicode字符串做參數(shù)的函數(shù)。解決這個問題的第一個方案是使用 #ifdef 來包含字符串變量的定義:

    HWND hwnd = GetSomeWindowHandle();
    #ifdef UNICODE
    wchar_t szNewText[] = L"we love Bob!";
    #else
    char szNewText[] = "we love Bob!";
    #endif
    SetWindowText ( hwnd, szNewText );

    你可能已經(jīng)感受到了這樣做將會使你多么的頭疼。完美的解決方案是使用TCHAR.

    使用TCHAR

    TCHAR是一種字符串類型,它讓你在以MBCS和UNNICODE來build程序時可以使用同樣的代碼,不需要使用繁瑣的宏定義來包含你的代碼。TCHAR的定義如下:

    #ifdef UNICODE
    typedef wchar_t TCHAR;
    #else
    typedef char TCHAR;
    #endif

    所以用MBCS來build時,TCHAR是char,使用UNICODE時,TCHAR是wchar_t。還有一個宏來處理定義Unicode字符串常量時所需的L前綴。

    #ifdef UNICODE
    #define _T(x) L##x
    #else
    #define _T(x) x
    #endif

    ##是一個預(yù)處理操作符,它可以把兩個參數(shù)連在一起。如果你的代碼中需要字符串常量,在它前面加上_T宏。如果你使用Unicode來build,它會在字符串常量前加上L前綴。

    TCHAR szNewText[] = _T("we love Bob!");

    像是用宏來隱藏SetWindowTextA/W的細(xì)節(jié)一樣,還有很多可以供你使用的宏來實(shí)現(xiàn)str***()和_mbs***()等字符串函數(shù)。例如,你可以使用_tcsrchr宏來替換strrchr()、_mbsrchr()和wcsrchr()。_tcsrchr根據(jù)你預(yù)定義的宏是_MBCS 還是UNICODE來擴(kuò)展成正確的函數(shù),就像SetWindowText所作的一樣。
    不僅str***()函數(shù)有TCHAR宏。其他的函數(shù)如, _stprintf(代替sprinft()和swprintf()),_tfopen(代替fopen()和_wfopen())。 MSDN中"Generic-Text Routine Mappings."標(biāo)題下有完整的宏列表。

    字符串和TCHAR typedefs

    由于Win32 API文檔的函數(shù)列表使用函數(shù)的常用名字(例如,"SetWindowText"),所有的字符串都是用TCHAR來定義的。(除了XP中引入的只適用于 Unicode的API)。下面列出一些常用的typedefs,你可以在msdn中看到他們。

    type Meaning in MBCS builds Meaning in Unicode builds
    WCHARwchar_twchar_t
    LPSTR zero-terminated string of char (char*)zero-terminated string of char (char*)
    LPCSTR constant zero-terminated string of char (const char*)constant zero-terminated string of char (const char*)
    LPWSTRzero-terminated Unicode string (wchar_t*) zero-terminated Unicode string (wchar_t*)
    LPCWSTRconstant zero-terminated Unicode string (const wchar_t*)constant zero-terminated Unicode string (const wchar_t*)
    TCHARcharwchar_t
    LPTSTRzero-terminated string of TCHAR (TCHAR*) zero-terminated string of TCHAR (TCHAR*)
    LPCTSTR constant zero-terminated string of TCHAR (const TCHAR*)constant zero-terminated string of TCHAR (const TCHAR*)

    何時使用 TCHAR 和 Unicode

    到現(xiàn)在,你可能會問,我們?yōu)槭裁匆褂肬nicode。我已經(jīng)用了很多年的char。下列3種情況下,使用Unicode將會使你受益:

  • 1.你的程序只運(yùn)行在Windows NT系統(tǒng)中。
  • 2. 你的程序需要處理超過MAX_PATH個字符長的文件名。
  • 3. 你的程序需要使用XP中引入的只有Unicode版本的API.
  • Windows 9x 中大多數(shù)的 API 沒有實(shí)現(xiàn) Unicode 版本。所以,如果你的程序要在windows 9x中運(yùn)行,你必須使用MBCS APIs。然而,由于NT系統(tǒng)內(nèi)部都使用Unicode,所以使用Unicode APIs將會加快你的程序的運(yùn)行速度。每次,你傳遞一個字符串調(diào)用MBCS API,操作系統(tǒng)會把這個字符串轉(zhuǎn)換成Unicode字符串,然后調(diào)用對應(yīng)的Unicode API。如果一個字符串被返回,操作系統(tǒng)還要把它轉(zhuǎn)變回去。盡管這個轉(zhuǎn)換過程被高度優(yōu)化了,但它對速度造成的損失是無法避免的。
    只要你使用Unicode API,NT系統(tǒng)允許使用非常長的文件名(突破了MAX_PATH的限制,MAX_PATH=260)。使用Unicode API的另一個優(yōu)點(diǎn)是你的程序會自動處理用戶輸入的各種語言。所以一個用戶可以輸入英文,中文或者日文,而你不需要額外編寫代碼去處理它們。
    最后,隨著windows 9x產(chǎn)品的淡出,微軟似乎正在拋棄MBCS APIs。例如,包含兩個字符串參數(shù)的SetWindowTheme() API只有Unicode版本的。使用Unicode來build你的程序?qū)喕址奶幚?#xff0c;你不必在MBCS和Unicdoe之間相互轉(zhuǎn)換。
    即使你現(xiàn)在不使用Unicode來build你的程序,你也應(yīng)該使用TCHAR及其相關(guān)的宏。這樣做不僅可以的代碼可以很好地處理DBCS,而且如果將來你想用Unicode來build你的程序,你只需要改變一下預(yù)處理器中的設(shè)置就可以實(shí)現(xiàn)了。 ? http://www.cnblogs.com/Sonic2007/

    總結(jié)

    以上是生活随笔為你收集整理的Windbg内核调试(大杂烩)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

    国产精品视频你懂的 | 日韩特黄av | 2019av在线视频 | 少妇bbbb揉bbbb日本 | 欧美最猛性xxxxx免费 | 日韩激情片在线观看 | 国产精品一区二区在线 | 香蕉视频国产在线观看 | 人人澡人 | 天天摸天天干天天操天天射 | 欧美日韩精品在线免费观看 | 丁香综合网 | 免费的国产精品 | 国产精品久久久久久久久久ktv | 亚洲另类久久 | 色综合五月天 | 久久天天操 | 国产精品 9999 | 毛片99| 免费成人av在线 | 2019中文 | 国产精品入口a级 | 久久艹艹| 成人黄色在线电影 | 日本3级在线观看 | 天天干天天干天天干天天干天天干天天干 | 久久超 | 四虎影视国产精品免费久久 | 色婷婷丁香 | 国产在线精品一区二区 | 香蕉视频导航 | 午夜精品一二区 | 色综合久久中文综合久久牛 | 99精品视频观看 | 在线精品视频免费播放 | a在线一区 | www色com| 亚洲日本中文字幕在线观看 | 在线观看精品视频 | 99久久激情 | 久久全国免费视频 | 欧美一级特黄aaaaaa大片在线观看 | 免费a v观看 | 精品国产一区二区三区久久久蜜臀 | 国产资源 | 亚洲v精品 | 日本性高潮视频 | 九九av| 97视频免费观看2区 亚洲视屏 | 午夜精品久久久久99热app | 天天色棕合合合合合合 | 国产日韩视频在线播放 | 欧产日产国产69 | 美女黄久久 | 国产精品久久久久aaaa | 精品一区二区视频 | 国产一区在线不卡 | 国产不卡一区二区视频 | 国产精品 日韩精品 | 一本一本久久aa综合精品 | 在线观看网站你懂的 | 久久精品三| 男女视频91 | 91成人蝌蚪 | a天堂一码二码专区 | 国产精品一区二区视频 | 久久激情视频网 | 天天久久夜夜 | 国产99久久 | 在线观看中文字幕第一页 | 91最新地址永久入口 | 片网站 | 91视频午夜 | 国产综合片 | 日本精品在线 | 狠狠干夜夜爽 | 国产精品久久久久婷婷二区次 | 欧美91av| 亚洲人在线7777777精品 | 精品视频久久 | 欧美精品在线观看免费 | 综合久久久 | 超碰在线91| 欧美日韩免费视频 | 国产精品2020 | 日色在线视频 | 日韩手机在线观看 | 91手机电影| 中文日韩在线视频 | 亚洲免费在线播放视频 | 在线91观看 | 69久久99精品久久久久婷婷 | 精品久久久久久久久久岛国gif | 深爱激情婷婷网 | 亚洲狠狠婷婷综合久久久 | 亚洲精品一区中文字幕乱码 | 免费国产一区二区视频 | 久久蜜臀av | 亚洲91中文字幕无线码三区 | 91福利视频在线 | 久热免费在线观看 | 国产精品久久一卡二卡 | 成人91av| 欧美日韩国产精品一区二区亚洲 | 日韩最新在线 | 久久久久久蜜桃一区二区 | 成年性视频 | 五月激情亚洲 | 日日夜夜狠狠 | 久久久久激情视频 | 69人人| 亚洲精品久久久蜜桃 | 97偷拍视频| 亚洲二区精品 | 四虎成人精品永久免费av | 国产精品久久久久久久免费 | 精品国产精品一区二区夜夜嗨 | 91成人精品在线 | 色噜噜狠狠狠狠色综合久不 | 久久久久免费精品国产小说色大师 | 天天天天天天天操 | 中文字幕亚洲欧美日韩2019 | 亚洲做受高潮欧美裸体 | 国产精品原创av片国产免费 | 91少妇精拍在线播放 | 久久综合狠狠综合久久狠狠色综合 | 欧美日韩国产一二三区 | 一区二区三区在线视频观看58 | 久久无码精品一区二区三区 | 91av在线播放视频 | 亚洲干视频在线观看 | 久久精品中文字幕免费mv | 国产一级免费片 | 成人黄性视频 | 亚洲动漫在线观看 | 色综合久久88色综合天天 | 五月婷婷激情综合网 | 又黄又爽又刺激视频 | 国产一级大片在线观看 | 欧美福利网站 | 久草在线在线精品观看 | 久久男人中文字幕资源站 | 日韩激情中文字幕 | 国产精品毛片久久久 | 嫩草av影院 | 91中文字幕永久在线 | 99热日本 | 国产精品99精品久久免费 | 婷婷精品国产欧美精品亚洲人人爽 | 国产第一页精品 | 午夜精品一区二区三区免费 | 一级黄色视屏 | 成人av电影网址 | 久久精品亚洲一区二区三区观看模式 | 91c网站色版视频 | 久久久久久久av麻豆果冻 | 免费国产视频 | 91在线网址| 久久久精品日本 | 国产小视频免费观看 | 久久高清免费观看 | 国产午夜av | 99热这里只有精品免费 | 久久精彩免费视频 | 亚洲影院一区 | 婷婷丁香色 | 探花视频免费观看高清视频 | 成人一级片视频 | 中文字幕黄网 | www.狠狠插.com| 成人影视免费看 | 亚洲国产精品va在线 | 久久久久久久久爱 | 中文字幕一区二区三区四区 | 97超级碰| 精品女同一区二区三区在线观看 | 天天干天天爽 | 久综合网| 伊人狠狠色 | 99热高清 | 久久不射电影院 | 色丁香久久| 韩日电影在线 | 九九热在线观看 | 久久久久欠精品国产毛片国产毛生 | 国产高清在线看 | 91桃色在线免费观看 | 狠狠操.com | 麻豆视频观看 | 亚洲国产精品电影在线观看 | 亚洲高清视频在线观看 | 在线天堂中文在线资源网 | 去看片 | 日韩精品 在线视频 | 国产裸体永久免费视频网站 | 国产乱对白刺激视频不卡 | 亚洲精品中文字幕视频 | 日韩av片在线 | 色婷婷激情综合 | 黄色电影在线免费观看 | 欧美伦理一区 | 在线成人观看 | 精品国产一二三 | 国际av在线 | 少妇bbbb搡bbbb搡bbbb | 麻豆视频免费在线 | 国产一二三四在线视频 | 午夜精品久久久 | 日本在线观看视频一区 | 精品国产一区二区三区久久 | 亚洲一级二级 | 成人免费毛片aaaaaa片 | 超碰在线成人 | 精品福利视频在线 | 亚洲视频axxx | 精品国产伦一区二区三区免费 | 久久久久国产精品视频 | 国产91勾搭技师精品 | 亚洲国产网址 | 欧美999| 成人黄色小说视频 | 日韩欧美网站 | 精品一区二区视频 | 久草在线高清视频 | 精品视频国产 | 五月婷亚洲 | 精品欧美一区二区精品久久 | 久久久福利视频 | 亚洲伊人网在线观看 | 四虎国产精品成人免费影视 | av在线播放中文字幕 | 午夜精品久久久久久久99 | 天天插视频 | 久久天天操 | 免费久久片| 日本在线观看一区二区三区 | 国内精品免费久久影院 | 久久久久久久久久久综合 | 在线观看黄色小视频 | 久久综合精品国产一区二区三区 | 国产美女精品视频免费观看 | 亚洲区另类春色综合小说 | 欧美二区视频 | 久热超碰| 精品主播网红福利资源观看 | 欧美一级爽| 中文字幕亚洲情99在线 | 在线看中文字幕 | 亚洲亚洲精品在线观看 | 在线综合 亚洲 欧美在线视频 | 欧美成人精品在线 | 综合精品久久久 | 激情五月婷婷网 | 国产区精品区 | 久久精品欧美一区二区三区麻豆 | 特黄特色特刺激视频免费播放 | 99精品国产在热久久 | 久久婷亚洲五月一区天天躁 | www夜夜操com| aav在线| 国产91对白在线 | 亚洲成人精品 | 中文字幕乱码亚洲精品一区 | 人人干天天干 | 高潮毛片无遮挡高清免费 | 99热超碰 | a√资源在线 | av免费网站观看 | 丁香花在线观看免费完整版视频 | 麻豆久久久久久久 | av短片在线 | 欧美在线视频a | 中文字幕五区 | 91亚洲激情 | 天天操天天干天天插 | 欧美激情奇米色 | 97免费在线观看视频 | 成人久久久精品国产乱码一区二区 | 国产特级毛片aaaaaa高清 | 亚洲精品乱码久久久久久蜜桃动漫 | 日本午夜在线亚洲.国产 | 久草在线资源免费 | 国产手机精品视频 | 天堂av高清| 精品一区二区免费在线观看 | 久久久精品小视频 | 国产aa精品 | 国产91免费看 | 99久久www免费 | 国产精品久久久久久吹潮天美传媒 | 久久久一本精品99久久精品 | 国产成人av一区二区三区在线观看 | 日韩一二三在线 | 久久国产精品99精国产 | 粉嫩av一区二区三区免费 | 日韩大片免费在线观看 | 在线免费观看不卡av | 四虎永久视频 | 久久九九免费视频 | 精品影院一区二区久久久 | 欧美狠狠色 | 91精品视频免费看 | 麻豆免费视频观看 | 国产亚洲成人精品 | 日韩欧美在线视频一区二区三区 | 黄色日本片 | 国产精品视频地址 | japanesexxxhd奶水 国产一区二区在线免费观看 | 精品国产精品一区二区夜夜嗨 | 99草视频 | 久久免费视频精品 | 国产va在线 | 99久久超碰中文字幕伊人 | 久久草 | 亚洲高清免费在线 | 99久久久久成人国产免费 | 丁香五月缴情综合网 | 日本公妇在线观看高清 | 国产专区在线 | 国产综合视频在线观看 | 日本99精品 | 99国产免费网址 | 国产精品高潮久久av | 97在线观看免费 | 国产一级片免费视频 | 婷婷国产一区二区三区 | 免费亚洲视频在线观看 | 丁香婷婷久久 | 天天做天天爱夜夜爽 | 午夜视频在线瓜伦 | 欧美日韩在线电影 | 午夜精品一区二区三区可下载 | 亚洲高清av | 国产精品久久久一区二区 | 九九九热精品免费视频观看 | 欧美动漫一区二区三区 | 国产999精品久久久 免费a网站 | 亚洲综合网站在线观看 | 波多野结依在线观看 | 九九三级毛片 | 久久久久久久网站 | 国产精品中文久久久久久久 | 国产大陆亚洲精品国产 | 日本最新一区二区三区 | 草久热 | 天天在线视频色 | 久久国产精品视频免费看 | 久黄色| 日韩区在线观看 | 男女精品久久 | av免费看电影 | 久久久久久久久久久免费 | 欧美日性视频 | 日韩在线影视 | 99久久久久久 | 久久新| 精品一区 在线 | 亚洲精选在线 | 国产青草视频在线观看 | 91视频在线自拍 | www久久99 | 亚洲毛片一区二区三区 | 国产免费观看视频 | 亚洲国产精品99久久久久久久久 | 五月天久久婷婷 | 狠狠色丁香婷婷综合久小说久 | 日韩高清在线看 | 午夜在线观看一区 | 中文字幕av在线 | 五月激情电影 | 亚洲美女免费精品视频在线观看 | 人人讲| 97理论电影| 天天综合网入口 | 午夜成人免费影院 | 国产又粗又硬又爽视频 | 五月天.com | 精品国产理论片 | 美女网站视频免费都是黄 | 国产不卡免费视频 | 日韩免费一级电影 | 国产精品丝袜在线 | 国产精品久久久久久久免费大片 | 日韩亚洲在线视频 | 国产原创在线 | 97在线观看视频免费 | 超级碰99 | 精品在线二区 | 国产成人三级在线 | 99热最新| 久久精品亚洲国产 | av免费观看在线 | 国产a高清| 国产色婷婷精品综合在线手机播放 | 色综合天天做天天爱 | 中文字幕亚洲不卡 | 四虎成人精品在永久免费 | 一区在线播放 | 精品免费| 亚洲一级片 | 亚洲天堂自拍视频 | 在线免费黄色av | 最新中文在线视频 | 日韩黄色一区 | 99 色| 亚洲成av人片在线观看香蕉 | 天天操天天爱天天干 | 密桃av在线 | www.com久久久 | 国产精品理论片在线播放 | 91九色在线 | 91精品一区二区三区蜜桃 | 亚洲精品在线视频观看 | 97超碰免费在线观看 | 欧美日韩在线精品 | 91av在线播放视频 | 国产中文字幕三区 | 成年人毛片在线观看 | 玖玖在线精品 | 韩国一区视频 | 欧美不卡视频在线 | 成人电影毛片 | av中文字幕剧情 | 日韩精品观看 | 激情婷婷六月 | 精品久久91 | 99视频精品视频高清免费 | 国产精品麻豆99久久久久久 | 久99久视频 | 91成年视频 | 亚洲人人av | 久99热| 亚洲最新精品 | 欧美国产三区 | 午夜视频日本 | 成人中文字幕+乱码+中文字幕 | 免费在线观看av的网站 | 国产精品毛片一区二区三区 | 91精品国产自产在线观看永久 | 亚洲综合成人专区片 | 国产一区二区免费 | 韩日电影在线观看 | 国产黄色片免费在线观看 | 亚洲国产精品视频在线观看 | 欧美性生活免费看 | 日韩va在线观看 | 久久精品国产免费看久久精品 | 亚洲精品网站在线 | 国产 字幕 制服 中文 在线 | 免费在线观看中文字幕 | 国产综合在线观看视频 | 国产91精品在线播放 | 日韩av一卡二卡三卡 | 最新色视频 | 午夜av片| 欧美激情视频在线观看免费 | 狠狠的日 | 国产中文字幕国产 | 超碰国产在线观看 | 伊人手机在线 | 亚洲精品一区二区三区四区高清 | 天天色天天色天天色 | 91在线看免费 | 丁香网五月天 | 97在线观看免费视频 | 国产精品白丝jk白祙 | 一区二区三区精品久久久 | 天天草综合网 | 日韩欧美一区视频 | 国产视频在线观看一区二区 | 最近更新的中文字幕 | 日韩有码专区 | 午夜精品三区 | 天天噜天天色 | 一本一本久久a久久精品牛牛影视 | 18做爰免费视频网站 | 日韩中文字幕一区 | 狠狠色丁香婷婷综合橹88 | 国产96在线观看 | 国产黄色大片 | 中文字幕 国产视频 | 性色va| www色av| 日本一区二区三区免费看 | 91中文字幕在线播放 | 在线免费观看视频a | 国产久视频 | 狠狠躁夜夜躁人人爽超碰97香蕉 | 伊人狠狠操 | 麻豆视频入口 | 久久久婷 | 91免费看黄色 | 国产打女人屁股调教97 | japanese黑人亚洲人4k | www.久久久久 | 日韩欧美在线免费观看 | 久久久999精品视频 国产美女免费观看 | 制服丝袜欧美 | 精品一区免费 | 国产999精品 | 99精品视频在线播放免费 | 国产资源免费在线观看 | 国产精品久久在线观看 | 日韩免费小视频 | 91最新在线 | 日韩在线观看一区二区 | 91av免费观看| 中文字幕在线免费 | 91高清完整版在线观看 | 91成人区| 色www免费视频 | 去看片| 色黄www小说 | 久久香蕉影视 | 日韩免费观看视频 | 日韩三区在线 | 500部大龄熟乱视频使用方法 | 色在线免费观看 | 久久久久久久久影院 | 麻豆一精品传二传媒短视频 | 日韩高清不卡一区二区三区 | 精品9999| 亚洲天堂精品视频在线观看 | 国产资源免费 | 久草视频精品 | 日韩电影在线观看一区 | 国产精品白虎 | 五月天com| 欧美日韩调教 | 国产精品网站一区二区三区 | 91精品国产成人观看 | 九月婷婷人人澡人人添人人爽 | 欧美a免费| 这里只有精品视频在线 | 91热视频 | 一区二区三区国 | 亚洲视频六区 | 91香蕉视频720p | a在线播放 | av丁香| 探花视频网站 | 天天干夜夜擦 | 国产女人免费看a级丨片 | 国产女教师精品久久av | 亚洲精品乱码久久久久久蜜桃动漫 | 麻豆精品在线 | 国产福利91精品一区二区三区 | 麻豆视频免费版 | 日韩av电影中文字幕在线观看 | 亚洲精品乱码白浆高清久久久久久 | 波多野结衣理论片 | 日日夜夜av | 日韩区视频 | 精品国产视频在线 | 在线观看日韩中文字幕 | 九九热在线观看 | 在线免费观看一区二区三区 | 亚洲精品在线观看中文字幕 | 激情五月婷婷激情 | 亚洲永久免费av | 免费成人av在线 | 国产短视频在线播放 | 日韩一区在线免费观看 | 91精品国产三级a在线观看 | 久久久久 免费视频 | 国产一区二区三区视频在线 | 91.麻豆视频 | av免费看电影 | 成年人电影免费看 | 天天做天天爱天天综合网 | 久久看片网站 | 久久久噜噜噜久久久 | 日韩理论在线视频 | 久久视精品 | 中文字幕av全部资源www中文字幕在线观看 | 日日插日日干 | 91精品黄色 | 射综合网 | 欧美 另类 交 | 免费观看av网站 | 欧美一区免费观看 | av片一区二区 | 欧美少妇xxxxxx | 我爱av激情网 | 日本精品一区二区三区在线观看 | 国产精品嫩草影院99网站 | 欧美日韩另类在线观看 | 亚洲特级片 | 欧美a性 | 国产成人99久久亚洲综合精品 | 日本激情中文字幕 | 久久夜夜操 | www五月天 | 亚洲91中文字幕无线码三区 | 久热色超碰 | 欧美日韩高清一区二区三区 | 天天玩天天干天天操 | 久久美女免费视频 | 91自拍91| www色网站| 中文字幕最新精品 | 永久免费精品视频网站 | 国产理伦在线 | 91片在线观看| 亚洲精品免费在线播放 | 免费观看91视频大全 | 欧美精品久久久久久久亚洲调教 | 麻豆视频免费看 | 美女黄视频免费 | 久草在线观看视频免费 | 欧美男同网站 | 91成人精品国产刺激国语对白 | 久久精品毛片 | 99re6热在线精品视频 | 国产人成一区二区三区影院 | 日本动漫做毛片一区二区 | 天天做夜夜做 | 成年人黄色免费看 | 久久亚洲精品电影 | 制服丝袜在线 | 成人久久久久久久久久 | 五月婷在线 | 欧美一区视频 | 久久爽久久爽久久av东京爽 | 亚洲精品视频在线观看免费 | 日本久久99 | 九九视频免费在线观看 | 日韩av在线小说 | 欧美污污网站 | 免费h精品视频在线播放 | 一级黄色片在线免费看 | 国产在线观看黄 | 99久热在线精品视频成人一区 | 精品国产一区二区在线 | 欧美日韩高清在线观看 | 国产一二三在线视频 | 国产精品观看视频 | 中文字幕在线观看2018 | 久久成电影 | 久草精品视频在线播放 | 特级a老妇做爰全过程 | 天天艹天天 | 久久国产精品免费视频 | 毛片美女网站 | 国产一区二区手机在线观看 | 亚洲精品在线免费观看视频 | 五月天堂网 | 国产午夜精品一区二区三区嫩草 | www.夜夜| 精品视频123区在线观看 | 免费网站黄 | 国产视频一二区 | 国产日韩欧美在线观看视频 | 亚洲综合色视频在线观看 | 久久不卡视频 | 免费h精品视频在线播放 | 国产亚洲精品美女久久 | 色婷婷国产精品 | 特及黄色片 | 成人av手机在线 | 亚洲综合成人婷婷小说 | 久久免费高清视频 | 蜜臀av性久久久久av蜜臀妖精 | 成年人在线免费看视频 | 777久久久| 久久久久久久久免费 | 国产精品久久久久久电影 | 99爱这里只有精品 | 99精品电影 | 国内丰满少妇猛烈精品播 | 特级a老妇做爰全过程 | 日韩在线观看第一页 | 国产免费嫩草影院 | 99精品国产免费久久 | 丝袜美腿在线播放 | 五月天综合网 | 天天插一插 | 免费看黄在线看 | 成人av日韩 | 亚洲资源视频 | 久久久久久久综合色一本 | 欧洲av在线| v片在线播放 | 国产精品白浆 | 99久在线精品99re8热视频 | 91久久精品一区二区二区 | 亚洲成a人片综合在线 | 999视频在线观看 | 日批视频国产 | 亚洲欧美日韩精品一区二区 | 日韩专区一区二区 | 国产精品国产三级国产不产一地 | 国产精品美女免费看 | 天天射色综合 | 国产在线999 | 黄色网www | 久久99精品一区二区三区三区 | 中文字幕在线色 | 在线成人一区 | 日韩一级电影网站 | 亚洲高清色综合 | 久久国产精品色婷婷 | 中文字幕在线免费观看 | 日韩av偷拍 | 亚洲专区欧美专区 | 久久国产精品久久国产精品 | 黄色大片网 | 青青草国产免费 | 在线探花| 国产91九色视频 | 在线观看免费一区 | 免费观看视频的网站 | 在线91观看| 6080yy午夜一二三区久久 | 亚洲美女精品区人人人人 | 久久免费国产电影 | 国产亚洲va综合人人澡精品 | 免费黄色网址网站 | 91视频高清完整版 | 丝袜+亚洲+另类+欧美+变态 | 人人爽人人澡人人添人人人人 | 黄网站app在线观看免费视频 | 日本3级在线观看 | 久久爱综合 | 欧美日韩午夜爽爽 | 中文字幕av电影下载 | 9在线观看免费高清完整 | 国产精品久久一 | 国产成人精品一区二区三区福利 | 波多野结衣视频一区 | 亚洲三级网站 | 国产精品久久久久婷婷二区次 | 成人一级片在线观看 | 99高清视频有精品视频 | 国产精品久久久久久吹潮天美传媒 | 亚洲精品美女久久 | 欧美日韩国产亚洲乱码字幕 | 免费能看的黄色片 | 日韩精品一区二区三区视频播放 | 国产精品久久久久aaaa九色 | 欧美大片aaa | 一级黄色大片在线观看 | 99视频一区| 91精品天码美女少妇 | 免费av成人在线 | 亚洲 欧美 精品 | 成人黄在线 | 久草a视频 | 99re国产| 国产免费叼嘿网站免费 | 成 人 黄 色视频免费播放 | 免费视频久久久久久久 | 精品视频免费久久久看 | 97福利 | 国产一区 在线播放 | 91在线观看欧美日韩 | 日韩在线视频看看 | 欧美亚洲另类在线视频 | 久久视频 | 亚欧日韩成人h片 | 亚洲国产精品久久久久久 | 久草网视频在线观看 | 精品国产免费一区二区三区五区 | 五月婷婷毛片 | 国产精品99精品 | 国内免费的中文字幕 | 在线观看免费黄视频 | 亚洲午夜不卡 | 国产小视频网站 | 色诱亚洲精品久久久久久 | 国产男女免费完整视频 | 色资源二区在线视频 | 亚洲精品乱码久久久久久蜜桃91 | 国产日韩在线看 | 日韩在线观看av | 超碰97久久 | 亚洲夜夜综合 | 欧美精品一区二区蜜臀亚洲 | 免费看黄在线网站 | 国产剧情av在线播放 | 免费碰碰 | 在线之家免费在线观看电影 | 精品产品国产在线不卡 | 91丨九色丨国产在线观看 | 久久大片 | 久草在线观看资源 | 成人av免费看| 狠狠干综合网 | 国产69精品久久99的直播节目 | 日韩精品欧美一区 | 亚洲高清久久久 | 精品99免费视频 | 久久精品高清 | 九九免费观看全部免费视频 | 欧美午夜视频在线 | 美女性爽视频国产免费app | 九九热在线视频 | 丁香婷婷激情啪啪 | 98福利在线 | 久久久电影 | 国产视频1区2区3区 久久夜视频 | 成人天堂网 | 精品视频国产 | 日日干 天天干 | 日韩中文字幕第一页 | 有码中文字幕在线观看 | 亚洲视频综合在线 | 精品国产美女 | 国产精品对白一区二区三区 | 狠狠ri| 激情综合狠狠 | 久久精品视频在线观看 | 在线日韩中文 | 五月天丁香综合 | 2020天天干夜夜爽 | 天天草天天操 | 最新真实国产在线视频 | 免费看的毛片 | 国产原创91| 精品网站999www | 国产精品久久电影观看 | 九九久久久久99精品 | 久久综合久久久久88 | 国产一区视频在线观看免费 | 免费在线观看成人小视频 | 欧美激情精品久久久久久免费印度 | 成人综合婷婷国产精品久久免费 | 九九精品视频在线观看 | 日韩高清www | 91成人免费在线 | 在线免费观看不卡av | 91在线视频 | 亚洲成人精品在线观看 | 99久久精品免费看国产一区二区三区 | 草久久影院 | 天天av资源 | 色香蕉在线 | 久久99久久久久 | 五月婷在线视频 | 四虎在线免费观看 | 九九热视频在线 | 黄色av播放| 国产精品免费久久 | 久久a免费视频 | 亚洲日韩欧美一区二区在线 | 久久精品视频免费观看 | av看片在线观看 | 亚洲国产欧美在线看片xxoo | 日韩www在线| 久久亚洲影院 | 香蕉影院在线观看 | 人人舔人人 | 女人18毛片a级毛片一区二区 | 国产日韩精品一区二区在线观看播放 | www.伊人网.com| 色婷婷在线观看视频 | 超碰日韩在线 | 欧美国产日韩在线观看 | 97网站| 又色又爽的网站 | 久久av福利 | 久久久久久不卡 | 国内外成人在线视频 | 中文字幕高清 | 久久久国产一区二区三区四区小说 | av黄色免费在线观看 | 黄色在线免费观看网站 | 欧美极品少妇xxxxⅹ欧美极品少妇xxxx亚洲精品 | 毛片99 | 五月婷婷狠狠 | 天天爽综合网 | 国产亚洲精品久久久久久 | 欧美日韩xx| 丁香午夜| 精品av网站| www.伊人网| 91漂亮少妇露脸在线播放 | 99久久精品国产欧美主题曲 | 免费亚洲成人 | 国产精品99久久久久人中文网介绍 | 波多野结衣在线观看一区二区三区 | 久草在线最新 | 日本特黄特色aaa大片免费 | 成人中文字幕在线 | 精品在线亚洲视频 | 精品亚洲免费视频 | 国产午夜精品久久 | 日韩h在线观看 | av三级av | 在线电影a | 欧美va天堂va视频va在线 | 色播亚洲婷婷 | 婷婷黄色片| 99免费在线视频 | 欧美视频日韩 | 国产精品视频免费在线观看 | 婷婷视频在线观看 | 天天操天天综合网 | 天天操天天干天天摸 | 99久久这里有精品 | 成人在线中文字幕 | 久久人人爽爽人人爽人人片av | 永久免费毛片在线观看 | 日本黄网站 | 久久成人国产精品入口 | 国产黄在线免费观看 | 日韩欧美大片免费观看 | 亚洲一区不卡视频 | 综合五月婷婷 | 九九视频网站 | 在线观看精品视频 | 日韩欧美国产激情在线播放 | 中文字幕久久精品亚洲乱码 | 日韩在观看线 | 国产成人资源 | 婷婷综合电影 | 亚洲区另类春色综合小说校园片 | 国产一区二区三区免费视频 | 色婷婷av在线 | 亚洲精品久久久久久中文传媒 | 国产老妇av| 婷婷久久一区二区三区 | 亚洲国产欧美在线人成大黄瓜 | 国产裸体无遮挡 | www.黄色片网站 | 伊人久久国产精品 | 国产馆在线播放 | 国产精品久久久久久久久久免费看 | 国产成人精品a | 在线免费观看av网站 | 人人射人人插 | 中文字幕在线免费97 | 精品国产一区二区三区蜜臀 | 69精品| 久久精品一区二区三区视频 | 国产精品美女久久久久久 | 在线a亚洲视频播放在线观看 | 精品国产片 | 五月天色综合 | 精品久久片| 日日爱av | 99久久精品视频免费 | 天天搞天天干天天色 | 国产高清中文字幕 | 超碰在线日韩 | 亚洲最新精品 | 五月天亚洲精品 | 久久久久国 | 天天操天天操天天操天天操 | 精品一区欧美 | 欧美日韩性 | 手机在线视频福利 | 精品久久久久久国产偷窥 | 欧美日韩亚洲在线观看 | 日本少妇高清做爰视频 | 天天插伊人| www.夜夜夜 | 人人爽人人爽人人爽学生一级 | 草久视频在线 | 国产伦精品一区二区三区在线 | 日韩在线视频二区 | 亚洲色图色 | 久久精品伊人 | 国产理论一区二区三区 | 人人看黄色 | 人人澡人人爽 | 亚洲欧美婷婷六月色综合 | 欧美午夜精品久久久久久孕妇 | 国产涩涩在线观看 | 少妇精品久久久一区二区免费 | 精品亚洲成a人在线观看 | 亚洲精品伦理在线 | 激情欧美一区二区三区免费看 | 精品国产免费一区二区三区五区 | 99夜色 | 9999国产精品 | 超碰av在线| 超碰伊人网 | 久久国内视频 | 国产福利在线免费 | 99久久婷婷国产精品综合 | 久久精品毛片基地 | 综合网成人 | 高清色免费 | 久草在线国产 | 91精品啪啪 | 麻豆国产网站入口 | 亚洲久草在线 | 高清av免费看 | 国产淫片免费看 | 成人免费观看网站 | 精品96久久久久久中文字幕无 | 欧美在线一二 | 亚洲五月花 | 国产精品99久久久久人中文网介绍 | 色婷久久 | 久久这里只有精品1 | 香蕉网在线|