Quantcast
Channel: windows –易春木
Viewing all 57 articles
Browse latest View live

ubuntu裡如何設定vnc遠端遙控軟體, 讓windows連上ubuntu

$
0
0

利用vnc連線到ubuntu
底下分享兩個方式

第一個方式就是使用GUI介面的設定

  • 按下系統、偏好設定、遠端桌面
  • 然後勾選:允許其他使用者觀看您的桌面、允許其他使用者控制您的桌面、使用者需要輸入密碼
  • 然後輸入妳想要的密碼
  • 不要打勾:詢問您以確認
  • 這樣就可以了

第二個方式就是利用command line介面

  • 首先先不管ubuntu裡面有沒有裝vnc 都給他下這行指令啦~
    • apt-get install x11vnc
  • 安裝好了以後
    • sudo x11vnc -storepasswd [your_password] /etc/x11vnc.pass
  • 使用
    • x11vnc
      就會開始執行了

 

然後到Windows安裝VNC Viewer 

啟動vnc這樣就可以連進來了


Windbg 指令與分析之教學筆記

$
0
0

先說一些有用的工具:Useful tools

  • Process explorer: Process Explorer是一個類似Windows「工作管理員」的程式,主要功能就是列出目前電腦中正在運行的全部程式以及跟運行中程式相關的全部詳細資料,讓我們可以在簡單的圖表與清單中,找出哪些程式佔用最多CPU資源、哪些執行序用掉最多記憶體以及其他更深入的資訊,方便對整個Windows系統做更完整的監控與管理。

    Process Explorer算是一個進階版的「工作管理員」,它可以列出比原本工作管理員還更多、更詳細的各式資料,包含執行檔的圖示、完整的影像與程式所在路徑、記憶統計、權限與安全性屬性…等,更可針對單一處理程序列出使用中的DLL檔,或透過搜尋的方式找出你要的DLL或相關資源,對於程式開發者或電腦管理原來說,應該會是個很方便的實用工具。

  • Process dump: Procdump是一個輕量級的Sysinternal團隊開發的命令行工具, 它的主要目的是監控應用程序的CPU異常動向, 並在此異常時生成crash dump文件, 供研發人員和管理員確定問題發生的原因.你還可以把它作為生成dump的工具使用在其他的腳本中.

    ProcDump與Procexp一樣是Windows平台上的內部調試工具,Procexp採用圖形界面體現系統整體及個別進程的性能信息,而ProcDump則與我們在Unix平台上使用的性能分析工具類似使用CLI命令行界面。

  • LiveKD:使用Microsoft內核調試器檢查系統。
  • NotMyFault:NotMyFault 是一款用來檢測臭蟲(Bug)並除錯的工具,由微軟公司的 Sysinternals 小組發佈。允許使用者主動以多種方式來損毀、當機或凍結系統。使用者可以透過當機後產生的傾印檔案轉存映射來分析 bug 臭蟲。另外,NotMyFault 還能更改系統當機時的預設顏色,使之不再侷限於「藍底白字(BSOD)」。
    警告:極易造成資料遺失。不建議將此工具用於在有生產用途的電腦上作檢測、除錯。使用本工具有著極大的風險,欲使用嘗試者請自行承擔風險。

    NotMyFault 使用系統驅動程式(myfault.sys – 命名是因為該驅動程式是損毀/當機的實際罪魁禍首)在記憶體中四處胡鬧,並做您命令它做的其它壞事。它也可以用於導致系統當機(所以您可以練習除錯問題)或造成重大的記憶體流失。

 

Windbg basic operations

  • Open crash dump with File > Open crash dump
  • Load symbols
  • .symfix C:\symbols: Set local symbol folder
  • .reload: Reload symbols
  • .sympath: Show current symbol path

 

List threads

  • ~: List all threads

 

Switch thread

  • ~0s: Change to thread 0
  • ~1s: Change to thread 1

 

Show stacks

  • ~*k: List all threads’ stacks
  • ~*kv: List all threads’s stacks with 3 function arguments
  • k: List current thread’s stack
  • kv: List current thread’s stack with 3 function arguments
  • kp: List current thread’s stack with all function arguments (need symbol)

 

Show environment

  • lm: List modules
  • lmv: List module versions
  • lmvm: List specific module versions

 

Crash dump automatic analysis

  • !analyze –v: Analyze a crash
  • !analyze –v –hang: Analyze a hang

 

Show exception context

  • Get context address from kv
  • .cxr <address>
    • If there is “???” shown in the result of .cxr, it indicates an invalid address access or memory corruption

 

Disassemble code

  • u: Disassemble code at the address
  • ub: Disassemble code before that address

 

CPU high analysis

  • !runaway 7: Check thread used CPU time and total elapsed time
    • If thread CPU used time occupies a large portion of total execution time, it might indicate a CPU high symptom.

 

Analyze deadlock

  • !analyze –v –hang
    • However, it might not give correct result
  • !locks
    • Show current locks
  • !cs –s –l –o
    • Show critical section owner
  • ub <return address for EnterCriticalSection line>
    • Show the critical section it is going to enter

 

Analyze Stack overflow (recursive)

  • ~*k
    • Shows a lot of same functions in call stack
  • !teb
    • Show stack base and limit
  • dps <stack limit> <stack base>
    • Show stack range content

 

Analyze .NET framework process crash

  • There is “mscorwks” in call stack
  • Show .NET version
    • lmvm mscorwks
  • Load sos extension dll
    • .load C:\Windows\Microsoft.NET\<version>\SOS.dll
    • .unload sos
  • Print .NET exception with sos extension dll
    • !pe

 

Analyze WoW64 process

  • There is “wow64” in call stack
  • Show current machine type
    • .effmach
  • Set machine type to x86
    • .effmach x86
  • !sw can switch between x86 and x64
  • .load wow64exts
    • Then we can see x86 call stacks
  • To see MessageBox parameters
    • du <argument address>

 

Analyze memory leak

  • Turn on gflags by executing Windows Kits > Global Flags > System Registry > Create user mode stack trace database
  • Show current gflag
    • !gflag
  • Show heaps
    • !heap –s
    • !heap –k –h <heap id>

 

Analyze infinite wait

  • Analyze with !analyze
    • !analyze –v –hang
  • Show WaitForMultipleObjects arguments
    • 1st argument = number of handles
    • 2nd argument = handle array
    • dp <2nd argument address>
  • Show each handle
    • !handle <handle address> f

 

Analyze kernel dump

  • List current process
    • !process
  • List all processes
    • !process 0 0
    • !process 0 7: with call stack
  • Get IRP list from output of !process <process address>
  • Show IRP
    • !irp <irp address>
  • Get device object from “Device” in IRP list, then show its info
    • !devobj <device obj address>
  • Get file object from “File” in IRP list, then show its info
    • !fileobj <file obj address>
  • Show thread info
    • !thread
    • !thread <thread address>
  • Change thread
    • .thread <thread address>
  • Show page table entry
    • !pte <address>
  • Check if is running on VM
    • lmvm vmmemctl: for VMware

 

Analyze kernel dump for memory problem

  • Show memory
    • !vm
  • If NonPagedPool Usage is close to NonPagedPool Max, or if PagedPool Usage is closed to PagedPool Max, there could be memory stress problem.
    • !poolused 2: non-paged memory
    • !poolused 4: paged memory
  • Show running processors and threads
    • !running
    • !running –i –t: include idle processor, and show call stack
  • Show system info
    • !sysinfo cpuinfo
    • !sysinfo

 

Analyze complete kernel dump

  • There is “Kernel Complete Dump File: Full address space is available” in the output of windbg
  • Change process
    • .process /r /P <process address>
  • Change thread
    • .thread <thread address>

Windows Driver 開發學習筆記

$
0
0

微軟有寫了基本概要, 先看看原廠介紹吧! 然後根據每個小課程的練習與實作, 應該就會慢慢進步了解!
連結:Windows programming guide for driver technologies

寫出你的第一個驅動程式吧!

Write your first driver

工具書書本列表:

  • Windows系統程式設計(第四版)
  • Windows Device Driver Progamming驅動程式設計
  • 深入淺出Windows驅動程式開發(平裝附光碟片)
    • 這本在台灣已經絕版, 很難買到, 所幸作者為大陸人所以有簡體字版本,[竹林蹊径 深入浅出Windows驱动开发]

啟動/激活破解工具 安裝win10, office2016, 然後破解啟用

$
0
0

搜尋”win10 下載”
就可以看到這篇文章 666Windows 10 台灣繁體正式版 ISO 下載 (官方/Google載點),
裡面有windows10 Pro版與Home版,
也各自有x86與x64版本!
我就是在此下載iso檔, 然後用傳統的光碟開機安裝法, 把原本系統槽 “C:” 直接刪掉重灌…

但是美中不足的, 安裝完畢後windows10表示尚未正式啟用,
雖然所有功能看起來都很正常, 但是畫面右下角會一直提醒尚未啟用, 好礙眼!
我又找一找網路, 果然有專門破解的工具
啟動/激活破解工具 Win10/8.1/8/7/Vista/XP,Office 2016/2013/2010/2007/2003, 我試過後成功了!

office 2016 我是在這裡找到的
免破解,微軟 Office 2016 免費下載
, 也OK!

Dism備份還原相關指令整理 (ScratchDir目錄必須存在,避免錯誤)

$
0
0

先在備份存儲分區下新建文件夾X:\sources和X:\temp (X表示備份位置,文件夾名字自定,英文)

進入winPE後,命令提示符

使用diskpart確定路徑(不同的環境下顯示的盤符可能不同)
diskpart
Diskpart>在這行字符後面直接輸入diskpart命令回車即可執行相應的操作。

幾個基本命令如下(注:// 前面為命令,//後面為命令解釋):
list disk // 列出所有接在電腦上的存儲設備,並為每個存儲設備用數字編號,通常主硬盤編號為0。
select disk N // 選中編號為N的磁盤
list part // 列出選中磁盤上所有的分區
select part N // 選中編號為N的分區
detail part // 顯示選中分區的詳細信息。其中“LTR”即為分區盤符。
exit // 退出Diskpart

這裡再介紹兩條命令(非Diskpart命令):
cd /d xxxxxx // 進入xxxx目錄。如,進入C:,則輸入cd /d C:
dir /a // 顯示當前目錄中的文件結構。

第一次備份:
Dism /Capture-image /Imagefile:X:\sources\install.wim /Scratchdir:X:\temp (/compress:maximum) /Capturedir:C:\ /Name:xxxx /Description:xxxxxx

後期備份:
Dism /Append-Image /Imagefile:X:\sources\install.wim /Scratchdir:X:\temp
(compress:maximum) /Capturedir:C:\ /Name:xxxx /Description:xxxx

/Compress:Maximum 高壓,當然對應用時更多,這一句為可選參數,默認為fast
/Description:xxxxx 可選參數,添加描述方便後期處理使用
(C表示系統安裝所在分區,xxxx中不要出現空格,每個參數以/開始、用空格隔開)

獲取WIM文件映像信息:
例如:查看 G:\sources\install.wim 映像信息
Dism /Get-WimInfo /Wimfile:G:\sources\install.wim

命令解釋:
/Get-WimInfo //顯示有關 WIM 文件中的映像的信息。
/Wimfile //指定 WIM 文件路徑。
    可以在後面添加索引號或名稱來查看某次備份的詳細信息。還可以在後面添加 >d:\list.txt 把信息導出為記事本,方便查看。如:
Dism /Get-WimInfo /Wimfile:G:\sources\install.wim /Index:1 >d:\list.txt
或:Dism /Get-WimInfo /Wimfile:G:\sources\install.wim /Name:Win8Pro-1 >d:\list.txt
    在一個映像中如果有兩個卷映像同名,就不能用指定名稱來查看這兩個卷映像的詳細信息。

具有多個卷映像的 WIM 文件中卷映像的處理:
例如:刪除 G:\sources\install.wim 中的第二次備份:               
Dism /Delete-Image /ImageFile:G:\sources\install.wim /Index:2
命令解釋:
/Delete-Image // 從具有多個卷映像的 WIM 文件刪除指定的捲映像。
    此命令僅刪除卷映像名稱與描述,而不會刪除卷映像數據。可用於防止誤應用該卷映像。刪除指定的捲映像僅刪除了名稱與描述,而不會刪除卷映像數據,所以不會減小 WIM 文件的體積。

用 /Export-Image 命令從具有多個卷映像的 WIM 文件中提取需要保留的單獨卷映像,以減小 WIM 文件的體積。
例如:從 G:\sources\install.wim 中提取第二次備份到 G:\sources 中,保存為 install-02.wim):
Dism /Export-Image /SourceImageFile:G:\sources\install.wim /SourceIndex:2 /DestinationImageFile:G:\sources\install-02.wim
命令解釋:
/Export-Image – 將指定映像的副本導出到其他文件。
/SourceImageFile – 指定映像文件來源路徑。
/SourceIndex – 指定來源索引。
    如果 Win8Pro.wim 中有五個備份,我們只想保留其中第二與第五個備份,同樣可以用 /Export-Image 命令把其中 Index:2 與 Index:5 提取出來,保存為 Win8.wim。命令如下:
Dism /Export-Image /SourceImageFile:G:\sources\install.wim /SourceIndex:2 /DestinationImageFile:G:\sources\install-02.wim
Dism /Export-Image /SourceImageFile:G:\sources\install.wim /SourceIndex:5 /DestinationImageFile:G:\sources\install-02.wim
     install02.wim 中就包涵有兩個卷映像,卷映像的索引號會發生改變,但名稱不會變。

把 Windows 系統(WIM 映像)快速安裝到任何分區:
把 G:\sources\install.wim 中第二次備份還原到 C 分區,指定臨時目錄為 G:\temp
Dism /Apply-Image /ImageFile:G:\sources\install.wim /Index:2 /ScratchDir:G:\temp /ApplyDir:C:\ /Verify
 /Apply-Image //應用一個映像。
/ApplyDir //指定應用目錄。
/Index // 指定索引。此項不能省略。
    因 Dism 安裝 WIM 映像不會像 Ghost 那樣格式化磁盤,所以如果需要可以自己格式化系統盤。
    修改映像路徑與應用目錄可把任意一個系統備份的捲映像還原到任意一個分區。修改卷映像索引號或卷映像名稱可以還原備份映像中的任意一個備份。
    
也可以用此命令把 Windows 系統(WIM 映像)快速安裝到任何分區。
例如在 PE 中把 Win8Pro 安裝到 C 分區(設 Win8ISO 用虛擬光驅加載的盤符為 E):
Dism /Apply-Image /ImageFile:E:\sources\install.wim /Index:1 /Scratchdir:X:\temp /ApplyDir:C:\
    由於 Windows 系統原始(WIM 映像)中沒有啟動引導文件,需要添加啟動引導:
bcdboot C:\windows /s C: /l zh-cn
    如果是把 Windows 8 安裝到 USB 設備中作 Windows To Go,也應添加啟動引導:
bcdboot X:\windows /s X: /l zh-cn /f ALL (X為 USB 設備的盤符)。

附錄:一些可用選項

/Verify – 指定校驗。用於檢查錯誤和文件重複。
/CheckIntegrity – 用於在捕捉、卸載、導出和提交操作中使用 .wim 文件時檢測和跟踪 .wim 文件的損壞情況。
    用於在 DISM 檢測到 .wim 文件在應用和裝載操作中發生損壞時停止操作。
/ScratchDir – 指定暫存目錄的路徑。此目錄必須存在。該目錄必須位於本地。
/Compress – 用於指定對初始捕捉操作使用的壓縮類型。
    maximum 選項能提供最佳壓縮效果,但是捕捉映像所用的時間較長。
    fast 選項能提供更快速的映像壓縮,但生成的文件比使用 maximum 選項壓縮的文件大。這也是在未指定參數時所用的默認壓縮類型。
    none 選項不會壓縮捕捉的映像。
/ConfigFile – 指定映像捕捉和壓縮命令排除列表配置文件的位置。

默認的排除列表——默認情況下,DISM.exe 工具將排除以下文件:

[ExclusionList]
\$ntfs.log
\hiberfil.sys
\pagefile.sys
\swapfile.sys
“\System Volume Information”
\RECYCLER
\Windows\CSC
[CompressionExclusionList]
*.mp3
*.zip
*.cab
\WINDOWS\inf\*.pnf

 

/ScratchDir – 指定暫存目錄的路徑。

創建或處理 Windows 映像時,你應使用帶有 DISM 的 /ScratchDir 選項,在不同的驅動器上創建臨時目錄。臨時目錄適用於許多 DISM 操作,包括捕獲映像、安裝語言包、更新或在 Windows 映像中安裝或刪除 Windows 功能。先將一些文件擴展到此臨時目錄,然後再將它們應用於 Windows 映像。

/ScratchDir
    指定用來解壓縮服務文件的臨時目錄。此目錄必須存在。
    指定在服務期間提取臨時使用的文件時要使用的臨時目錄。該目錄必須位於本地。
    安裝完成後,不再需要此目錄的內容,可以將其刪除。
    如果你不使用 /ScratchDir 選項設置臨時目錄路徑, Windows PE 將默認創建 32-MB 臨時目錄。
    如果未指定臨時目錄,將使用 \Windows\%Temp% 目錄,以及每次運行 DISM 時隨機生成的十六進制值的子目錄名稱。每次操作後,都會刪除暫存目錄中的項。
    作為最佳做法,你應使用 /ScratchDir 選項,轉而在其他有足夠空間支持任何映像管理和你所執行的服務操作的分區上指定目錄

這篇文章 Dism備份還原相關指令整理 (ScratchDir目錄必須存在,避免錯誤) 最早出現於 易春木

安裝EDK2, UDK2017, Visual studio, 以建立UEFI Compiler environment setup 步驟!

$
0
0

從這裡UDK2017 How to Build原文延伸如下, 這裡拿的UDK2017是什麼呢? 而什麼又是UDK呢?

介紹 UDK

(原文)
英文為UEFI Development Kit 表示是在做UEFI開發所需要的軟體套件, 然後UDK是在EDK2當中比較穩定的release, 因為UDK的releases 會透過Intel架構平台測試! UDK2017是基於經過驗證的EDK II快照的UDK版本。 之前的穩定分支是UDK2015。

UDK和EDK2的區別

EDKII是志願者參與開發的開源項目。 此項目的提示不斷更新,以進行修復和增強。而UDK版本代表了對英特爾架構平台進行測試的EDK II源特定修訂版的完全驗證實施。 UDK版本是可用EDK II軟件包的子集,因為並非所有軟件包都可在英特爾平台上進行驗證。 每個UDK版本都在發行說明中列出了相應的EDK II版本。 每個版本都以ZIP文件形式提供,並映射到EDK II github項目的一個分支(例如:https://github.com/tianocore/edk2/tree/UDK2017,適用於UDK2017)。

怎麼編譯UDK2017呢? How to Build UDK2017

第一步: UEFI Compiler environment setup step by step

#############################################################################
# #
# UEFI Compiler environment setup step by step (tool chain UDK2017 + Microsoft VS2015 Free) #
# #
#############################################################################

1. Install Microsoft Visual Studio 2015* in the build machine and make sure that AMD64 complier was selected when installing. (Download Visual Studio 2015 Community Edition (FREE) and Installed. https://go.microsoft.com/fwlink/?LinkId=532606&clcid=0x409 )

2. Create the full Source Code directory for the UDK2017 release
Create a working space directory in the build machine, for example, C:\MyWorkspace
Download the official UDK2017 release .zip file from the UDK2017 Release Page
Download – UDK2017 edk-vUDK2017 Workspace Source code (zip file)
Extract files in [edk2-vUDK2017] to the working space directory C:\MyWorkspace.

2.1 Download UDK2017 Tool kit (Choose one) and unzip to your WORKSPACE
SVN export http://edk2.svn.sourceforge.net/svnroot/edk2/branches/UDK2017

3. Download edk2-BaseTools-win32 and unzip to your
WORKSPACE\UDK2017\BaseTools\Bin\win32
SVN export https://svn.code.sf.net/p/edk2-toolbinaries/code/trunk/Win32

4. Download NASM 2.0.7 or later from http://www.nasm.us/ and install it to C:\Nasm. Make sure C:\Nasm is added to system environment variable ‘PATH’

5. Download and install Python2.7.x https://www.python.org/ for building the BaseTools Default install directory is: C:/Python27

5.1 Download VC compiler tools (這部分我覺得只要第一步有勾選如圖. 應可省略)
https://drive.google.com/file/d/0B-pcJAXauP7WRGFTYkZGYl9aRUk/view?usp=sharing
and override to your
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin

6.Compile the BaseTools
See: https://github.com/tianocore/tianocore.github.io/wiki/Windows-systems#compile-tools

Open a Microsoft Visual Studio* command prompt, type cd C:\MyWorkspace to enter the workspace directory
Compile the BaseTools C source tools

set PYTHON_HOME=C:\Python27
        set EDK_TOOLS_PATH=%CD%\BaseTools
        BaseTools\toolssetup.bat Rebuild

7. Build Steps *** NT32 ***

Open a Microsoft Visual Studio* command prompt, type cd C:\MyWorkspace to enter the workspace directory
Use edksetup.bat command to initialize the working environment. edksetup –nt32
Type following command to build Nt32 platform build -t VS2015x86
Upon the build completing successfully there should be the UEFI Application “HelloWorld.efi” in the C:\MyWorkspace\Build\MdeModule\DEBUG_VS2015x86\IA32 directory

或另一方式.
Setup your EDK2 compiler

A) Run <strong>Developer Command Propmt</strong> for VS2015
   B) 自己切換位置到 your WORKSPACE  (ex D:\UDK2017)
   C) Run <strong>Edk2setup</strong>
   D) 手動改Change compiler environment D:\UDK2017\Conf\<strong>target.txt</strong>
      ACTIVE_PLATFORM       = MdePkg/MdePkg.dsc
      TARGET                = RELEASE
      TARGET_ARCH           = X64
      TOOL_CHAIN_TAG        = VS2015x86

Run build in VS command line at your WORKSPACE path.

第二步: build python

先看 D:\MyWorkspace\AppPkg\Applications\Python\PythonReadMe.txt

1. Edit AppPkg/AppPkg.dsc to enable (uncomment) the PythonCore.inf line
within the [Components] section.

2. build -a X64 -p AppPkg\AppPkg.dsc -t VS2015x86

這篇文章 安裝EDK2, UDK2017, Visual studio, 以建立UEFI Compiler environment setup 步驟! 最早出現於 易春木

Video and Graphic windows driver 驅動程式開發筆記

$
0
0

* 前情提要之本文另一篇: Windows Driver 開發學習筆記
* Graphic driver要了解一下
* Umd kmd 基本概念要懂
* 會寫程式 XD

關於 WDF 、WDK、WDM、DDK 介紹

(轉自: DDK與WDK的區別與聯繫)
最近嘗試去了解WINDOWS下的驅動開發,現在總結一下最近看到的資料。

1. 專門的開發包

首先,先從基礎的東西說起,開發WINDOWS下的驅動程序,需要一個專門的開發包
如:

  • 開發 JAVA 程序,我們可能需要一個JDK
  • 開發 WINDOWS 應用程式(Applications),我們需要WINDOWS的SDK
  • 開發 WINDOWS 下的驅動程序(Drivers),我們需要一個DDK/WDK。

2. DDK(Driver Developer Kit)和 WDK(Windows Driver Kit)的區別:

這個要說說驅動相關的一些歷史:

  • (1). 95/98/ME下,驅動模型為:Vxd, 相關資料可以看《編程高手箴言》的前幾個章節,裡面有很詳細的介紹,雖然這個東西已經過時,但大概看看還是會增長見識的。
  • (2). 2000/XP/2003下,Windows採用WDM驅動模型(Windows Driver Model),開發2000/XP/2003的驅動開發包為:DDK。
  • (3). Vista及以後版本,採用了WDF驅動模型(Windows Driver Foudation),對應的開發包:WDK。

其實WDK可以看做是DDK的升級版本,現在一般的WDK是包含以前DDK相關的功能,現在XP下也可以用WDK開發驅動,WDK能編譯出2000-2008的各種驅動。

3. 副檔名的區別

  • Vxd驅動文件擴展名為:.vxd。
  • WDM和WDF驅動文件擴展名為:.sys。

4. 相關的名詞解釋

  • DDK——Driver Developer Kit,設備驅動程序開發包。
  • WDK——Windows Driver Kit,Windows驅動程序開發包,就是DDK升級改名了。
  • VxD——Virtual Device Driver,虛擬設備驅動程序,9X中的一種驅動程序。
  • WDM——Windows Driver Model,Windows驅動程序模型,98以上版本的一種驅動程序。
  • WDF——Windows Driver Frameworks, 驅動程序包括兩個類型,一個是內核級的,稱為KMDF(Kernel-Mode Driver Framework),為SYS文件;另一個是用戶級的,稱為UMDF(User-Mode Driver Framework),為DLL文件。
  • 驅動程序的文件格式都是PE格式。

windows驅動開發: 比較 WDM 與 WDF

(轉載自 yinaiyun52)

如所周知,自Windows 2000開始,開發驅動程序必以WDM為基礎的,但其開發難度之大,根本不能奢望像用戶模式應用程序開發那樣容易。為改善這種局面,微軟推出了新的驅動程序開發環境 – WDF驅動模型。

WDF驅動框架對WDM進行了一次封裝
WDF框架就好像C++中的基類一樣,且這個基類中的model, IO model ,pnp和電源管理模型;且提供了一些與操作系統相關的處理函數,這些函數好像C++中的虛函數一樣,WDF驅動中能夠對這些函數進行override;特別是Pnp管理和電源管理!基本上都由WDF框架做了,而WDF的功能驅動幾乎不要對它進行特殊的處理;

要預先指出的是,WDF這不是另起爐灶改弦更張,而是以WDM為基礎進行了建模和封裝,顯著特點是降低了開發難度。
因為:
1、 將原來普通程序設計中基於對象的技術應用到了驅動開發中。 WDM中雖也有對像模型,但與真正的基於對象技術根本就不是一回事。為了實現基於對象的技術,微軟精心設計了對像模型並進行了封裝。屬性、方法、事件等等“一個都不能少”。

2、 無論內核模式的驅動程序或者用戶模式的驅動程序,都採用同一套對像模型構建,採用同一個基礎承載。這個基礎就是WDF。 WDF雖然已經是經過封裝和定義的對像模型,但對內核模式和用戶模式對象來說,WDF又是兩者的父對象。換言之兩者都是繼承了WDF才得到的,或者都是從WDF派生而來的。相對於內核模式,派生出的對象稱為“KMD框架”即KMDF;相對於用戶模式,派生出的模型稱為“UMD框架”即UMDF。無論何種模式的框架,其內部封裝的方法、執行的行為其實還是用WDM完成的。

3、 更重要的,也是微軟反复炫耀的是封裝了驅動程序中的某些共同行為:例如即插即用和電源管理就屬於這種共同行為。因為大多數驅動程序中都需要處理即插即用和電源管理問題,據說這大概要上千行的代碼,況且,沒有相當水平還不一定能處理好。為了一勞永逸,WDF乾脆將即插即用和電源管理封裝了進了對象之內,一舉成了對象的缺省(默認)行為。

4、 改變了操作系統內核與驅動程序之間的關係,WDM驅動程序中,一方面要處理硬件,另一方面要處理驅動程序與操作系統內核的交互。現在WDF則將驅動程序與操作系統內核之間進行了分離,驅動程序與操作系統交互工作交給框架內封裝的方法(函數)完成,這樣驅動開發者只需專注處理硬件的行為即可。這不僅避免了顧此失彼兩面不周的弊端,也由於雙方的分離,對操作系統內的某些改動,硬件製造商配套驅動程序的開發都有莫大的好處。

5、 兩種模式的驅動程序(KMDF、UMDF)都使用同一環境進行構建,這一環境稱為WDK。
即KMDF,UMDF的開發環境為WDK。
Windows Device Kit (WDK): 把測試套件(test suites)集成進來,DDK 就成了WDK。 WDK是針對微軟操作系統系列的驅動器集成開發系統。它組合了Windows DDK和Hardware Compatibility Test (HCT) kits(硬件兼容性測試工具),同時提供了微軟內部用來測試Windows操作系統穩定性和可靠性的測試套件。

6、 雖然經過封裝並引入基於對象的技術,所開發的驅動程序在執行效率上並不比原來遜色。
WDF和WDM的關係有點類似於MFC和Windows SDK的關係。 WDF只是在WDM上面加了一層,來簡化driver的開發,特別是PNP,power和IO cancellation 等幾個方面有很大改進。

現在學習WDF最好的資料還是DDK帶的sample code。 WHDC有一些資料:
http://www.microsoft.com/whdc/driver/wdf/default.mspx

再繼續說新舊時代

關鍵的visual studio 2012, 新時代忽然就來了, VS2012是Win8發布之後才出來的,有很多針對Win8的特性,但也能在Win7/Vista系統上運行。

舊時代

近期的win8與win10的系統出現以前, 也就是含win7之前的系統, 如果要開發kernel/driver的驅動程式, 環境的準備比較繁瑣, 首先需要手動下載WDK並安裝, 因為開發環境是在安裝好的WDK當中。WDK是Windows Driver Kit縮寫,即Windows驅動開發包。它提供的開發環境非常簡陋,不是一個便於開發的IDE環境,而僅僅是一些散裝的編譯工具包。

新時代

基本上必須要安裝三個軟體:
1. 我這裡使用 visual studio 2015版本 (或2012之後的版本)
2. WDK 10 (或Win8之後的版本)
3. Win10 SDK

請您先下載安裝 VS2015, 再安裝 Win10 的 WDK, 然後WDK的編譯環境就會自動整合進去 VS2015裡面, 所以重新開啟VS2015的時候, 在new project 新項目嚮導裡面, 就能夠找到driver(內核驅動)項目了。

開啟 Visual Studio 2015,選擇 New Project 建立新專案:
選擇 Visual C++ > Windows Driver > WDF

跳出的分別是有自動生成代碼的KMDF和沒有自動代碼的KMDF(empty),以及UMDF
我們選擇 Kernel Mode Driver (KMDF),直接按 OK 用預設值建立一個簡單的 driver 專案

測試安裝好的 Visual Studio 2015 + Windows SDK 10 + Windows Driver Kit 10
正常的話, 可以編譯通過successfully!

微軟釋出的 Driver samples for Windows 10 – 擷取video 部分

網址: https://github.com/microsoft/Windows-driver-samples/tree/master/video/KMDOD
目前我使用的為visual studio 2015 + (WinSDK)Windows Software Development kit 10.0.14393.33 + (WDK)Windows Driver kit 10.0.14393.0

資源指令碼檔案 .rc
包含目前專案中的資源所適用之指令碼的資源指令碼檔。 .aps 檔會在您每次存檔時覆寫此檔案。
原始檔控制中包含此檔案。

/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
    SampleDisplay.rc
Abstract:
    This modules contains the version information for the Sample Display Driver
Environment:
    Kernel mode only
--*/

#include <windows.h>
#include <ntverp.h>

#define VER_FILETYPE                VFT_DRV
#define VER_FILESUBTYPE             VFT2_DRV_DISPLAY
#define VER_FILEDESCRIPTION_STR     "Microsoft Sample Display Driver"
#define VER_INTERNALNAME_STR        "SampleDisplay.sys"
#define VER_ORIGINALFILENAME_STR    "SampleDisplay.sys"

#define VER_LANGNEUTRAL
#include "common.ver"

驅動程式檔案 .inf
.INF是 Device Information File 的縮寫,是微軟為供硬體製造商散佈其設備驅動程式而發展, .INF 檔是一種具有特定格式的純文字檔,我們可說它是種安裝腳本檔(Setup Script)。為甚麼當我們在檔案總管對 .INF 檔按右鍵後,快顯功能表會出現「安裝」指令呢? 這是因為微軟已在其作業系統 Windows 中提供了 Setup API(註一),程式設計師只需用任何文書編輯軟體撰寫 .INF 檔,便可完成大部份的安裝工作。

;++
;
; Copyright (c) 2011 Microsoft Corporation
;
; Module Name:
;
;    SampleDisplay.inf
;
; Abstract:
;
;    Inf file for Kernel mode display only sample driver
;
;--

[Version]
Signature = "$Windows NT$"
Class=Display
ClassGUID={4d36e968-e325-11ce-bfc1-08002be10318}
Provider=%ProviderString%
DriverVer= 03/15/2011, 0.03.15.0011
CatalogFile=SampleDisplay.cat

[DestinationDirs]
KDODSamp.Files = 12

[SourceDisksNames]
0=%SampleDisk%

[SourceDisksFiles]
SampleDisplay.sys=0

[Manufacturer]
%ManufacturerName%=Standard,NTx86,NTamd64,NTarm,NTarm64

;
;    Allow the driver to be loaded on VGA and XGA exposed by PCI bus and ACPI
;
[Standard.NTx86]
%SampleDeviceName% = KDODSamp_Inst, PCI\CC_0300
%SampleDeviceName% = KDODSamp_Inst, PCI\CC_0301
%SampleDeviceName% = KDODSamp_Inst, ACPI\CLS_0003&SUBCLS_0000
%SampleDeviceName% = KDODSamp_Inst, ACPI\CLS_0003&SUBCLS_0001

[Standard.NTamd64]
%SampleDeviceName% = KDODSamp_Inst, PCI\CC_0300
%SampleDeviceName% = KDODSamp_Inst, PCI\CC_0301
%SampleDeviceName% = KDODSamp_Inst, ACPI\CLS_0003&SUBCLS_0000
%SampleDeviceName% = KDODSamp_Inst, ACPI\CLS_0003&SUBCLS_0001


[Standard.NTarm]
%SampleDeviceName% = KDODSamp_Inst, PCI\CC_0300
%SampleDeviceName% = KDODSamp_Inst, PCI\CC_0301
%SampleDeviceName% = KDODSamp_Inst, ACPI\CLS_0003&SUBCLS_0000
%SampleDeviceName% = KDODSamp_Inst, ACPI\CLS_0003&SUBCLS_0001


[Standard.NTarm64]
%SampleDeviceName% = KDODSamp_Inst, PCI\CC_0300
%SampleDeviceName% = KDODSamp_Inst, PCI\CC_0301
%SampleDeviceName% = KDODSamp_Inst, ACPI\CLS_0003&SUBCLS_0000
%SampleDeviceName% = KDODSamp_Inst, ACPI\CLS_0003&SUBCLS_0001


[KDODSamp_Inst]
FeatureScore=F9
CopyFiles = KDODSamp.Files

;
;   Install driver service
;
[KDODSamp_Inst.Services]
AddService = KDODSamp,0x00000002,KDODSamp_Service_Inst,KDODSamp_EventLog_Inst

[KDODSamp_Service_Inst]
ServiceType = %SERVICE_KERNEL_DRIVER%
StartType = %SERVICE_DEMAND_START%
ErrorControl = %SERVICE_ERROR_IGNORE%
ServiceBinary = %12%\SampleDisplay.sys

;
;   Display Adapter Specific Settings
;
[KDODSamp.Files]
SampleDisplay.sys

[KDODSamp_EventLog_Inst]
AddReg = KDODSamp_EventLog_Inst.AddReg

[KDODSamp_EventLog_Inst.AddReg]
HKR,,EventMessageFile,%REG_EXPAND_SZ%,"%%SystemRoot%%\System32\IoLogMsg.dll"
HKR,,TypesSupported,%REG_DWORD%,7


[Strings]

;  *******Localizable Strings*******
ProviderString="TODO-Set-Provider"
ManufacturerName="TODO-Set-Manufacturer"
SampleDisk = "Sample Disk"
SampleDeviceName = "Kernel mode display only sample driver"

;  *******Non Localizable Strings*******

SERVICE_BOOT_START = 0x0
SERVICE_SYSTEM_START = 0x1
SERVICE_AUTO_START = 0x2
SERVICE_DEMAND_START = 0x3
SERVICE_DISABLED = 0x4

SERVICE_KERNEL_DRIVER = 0x1
SERVICE_ERROR_IGNORE = 0x0
SERVICE_ERROR_NORMAL = 0x1
SERVICE_ERROR_SEVERE = 0x2
SERVICE_ERROR_CRITICAL = 0x3

REG_MULTI_SZ   = 0x00010000
REG_EXPAND_SZ = 0x00020000
REG_DWORD = 0x00010001

C++程式標頭檔案 bdd.hxx

/******************************Module*Header*******************************\
* Module Name: BDD.hxx
*
* Basic Display Driver header file
*
*
* Copyright (c) 2010 Microsoft Corporation
\**************************************************************************/

#ifndef _BDD_HXX_
#define _BDD_HXX_

extern "C"
{
    #define __CPLUSPLUS

    // Standard C-runtime headers
    #include <stddef.h>
    #include <string.h>
    #include <stdarg.h>
    #include <stdio.h>
    #include <stdlib.h>

    #include <initguid.h>

    // NTOS headers
    #include <ntddk.h>

    #ifndef FAR
    #define FAR
    #endif

    // Windows headers
    #include <windef.h>
    #include <winerror.h>

    // Windows GDI headers
    #include <wingdi.h>

    // Windows DDI headers
    #include <winddi.h>
    #include <ntddvdeo.h>

    #include <d3dkmddi.h>
    #include <d3dkmthk.h>

    #include <ntstrsafe.h>
    #include <ntintsafe.h>

    #include <dispmprt.h>
};

#define EDID_V1_BLOCK_SIZE 128

#include "BDD_ErrorLog.hxx"

#define MIN_WIDTH                    640
#define MIN_HEIGHT                   480
#define MIN_BITS_PER_PIXEL_ALLOWED     8
#define MIN_BYTES_PER_PIXEL_REPORTED   4

#define DEFAULT_WIDTH               1024
#define DEFAULT_HEIGHT               768

#define MAX_INVALID_INHERITED_WIDTH 1024
#define MAX_INVALID_INHERITED_HEIGHT 768

#define BITS_PER_BYTE                  8

typedef struct _BLT_INFO
{
    PVOID pBits;
    UINT Pitch;
    UINT BitsPerPel;
    POINT Offset; // To unrotated top-left of dirty rects
    D3DKMDT_VIDPN_PRESENT_PATH_ROTATION Rotation;
    UINT Width; // For the unrotated image
    UINT Height; // For the unrotated image
} BLT_INFO;

#define MAX_CHILDREN                   1
#define MAX_VIEWS                      1

typedef struct _BDD_FLAGS
{
    UINT DriverStarted           : 1; // ( 1) 1 after StartDevice and 0 after StopDevice
    UINT EDID_Retrieved          : 1; // ( 2) EDID was successfully retrieved
    UINT EDID_ValidChecksum      : 1; // ( 3) Retrieved EDID has a valid checksum
    UINT EDID_ValidHeader        : 1; // ( 4) Retrieved EDID has a valid header
    UINT EDID_Attempted          : 1; // ( 5) 1 if an attempt was made to retrieve the EDID, successful or not

    // IMPORTANT: All new flags must be added to just before _LastFlag (i.e. right above this comment), this allows different versions of diagnostics to still be useful.
    UINT _LastFlag               : 1; // ( 6) Always set to 1, is used to ensure that diagnostic version matches binary version
    UINT Unused                  : 26;
} BDD_FLAGS;

// Represents the current mode, may not always be set (i.e. frame buffer mapped) if representing the mode passed in on single mode setups.
typedef struct _CURRENT_BDD_MODE
{
    // The source mode currently set for HW Framebuffer
    // For sample driver this info filled in StartDevice by the OS and never changed.
    DXGK_DISPLAY_INFORMATION             DispInfo;

    // The rotation of the current mode. Rotation is performed in software during Present call
    D3DKMDT_VIDPN_PRESENT_PATH_ROTATION  Rotation;

    D3DKMDT_VIDPN_PRESENT_PATH_SCALING Scaling;
    // This mode might be different from one which are supported for HW frame buffer
    // Scaling/displasment might be needed (if supported)
    UINT SrcModeWidth;
    UINT SrcModeHeight;

    // Various boolean flags the struct uses
    struct _CURRENT_BDD_MODE_FLAGS
    {
        UINT SourceNotVisible     : 1; // 0 if source is visible
        UINT FullscreenPresent    : 1; // 0 if should use dirty rects for present
        UINT FrameBufferIsActive  : 1; // 0 if not currently active (i.e. target not connected to source)
        UINT DoNotMapOrUnmap      : 1; // 1 if the FrameBuffer should not be (un)mapped during normal execution
        UINT IsInternal           : 1; // 1 if it was determined (i.e. through ACPI) that an internal panel is being driven
        UINT Unused               : 27;
    } Flags;

    // The start and end of physical memory known to be all zeroes. Used to optimize the BlackOutScreen function to not write
    // zeroes to memory already known to be zero. (Physical address is located in DispInfo)
    PHYSICAL_ADDRESS ZeroedOutStart;
    PHYSICAL_ADDRESS ZeroedOutEnd;

    // Linear frame buffer pointer
    // A union with a ULONG64 is used here to ensure this struct looks the same on 32bit and 64bit builds
    // since the size of a VOID* changes depending on the build.
    union
    {
        VOID*                            Ptr;
        ULONG64                          Force8Bytes;
    } FrameBuffer;
} CURRENT_BDD_MODE;

class BASIC_DISPLAY_DRIVER;

class BDD_HWBLT
{
public:
    D3DDDI_VIDEO_PRESENT_SOURCE_ID  m_SourceId;
    BASIC_DISPLAY_DRIVER*           m_DevExt;
    BOOLEAN                         m_SynchExecution;
    HANDLE                          m_hPresentWorkerThread;
    PVOID                           m_pPresentWorkerThread;

    //  Events to contol thread execution
    KEVENT                          m_hThreadStartupEvent;
    KEVENT                          m_hThreadSuspendEvent;

    BDD_HWBLT();

    ~BDD_HWBLT();

    void Initialize(_In_ BASIC_DISPLAY_DRIVER* DevExt, _In_ UINT IdSrc) { m_DevExt = DevExt; m_SourceId = IdSrc; }
    void SetPresentWorkerThreadInfo(HANDLE hWorkerThread);
    NTSTATUS ExecutePresentDisplayOnly(_In_ BYTE*             DstAddr,
                                       _In_ UINT              DstBitPerPixel,
                                       _In_ BYTE*             SrcAddr,
                                       _In_ UINT              SrcBytesPerPixel,
                                       _In_ LONG              SrcPitch,
                                       _In_ ULONG             NumMoves,
                                       _In_ D3DKMT_MOVE_RECT* pMoves,
                                       _In_ ULONG             NumDirtyRects,
                                       _In_ RECT*             pDirtyRect,
                                       _In_ D3DKMDT_VIDPN_PRESENT_PATH_ROTATION Rotation);

};

class BASIC_DISPLAY_DRIVER
{
private:
    DEVICE_OBJECT* m_pPhysicalDevice;
    DXGKRNL_INTERFACE m_DxgkInterface;

    // Information passed in by StartDevice DDI
    DXGK_START_INFO m_StartInfo;


    // Array of EDIDs, currently only supporting base block, hence EDID_V1_BLOCK_SIZE for size of each EDID
    BYTE m_EDIDs[MAX_CHILDREN][EDID_V1_BLOCK_SIZE];

    CURRENT_BDD_MODE m_CurrentModes[MAX_VIEWS];


    BDD_HWBLT        m_HardwareBlt[MAX_VIEWS];

    // Current monitorpower state (this needs to be changed if MAX_CHILDREN != 1)
    DEVICE_POWER_STATE m_MonitorPowerState;

    // Current adapter power state
    DEVICE_POWER_STATE m_AdapterPowerState;

    // Source ID to be used by SystemDisplay functions
    D3DDDI_VIDEO_PRESENT_SOURCE_ID m_SystemDisplaySourceId;

    // Various boolean flags the class uses
    BDD_FLAGS m_Flags;

    // Device information
    DXGK_DEVICE_INFO m_DeviceInfo;

public:
    BASIC_DISPLAY_DRIVER(_In_ DEVICE_OBJECT* pPhysicalDeviceObject);
    ~BASIC_DISPLAY_DRIVER();

#pragma code_seg(push)
#pragma code_seg()
    BOOLEAN IsDriverActive() const
    {
        return m_Flags.DriverStarted;
    }
#pragma code_seg(pop)

    NTSTATUS StartDevice(_In_  DXGK_START_INFO*   pDxgkStartInfo,
                         _In_  DXGKRNL_INTERFACE* pDxgkInterface,
                         _Out_ ULONG*             pNumberOfViews,
                         _Out_ ULONG*             pNumberOfChildren);

    NTSTATUS StopDevice(VOID);

    // Must be Non-Paged
    VOID ResetDevice(VOID);


    const CURRENT_BDD_MODE* GetCurrentMode(UINT SourceId) const
    {
        return (SourceId < MAX_VIEWS)?&m_CurrentModes[SourceId]:NULL;
    }
    const DXGKRNL_INTERFACE* GetDxgkInterface() const { return &m_DxgkInterface;}

    // Not implemented since no IOCTLs currently handled.
    NTSTATUS DispatchIoRequest(_In_  ULONG                 VidPnSourceId,
                               _In_  VIDEO_REQUEST_PACKET* pVideoRequestPacket);

    // Used to either turn off/on monitor (if possible), or mark that system is going into hibernate
    NTSTATUS SetPowerState(_In_  ULONG              HardwareUid,
                           _In_  DEVICE_POWER_STATE DevicePowerState,
                           _In_  POWER_ACTION       ActionType);

    // Report back child capabilities
    NTSTATUS QueryChildRelations(_Out_writes_bytes_(ChildRelationsSize) DXGK_CHILD_DESCRIPTOR* pChildRelations,
                                 _In_                             ULONG                  ChildRelationsSize);

    NTSTATUS QueryChildStatus(_Inout_ DXGK_CHILD_STATUS* pChildStatus,
                              _In_    BOOLEAN            NonDestructiveOnly);

    // Return EDID if previously retrieved
    NTSTATUS QueryDeviceDescriptor(_In_    ULONG                   ChildUid,
                                   _Inout_ DXGK_DEVICE_DESCRIPTOR* pDeviceDescriptor);

    // Must be Non-Paged
    // BDD doesn't have interrupts, so just returns false
    BOOLEAN InterruptRoutine(_In_  ULONG MessageNumber);

    VOID DpcRoutine(VOID);

    // Return DriverCaps, doesn't support other queries though
    NTSTATUS QueryAdapterInfo(_In_ CONST DXGKARG_QUERYADAPTERINFO* pQueryAdapterInfo);

    NTSTATUS SetPointerPosition(_In_ CONST DXGKARG_SETPOINTERPOSITION* pSetPointerPosition);

    NTSTATUS SetPointerShape(_In_ CONST DXGKARG_SETPOINTERSHAPE* pSetPointerShape);

    NTSTATUS PresentDisplayOnly(_In_ CONST DXGKARG_PRESENT_DISPLAYONLY* pPresentDisplayOnly);

    NTSTATUS IsSupportedVidPn(_Inout_ DXGKARG_ISSUPPORTEDVIDPN* pIsSupportedVidPn);

    NTSTATUS RecommendFunctionalVidPn(_In_ CONST DXGKARG_RECOMMENDFUNCTIONALVIDPN* CONST pRecommendFunctionalVidPn);

    NTSTATUS RecommendVidPnTopology(_In_ CONST DXGKARG_RECOMMENDVIDPNTOPOLOGY* CONST pRecommendVidPnTopology);

    NTSTATUS RecommendMonitorModes(_In_ CONST DXGKARG_RECOMMENDMONITORMODES* CONST pRecommendMonitorModes);

    NTSTATUS EnumVidPnCofuncModality(_In_ CONST DXGKARG_ENUMVIDPNCOFUNCMODALITY* CONST pEnumCofuncModality);

    NTSTATUS SetVidPnSourceVisibility(_In_ CONST DXGKARG_SETVIDPNSOURCEVISIBILITY* pSetVidPnSourceVisibility);

    NTSTATUS CommitVidPn(_In_ CONST DXGKARG_COMMITVIDPN* CONST pCommitVidPn);

    NTSTATUS UpdateActiveVidPnPresentPath(_In_ CONST DXGKARG_UPDATEACTIVEVIDPNPRESENTPATH* CONST pUpdateActiveVidPnPresentPath);

    NTSTATUS QueryVidPnHWCapability(_Inout_ DXGKARG_QUERYVIDPNHWCAPABILITY* pVidPnHWCaps);

    // Part of PnPStop (PnP instance only), returns current mode information (which will be passed to fallback instance by dxgkrnl)
    NTSTATUS StopDeviceAndReleasePostDisplayOwnership(_In_  D3DDDI_VIDEO_PRESENT_TARGET_ID TargetId,
                                                      _Out_ DXGK_DISPLAY_INFORMATION*      pDisplayInfo);

    // Must be Non-Paged
    // Call to initialize as part of bugcheck
    NTSTATUS SystemDisplayEnable(_In_  D3DDDI_VIDEO_PRESENT_TARGET_ID       TargetId,
                                 _In_  PDXGKARG_SYSTEM_DISPLAY_ENABLE_FLAGS Flags,
                                 _Out_ UINT*                                pWidth,
                                 _Out_ UINT*                                pHeight,
                                 _Out_ D3DDDIFORMAT*                        pColorFormat);

    // Must be Non-Paged
    // Write out pixels as part of bugcheck
    VOID SystemDisplayWrite(_In_reads_bytes_(SourceHeight * SourceStride) VOID* pSource,
                            _In_                                     UINT  SourceWidth,
                            _In_                                     UINT  SourceHeight,
                            _In_                                     UINT  SourceStride,
                            _In_                                     INT   PositionX,
                            _In_                                     INT   PositionY);


private:
    VOID CleanUp();

    NTSTATUS CommonStart();

    NTSTATUS GetEdid(D3DDDI_VIDEO_PRESENT_TARGET_ID TargetId);


    // Given pixel format, give back the bits per pixel. Only supports pixel formats expected by BDD
    // (i.e. the ones found below in PixelFormatFromBPP or that may come in from FallbackStart)
    // This is because these two functions combine to allow BDD to store the bpp of a VBE mode in the
    // ColorFormat field of a DispInfo
    UINT BPPFromPixelFormat(D3DDDIFORMAT Format) const
    {
        switch (Format)
        {
            case D3DDDIFMT_UNKNOWN: return 0;
            case D3DDDIFMT_P8: return 8;
            case D3DDDIFMT_R5G6B5: return 16;
            case D3DDDIFMT_R8G8B8: return 24;
            case D3DDDIFMT_X8R8G8B8: // fall through
            case D3DDDIFMT_A8R8G8B8: return 32;
            default: BDD_LOG_ASSERTION1("Unknown D3DDDIFORMAT 0x%I64x", Format); return 0;
        }
    }

    // Given bits per pixel, return the pixel format at the same bpp
    D3DDDIFORMAT PixelFormatFromBPP(UINT BPP) const
    {
        switch (BPP)
        {
            case  8: return D3DDDIFMT_P8;
            case 16: return D3DDDIFMT_R5G6B5;
            case 24: return D3DDDIFMT_R8G8B8;
            case 32: return D3DDDIFMT_X8R8G8B8;
            default: BDD_LOG_ASSERTION1("A bit per pixel of 0x%I64x is not supported.", BPP); return D3DDDIFMT_UNKNOWN;
        }
    }

    // These two functions make checks on the values of some of the fields of their respective structures to ensure
    // that the specified fields are supported by BDD, i.e. gamma ramp must be D3DDDI_GAMMARAMP_DEFAULT
    NTSTATUS IsVidPnPathFieldsValid(CONST D3DKMDT_VIDPN_PRESENT_PATH* pPath) const;
    NTSTATUS IsVidPnSourceModeFieldsValid(CONST D3DKMDT_VIDPN_SOURCE_MODE* pSourceMode) const;

    VOID BlackOutScreen(D3DDDI_VIDEO_PRESENT_SOURCE_ID SourceId);

    // Returns the index into gBddBiosData.BddModes of the VBE mode that matches the given VidPnSourceMode.
    // If such a mode cannot be found, returns a number outside of [0, gBddBiosData.CountBddModes)
    UINT FindMatchingVBEMode(CONST D3DKMDT_VIDPN_SOURCE_MODE* pSourceMode) const;

    // Must be Non-Paged
    // Returns the SourceId that has TargetId as a valid frame buffer or D3DDDI_ID_UNINITIALIZED if no such SourceId exists
    D3DDDI_VIDEO_PRESENT_SOURCE_ID FindSourceForTarget(D3DDDI_VIDEO_PRESENT_TARGET_ID TargetId, BOOLEAN DefaultToZero);

    // Set the given source mode on the given path
    NTSTATUS SetSourceModeAndPath(CONST D3DKMDT_VIDPN_SOURCE_MODE* pSourceMode,
                                  CONST D3DKMDT_VIDPN_PRESENT_PATH* pPath);


    // Add the current mode to the given monitor source mode set
    NTSTATUS AddSingleMonitorMode(_In_ CONST DXGKARG_RECOMMENDMONITORMODES* CONST pRecommendMonitorModes);


    // Add the current mode to the given VidPn source mode set
    NTSTATUS AddSingleSourceMode(_In_ CONST DXGK_VIDPNSOURCEMODESET_INTERFACE* pVidPnSourceModeSetInterface,
                                 D3DKMDT_HVIDPNSOURCEMODESET hVidPnSourceModeSet,
                                 D3DDDI_VIDEO_PRESENT_SOURCE_ID SourceId);

    // Add the current mode (or the matching to pinned source mode) to the give VidPn target mode set
    NTSTATUS AddSingleTargetMode(_In_ CONST DXGK_VIDPNTARGETMODESET_INTERFACE* pVidPnTargetModeSetInterface,
                                 D3DKMDT_HVIDPNTARGETMODESET hVidPnTargetModeSet,
                                 _In_opt_ CONST D3DKMDT_VIDPN_SOURCE_MODE* pVidPnPinnedSourceModeInfo,
                                 D3DDDI_VIDEO_PRESENT_SOURCE_ID SourceId);

    // Check that the hardware the driver is running on is hardware it is capable of driving.
    NTSTATUS CheckHardware();

    // Helper function for RegisterHWInfo
    NTSTATUS WriteHWInfoStr(_In_ HANDLE DevInstRegKeyHandle, _In_ PCWSTR pszwValueName, _In_ PCSTR pszValue);

    // Set the information in the registry as described here: http://msdn.microsoft.com/en-us/library/windows/hardware/ff569240(v=vs.85).aspx
    NTSTATUS RegisterHWInfo();
};

//
// Blt functions
//

// Must be Non-Paged
VOID BltBits(
    BLT_INFO* pDst,
    CONST BLT_INFO* pSrc,
    UINT  NumRects,
    _In_reads_(NumRects) CONST RECT *pRects);

//
// Driver Entry point
//

extern "C"
DRIVER_INITIALIZE DriverEntry;

//
// PnP DDIs
//

VOID
BddDdiUnload(VOID);

// If uncommenting ENABLE_DXGK_SAL in the sources file, all the below function prototypes should be updated to use
// the function typedef's from the header files. Additionally, annotations on the function definitions can be removed
// as they are inherited from the prototype definition here. As an example the entire 4-line prototype for BddDdiAddDevice
// is replaced by the single commented line below:
// DXGKDDI_ADD_DEVICE BddDdiAddDevice;
NTSTATUS
BddDdiAddDevice(
    _In_ DEVICE_OBJECT* pPhysicalDeviceObject,
    _Outptr_ PVOID*  ppDeviceContext);

NTSTATUS
BddDdiRemoveDevice(
    _In_  VOID* pDeviceContext);

NTSTATUS
BddDdiStartDevice(
    _In_  VOID*              pDeviceContext,
    _In_  DXGK_START_INFO*   pDxgkStartInfo,
    _In_  DXGKRNL_INTERFACE* pDxgkInterface,
    _Out_ ULONG*             pNumberOfViews,
    _Out_ ULONG*             pNumberOfChildren);

NTSTATUS
BddDdiStopDevice(
    _In_  VOID* pDeviceContext);

VOID
BddDdiResetDevice(
    _In_  VOID* pDeviceContext);


NTSTATUS
BddDdiDispatchIoRequest(
    _In_  VOID*                 pDeviceContext,
    _In_  ULONG                 VidPnSourceId,
    _In_  VIDEO_REQUEST_PACKET* pVideoRequestPacket);

NTSTATUS
BddDdiSetPowerState(
    _In_  VOID*              pDeviceContext,
    _In_  ULONG              HardwareUid,
    _In_  DEVICE_POWER_STATE DevicePowerState,
    _In_  POWER_ACTION       ActionType);

NTSTATUS
BddDdiQueryChildRelations(
    _In_                             VOID*                  pDeviceContext,
    _Out_writes_bytes_(ChildRelationsSize) DXGK_CHILD_DESCRIPTOR* pChildRelations,
    _In_                             ULONG                  ChildRelationsSize);

NTSTATUS
BddDdiQueryChildStatus(
    _In_    VOID*              pDeviceContext,
    _Inout_ DXGK_CHILD_STATUS* pChildStatus,
    _In_    BOOLEAN            NonDestructiveOnly);

NTSTATUS
BddDdiQueryDeviceDescriptor(
    _In_  VOID*                     pDeviceContext,
    _In_  ULONG                     ChildUid,
    _Inout_ DXGK_DEVICE_DESCRIPTOR* pDeviceDescriptor);

// Must be Non-Paged
BOOLEAN
BddDdiInterruptRoutine(
    _In_  VOID* pDeviceContext,
    _In_  ULONG MessageNumber);

VOID
BddDdiDpcRoutine(
    _In_  VOID* pDeviceContext);

//
// WDDM Display Only Driver DDIs
//

NTSTATUS
APIENTRY
BddDdiQueryAdapterInfo(
    _In_ CONST HANDLE                         hAdapter,
    _In_ CONST DXGKARG_QUERYADAPTERINFO*      pQueryAdapterInfo);

NTSTATUS
APIENTRY
BddDdiSetPointerPosition(
    _In_ CONST HANDLE                         hAdapter,
    _In_ CONST DXGKARG_SETPOINTERPOSITION*    pSetPointerPosition);

NTSTATUS
APIENTRY
BddDdiSetPointerShape(
    _In_ CONST HANDLE                         hAdapter,
    _In_ CONST DXGKARG_SETPOINTERSHAPE*       pSetPointerShape);

NTSTATUS
APIENTRY
BddDdiPresentDisplayOnly(
    _In_ CONST HANDLE                         hAdapter,
    _In_ CONST DXGKARG_PRESENT_DISPLAYONLY*   pPresentDisplayOnly);

NTSTATUS
APIENTRY
BddDdiIsSupportedVidPn(
    _In_ CONST HANDLE                         hAdapter,
    _Inout_ DXGKARG_ISSUPPORTEDVIDPN*         pIsSupportedVidPn);

NTSTATUS
APIENTRY
BddDdiRecommendFunctionalVidPn(
    _In_ CONST HANDLE                                   hAdapter,
    _In_ CONST DXGKARG_RECOMMENDFUNCTIONALVIDPN* CONST  pRecommendFunctionalVidPn);

NTSTATUS
APIENTRY
BddDdiRecommendVidPnTopology(
    _In_ CONST HANDLE                                 hAdapter,
    _In_ CONST DXGKARG_RECOMMENDVIDPNTOPOLOGY* CONST  pRecommendVidPnTopology);

NTSTATUS
APIENTRY
BddDdiRecommendMonitorModes(
    _In_ CONST HANDLE                                hAdapter,
    _In_ CONST DXGKARG_RECOMMENDMONITORMODES* CONST  pRecommendMonitorModes);

NTSTATUS
APIENTRY
BddDdiEnumVidPnCofuncModality(
    _In_ CONST HANDLE                                  hAdapter,
    _In_ CONST DXGKARG_ENUMVIDPNCOFUNCMODALITY* CONST  pEnumCofuncModality);

NTSTATUS
APIENTRY
BddDdiSetVidPnSourceVisibility(
    _In_ CONST HANDLE                             hAdapter,
    _In_ CONST DXGKARG_SETVIDPNSOURCEVISIBILITY*  pSetVidPnSourceVisibility);

NTSTATUS
APIENTRY
BddDdiCommitVidPn(
    _In_ CONST HANDLE                         hAdapter,
    _In_ CONST DXGKARG_COMMITVIDPN* CONST     pCommitVidPn);

NTSTATUS
APIENTRY
BddDdiUpdateActiveVidPnPresentPath(
    _In_ CONST HANDLE                                       hAdapter,
    _In_ CONST DXGKARG_UPDATEACTIVEVIDPNPRESENTPATH* CONST  pUpdateActiveVidPnPresentPath);

NTSTATUS
APIENTRY
BddDdiQueryVidPnHWCapability(
    _In_ CONST HANDLE                         hAdapter,
    _Inout_ DXGKARG_QUERYVIDPNHWCAPABILITY*   pVidPnHWCaps);

NTSTATUS
APIENTRY
BddDdiStopDeviceAndReleasePostDisplayOwnership(
    _In_  VOID*                          pDeviceContext,
    _In_  D3DDDI_VIDEO_PRESENT_TARGET_ID TargetId,
    _Out_ DXGK_DISPLAY_INFORMATION*      DisplayInfo);

// Must be Non-Paged
NTSTATUS
APIENTRY
BddDdiSystemDisplayEnable(
    _In_  VOID* pDeviceContext,
    _In_  D3DDDI_VIDEO_PRESENT_TARGET_ID TargetId,
    _In_  PDXGKARG_SYSTEM_DISPLAY_ENABLE_FLAGS Flags,
    _Out_ UINT* Width,
    _Out_ UINT* Height,
    _Out_ D3DDDIFORMAT* ColorFormat);

// Must be Non-Paged
VOID
APIENTRY
BddDdiSystemDisplayWrite(
    _In_  VOID* pDeviceContext,
    _In_  VOID* Source,
    _In_  UINT  SourceWidth,
    _In_  UINT  SourceHeight,
    _In_  UINT  SourceStride,
    _In_  UINT  PositionX,
    _In_  UINT  PositionY);

//
// Frame buffer map/unmap
//

NTSTATUS
MapFrameBuffer(
    _In_                       PHYSICAL_ADDRESS    PhysicalAddress,
    _In_                       ULONG               Length,
    _Outptr_result_bytebuffer_(Length) VOID**              VirtualAddress);

NTSTATUS
UnmapFrameBuffer(
    _In_reads_bytes_(Length) VOID* VirtualAddress,
    _In_                ULONG Length);


BOOLEAN
IsEdidHeaderValid(_In_reads_bytes_(EDID_V1_BLOCK_SIZE) const BYTE* pEdid);

BOOLEAN
IsEdidChecksumValid(_In_reads_bytes_(EDID_V1_BLOCK_SIZE) const BYTE* pEdid);

//
// Memory handling
//

// Defaulting the value of PoolType means that any call to new Foo()
// will raise a compiler error for being ambiguous. This is to help keep
// any calls to allocate memory from accidentally NOT going through
// these functions.
_When_((PoolType & NonPagedPoolMustSucceed) != 0,
    __drv_reportError("Must succeed pool allocations are forbidden. "
            "Allocation failures cause a system crash"))
void* __cdecl operator new(size_t Size, POOL_TYPE PoolType = PagedPool);
_When_((PoolType & NonPagedPoolMustSucceed) != 0,
    __drv_reportError("Must succeed pool allocations are forbidden. "
            "Allocation failures cause a system crash"))
void* __cdecl operator new[](size_t Size, POOL_TYPE PoolType = PagedPool);
void  __cdecl operator delete(void* pObject);
void  __cdecl operator delete(void* pObject, size_t s);
void  __cdecl operator delete[](void* pObject);

// Pool allocation tag for the Sample Display Driver. All allocations use this tag.
#define BDDTAG 'DDBS'

#endif // _BDD_HXX_

C++程式檔案 bdd.cxx

/******************************Module*Header*******************************\
* Module Name: bdd.cxx
*
* Basic Display Driver functions implementation
*
*
* Copyright (c) 2010 Microsoft Corporation
\**************************************************************************/


#include "BDD.hxx"

#pragma code_seg("PAGE")


BASIC_DISPLAY_DRIVER::BASIC_DISPLAY_DRIVER(_In_ DEVICE_OBJECT* pPhysicalDeviceObject) : m_pPhysicalDevice(pPhysicalDeviceObject),
                                                                                        m_MonitorPowerState(PowerDeviceD0),
                                                                                        m_AdapterPowerState(PowerDeviceD0)
{
    PAGED_CODE();
    *((UINT*)&m_Flags) = 0;
    m_Flags._LastFlag = TRUE;
    RtlZeroMemory(&m_DxgkInterface, sizeof(m_DxgkInterface));
    RtlZeroMemory(&m_StartInfo, sizeof(m_StartInfo));
    RtlZeroMemory(m_CurrentModes, sizeof(m_CurrentModes));
    RtlZeroMemory(&m_DeviceInfo, sizeof(m_DeviceInfo));


    for (UINT i=0;i<MAX_VIEWS;i++)
    {
        m_HardwareBlt[i].Initialize(this,i);
    }
}

BASIC_DISPLAY_DRIVER::~BASIC_DISPLAY_DRIVER()
{
    PAGED_CODE();


    CleanUp();
}


NTSTATUS BASIC_DISPLAY_DRIVER::StartDevice(_In_  DXGK_START_INFO*   pDxgkStartInfo,
                                           _In_  DXGKRNL_INTERFACE* pDxgkInterface,
                                           _Out_ ULONG*             pNumberOfViews,
                                           _Out_ ULONG*             pNumberOfChildren)
{
    PAGED_CODE();

    BDD_ASSERT(pDxgkStartInfo != NULL);
    BDD_ASSERT(pDxgkInterface != NULL);
    BDD_ASSERT(pNumberOfViews != NULL);
    BDD_ASSERT(pNumberOfChildren != NULL);

    RtlCopyMemory(&m_StartInfo, pDxgkStartInfo, sizeof(m_StartInfo));
    RtlCopyMemory(&m_DxgkInterface, pDxgkInterface, sizeof(m_DxgkInterface));
    RtlZeroMemory(m_CurrentModes, sizeof(m_CurrentModes));
    m_CurrentModes[0].DispInfo.TargetId = D3DDDI_ID_UNINITIALIZED;

    // Get device information from OS.
    NTSTATUS Status = m_DxgkInterface.DxgkCbGetDeviceInformation(m_DxgkInterface.DeviceHandle, &m_DeviceInfo);
    if (!NT_SUCCESS(Status))
    {
        BDD_LOG_ASSERTION1("DxgkCbGetDeviceInformation failed with status 0x%I64x",
                           Status);
        return Status;
    }

    // Ignore return value, since it's not the end of the world if we failed to write these values to the registry
    RegisterHWInfo();

    // TODO: Uncomment the line below after updating the TODOs in the function CheckHardware
//    Status = CheckHardware();
    if (!NT_SUCCESS(Status))
    {
        return Status;
    }

    // This sample driver only uses the frame buffer of the POST device. DxgkCbAcquirePostDisplayOwnership
    // gives you the frame buffer address and ensures that no one else is drawing to it. Be sure to give it back!
    Status = m_DxgkInterface.DxgkCbAcquirePostDisplayOwnership(m_DxgkInterface.DeviceHandle, &(m_CurrentModes[0].DispInfo));
    if (!NT_SUCCESS(Status) || m_CurrentModes[0].DispInfo.Width == 0)
    {
        // The most likely cause of failure is that the driver is simply not running on a POST device, or we are running
        // after a pre-WDDM 1.2 driver. Since we can't draw anything, we should fail to start.
        return STATUS_UNSUCCESSFUL;
    }
    m_Flags.DriverStarted = TRUE;
    *pNumberOfViews = MAX_VIEWS;
    *pNumberOfChildren = MAX_CHILDREN;


   return STATUS_SUCCESS;
}

NTSTATUS BASIC_DISPLAY_DRIVER::StopDevice(VOID)
{
    PAGED_CODE();

    CleanUp();

    m_Flags.DriverStarted = FALSE;

    return STATUS_SUCCESS;
}

VOID BASIC_DISPLAY_DRIVER::CleanUp()
{
    PAGED_CODE();

    for (UINT Source = 0; Source < MAX_VIEWS; ++Source)
    {
        if (m_CurrentModes[Source].FrameBuffer.Ptr)
        {
            UnmapFrameBuffer(m_CurrentModes[Source].FrameBuffer.Ptr, m_CurrentModes[Source].DispInfo.Height * m_CurrentModes[Source].DispInfo.Pitch);
            m_CurrentModes[Source].FrameBuffer.Ptr = NULL;
            m_CurrentModes[Source].Flags.FrameBufferIsActive = FALSE;
        }
    }

}


NTSTATUS BASIC_DISPLAY_DRIVER::DispatchIoRequest(_In_  ULONG                 VidPnSourceId,
                                                 _In_  VIDEO_REQUEST_PACKET* pVideoRequestPacket)
{
    PAGED_CODE();

    BDD_ASSERT(pVideoRequestPacket != NULL);
    BDD_ASSERT(VidPnSourceId < MAX_VIEWS);

    return STATUS_NOT_IMPLEMENTED;
}

NTSTATUS BASIC_DISPLAY_DRIVER::SetPowerState(_In_  ULONG              HardwareUid,
                                             _In_  DEVICE_POWER_STATE DevicePowerState,
                                             _In_  POWER_ACTION       ActionType)
{
    PAGED_CODE();

    UNREFERENCED_PARAMETER(ActionType);

    BDD_ASSERT((HardwareUid < MAX_CHILDREN) || (HardwareUid == DISPLAY_ADAPTER_HW_ID));

    if (HardwareUid == DISPLAY_ADAPTER_HW_ID)
    {
        if (DevicePowerState == PowerDeviceD0)
        {

            // When returning from D3 the device visibility defined to be off for all targets
            if (m_AdapterPowerState == PowerDeviceD3)
            {
                DXGKARG_SETVIDPNSOURCEVISIBILITY Visibility;
                Visibility.VidPnSourceId = D3DDDI_ID_ALL;
                Visibility.Visible = FALSE;
                SetVidPnSourceVisibility(&Visibility);
            }
        }

        // Store new adapter power state
        m_AdapterPowerState = DevicePowerState;

        // There is nothing to do to specifically power up/down the display adapter
        return STATUS_SUCCESS;
    }
    else
    {
        // TODO: This is where the specified monitor should be powered up/down
        NOTHING;
        return STATUS_SUCCESS;
    }
}

NTSTATUS BASIC_DISPLAY_DRIVER::QueryChildRelations(_Out_writes_bytes_(ChildRelationsSize) DXGK_CHILD_DESCRIPTOR* pChildRelations,
                                                   _In_                             ULONG                  ChildRelationsSize)
{
    PAGED_CODE();

    BDD_ASSERT(pChildRelations != NULL);

    // The last DXGK_CHILD_DESCRIPTOR in the array of pChildRelations must remain zeroed out, so we subtract this from the count
    ULONG ChildRelationsCount = (ChildRelationsSize / sizeof(DXGK_CHILD_DESCRIPTOR)) - 1;
    BDD_ASSERT(ChildRelationsCount <= MAX_CHILDREN);

    for (UINT ChildIndex = 0; ChildIndex < ChildRelationsCount; ++ChildIndex)
    {
        pChildRelations[ChildIndex].ChildDeviceType = TypeVideoOutput;
        pChildRelations[ChildIndex].ChildCapabilities.HpdAwareness = HpdAwarenessInterruptible;
        pChildRelations[ChildIndex].ChildCapabilities.Type.VideoOutput.InterfaceTechnology = m_CurrentModes[0].Flags.IsInternal ? D3DKMDT_VOT_INTERNAL : D3DKMDT_VOT_OTHER;
        pChildRelations[ChildIndex].ChildCapabilities.Type.VideoOutput.MonitorOrientationAwareness = D3DKMDT_MOA_NONE;
        pChildRelations[ChildIndex].ChildCapabilities.Type.VideoOutput.SupportsSdtvModes = FALSE;
        // TODO: Replace 0 with the actual ACPI ID of the child device, if available
        pChildRelations[ChildIndex].AcpiUid = 0;
        pChildRelations[ChildIndex].ChildUid = ChildIndex;
    }

    return STATUS_SUCCESS;
}

NTSTATUS BASIC_DISPLAY_DRIVER::QueryChildStatus(_Inout_ DXGK_CHILD_STATUS* pChildStatus,
                                                _In_    BOOLEAN            NonDestructiveOnly)
{
    PAGED_CODE();

    UNREFERENCED_PARAMETER(NonDestructiveOnly);
    BDD_ASSERT(pChildStatus != NULL);
    BDD_ASSERT(pChildStatus->ChildUid < MAX_CHILDREN);

    switch (pChildStatus->Type)
    {
        case StatusConnection:
        {
            // HpdAwarenessInterruptible was reported since HpdAwarenessNone is deprecated.
            // However, BDD has no knowledge of HotPlug events, so just always return connected.
            pChildStatus->HotPlug.Connected = IsDriverActive();
            return STATUS_SUCCESS;
        }

        case StatusRotation:
        {
            // D3DKMDT_MOA_NONE was reported, so this should never be called
            BDD_LOG_ERROR0("Child status being queried for StatusRotation even though D3DKMDT_MOA_NONE was reported");
            return STATUS_INVALID_PARAMETER;
        }

        default:
        {
            BDD_LOG_WARNING1("Unknown pChildStatus->Type (0x%I64x) requested.", pChildStatus->Type);
            return STATUS_NOT_SUPPORTED;
        }
    }
}

// EDID retrieval
NTSTATUS BASIC_DISPLAY_DRIVER::QueryDeviceDescriptor(_In_    ULONG                   ChildUid,
                                                     _Inout_ DXGK_DEVICE_DESCRIPTOR* pDeviceDescriptor)
{
    PAGED_CODE();

    BDD_ASSERT(pDeviceDescriptor != NULL);
    BDD_ASSERT(ChildUid < MAX_CHILDREN);

    // If we haven't successfully retrieved an EDID yet (invalid ones are ok, so long as it was retrieved)
    if (!m_Flags.EDID_Attempted)
    {
        GetEdid(ChildUid);
    }

    if (!m_Flags.EDID_Retrieved || !m_Flags.EDID_ValidHeader || !m_Flags.EDID_ValidChecksum)
    {
        // Report no EDID if a valid one wasn't retrieved
        return STATUS_GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED;
    }
    else if (pDeviceDescriptor->DescriptorOffset == 0)
    {
        // Only the base block is supported
        RtlCopyMemory(pDeviceDescriptor->DescriptorBuffer,
                      m_EDIDs[ChildUid],
                      min(pDeviceDescriptor->DescriptorLength, EDID_V1_BLOCK_SIZE));

        return STATUS_SUCCESS;
    }
    else
    {
        return STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA;
    }
}

NTSTATUS BASIC_DISPLAY_DRIVER::QueryAdapterInfo(_In_ CONST DXGKARG_QUERYADAPTERINFO* pQueryAdapterInfo)
{
    PAGED_CODE();

    BDD_ASSERT(pQueryAdapterInfo != NULL);

    switch (pQueryAdapterInfo->Type)
    {
        case DXGKQAITYPE_DRIVERCAPS:
        {
            if (pQueryAdapterInfo->OutputDataSize < sizeof(DXGK_DRIVERCAPS))
            {
                BDD_LOG_ERROR2("pQueryAdapterInfo->OutputDataSize (0x%I64x) is smaller than sizeof(DXGK_DRIVERCAPS) (0x%I64x)", pQueryAdapterInfo->OutputDataSize, sizeof(DXGK_DRIVERCAPS));
                return STATUS_BUFFER_TOO_SMALL;
            }

            DXGK_DRIVERCAPS* pDriverCaps = (DXGK_DRIVERCAPS*)pQueryAdapterInfo->pOutputData;

            // Nearly all fields must be initialized to zero, so zero out to start and then change those that are non-zero.
            // Fields are zero since BDD is Display-Only and therefore does not support any of the render related fields.
            // It also doesn't support hardware interrupts, gamma ramps, etc.
            RtlZeroMemory(pDriverCaps, sizeof(DXGK_DRIVERCAPS));

            pDriverCaps->WDDMVersion = DXGKDDI_WDDMv1_2;
            pDriverCaps->HighestAcceptableAddress.QuadPart = -1;

            pDriverCaps->SupportNonVGA = TRUE;
            pDriverCaps->SupportSmoothRotation = TRUE;

            return STATUS_SUCCESS;
        }

        case DXGKQAITYPE_DISPLAY_DRIVERCAPS_EXTENSION:
        {
            DXGK_DISPLAY_DRIVERCAPS_EXTENSION* pDriverDisplayCaps;

            if (pQueryAdapterInfo->OutputDataSize < sizeof(*pDriverDisplayCaps))
            {
                BDD_LOG_ERROR2("pQueryAdapterInfo->OutputDataSize (0x%I64x) is smaller than sizeof(DXGK_DISPLAY_DRIVERCAPS_EXTENSION) (0x%I64x)",
                               pQueryAdapterInfo->OutputDataSize,
                               sizeof(DXGK_DISPLAY_DRIVERCAPS_EXTENSION));

                return STATUS_INVALID_PARAMETER;
            }

            pDriverDisplayCaps = (DXGK_DISPLAY_DRIVERCAPS_EXTENSION*)pQueryAdapterInfo->pOutputData;

            // Reset all caps values
            RtlZeroMemory(pDriverDisplayCaps, pQueryAdapterInfo->OutputDataSize);

            // We claim to support virtual display mode.
            pDriverDisplayCaps->VirtualModeSupport = 1;

            return STATUS_SUCCESS;
        }

        default:
        {
            // BDD does not need to support any other adapter information types
            BDD_LOG_WARNING1("Unknown QueryAdapterInfo Type (0x%I64x) requested", pQueryAdapterInfo->Type);
            return STATUS_NOT_SUPPORTED;
        }
    }
}


NTSTATUS BASIC_DISPLAY_DRIVER::CheckHardware()
{
    PAGED_CODE();

    NTSTATUS Status;
    ULONG VendorID;
    ULONG DeviceID;

// TODO: If developing a driver for PCI based hardware, then use the second method to retrieve Vendor/Device IDs.
// If developing for non-PCI based hardware (i.e. ACPI based hardware), use the first method to retrieve the IDs.
#if 1 // ACPI-based device

    // Get the Vendor & Device IDs on non-PCI system
    ACPI_EVAL_INPUT_BUFFER_COMPLEX AcpiInputBuffer = {0};
    AcpiInputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_COMPLEX_SIGNATURE;
    AcpiInputBuffer.MethodNameAsUlong = ACPI_METHOD_HARDWARE_ID;
    AcpiInputBuffer.Size = 0;
    AcpiInputBuffer.ArgumentCount = 0;

    BYTE OutputBuffer[sizeof(ACPI_EVAL_OUTPUT_BUFFER) + 0x10];
    RtlZeroMemory(OutputBuffer, sizeof(OutputBuffer));
    ACPI_EVAL_OUTPUT_BUFFER* pAcpiOutputBuffer = reinterpret_cast<ACPI_EVAL_OUTPUT_BUFFER*>(&OutputBuffer);

    Status = m_DxgkInterface.DxgkCbEvalAcpiMethod(m_DxgkInterface.DeviceHandle,
                                                  DISPLAY_ADAPTER_HW_ID,
                                                  &AcpiInputBuffer,
                                                  sizeof(AcpiInputBuffer),
                                                  pAcpiOutputBuffer,
                                                  sizeof(OutputBuffer));
    if (!NT_SUCCESS(Status))
    {
        BDD_LOG_ERROR1("DxgkCbReadDeviceSpace failed to get hardware IDs with status 0x%I64x", Status);
        return Status;
    }

    VendorID = ((ULONG*)(pAcpiOutputBuffer->Argument[0].Data))[0];
    DeviceID = ((ULONG*)(pAcpiOutputBuffer->Argument[0].Data))[1];

#else // PCI-based device

    // Get the Vendor & Device IDs on PCI system
    PCI_COMMON_HEADER Header = {0};
    ULONG BytesRead;

    Status = m_DxgkInterface.DxgkCbReadDeviceSpace(m_DxgkInterface.DeviceHandle,
                                                   DXGK_WHICHSPACE_CONFIG,
                                                   &Header,
                                                   0,
                                                   sizeof(Header),
                                                   &BytesRead);

    if (!NT_SUCCESS(Status))
    {
        BDD_LOG_ERROR1("DxgkCbReadDeviceSpace failed with status 0x%I64x", Status);
        return Status;
    }

    VendorID = Header.VendorID;
    DeviceID = Header.DeviceID;

#endif

    // TODO: Replace 0x1414 with your Vendor ID
    if (VendorID == 0x1414)
    {
        switch (DeviceID)
        {
            // TODO: Replace the case statements below with the Device IDs supported by this driver
            case 0x0000:
            case 0xFFFF: return STATUS_SUCCESS;
        }
    }

    return STATUS_GRAPHICS_DRIVER_MISMATCH;
}

// Even though Sample Basic Display Driver does not support hardware cursors, and reports such
// in QueryAdapterInfo. This function can still be called to set the pointer to not visible
NTSTATUS BASIC_DISPLAY_DRIVER::SetPointerPosition(_In_ CONST DXGKARG_SETPOINTERPOSITION* pSetPointerPosition)
{
    PAGED_CODE();

    BDD_ASSERT(pSetPointerPosition != NULL);
    BDD_ASSERT(pSetPointerPosition->VidPnSourceId < MAX_VIEWS);

    if (!(pSetPointerPosition->Flags.Visible))
    {
        return STATUS_SUCCESS;
    }
    else
    {
        BDD_LOG_ASSERTION0("SetPointerPosition should never be called to set the pointer to visible since BDD doesn't support hardware cursors.");
        return STATUS_UNSUCCESSFUL;
    }
}

// Basic Sample Display Driver does not support hardware cursors, and reports such
// in QueryAdapterInfo. Therefore this function should never be called.
NTSTATUS BASIC_DISPLAY_DRIVER::SetPointerShape(_In_ CONST DXGKARG_SETPOINTERSHAPE* pSetPointerShape)
{
    PAGED_CODE();

    BDD_ASSERT(pSetPointerShape != NULL);
    BDD_LOG_ASSERTION0("SetPointerShape should never be called since BDD doesn't support hardware cursors.");

    return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS BASIC_DISPLAY_DRIVER::PresentDisplayOnly(_In_ CONST DXGKARG_PRESENT_DISPLAYONLY* pPresentDisplayOnly)
{
    PAGED_CODE();

    BDD_ASSERT(pPresentDisplayOnly != NULL);
    BDD_ASSERT(pPresentDisplayOnly->VidPnSourceId < MAX_VIEWS);

    if (pPresentDisplayOnly->BytesPerPixel < MIN_BYTES_PER_PIXEL_REPORTED)
    {
        // Only >=32bpp modes are reported, therefore this Present should never pass anything less than 4 bytes per pixel
        BDD_LOG_ERROR1("pPresentDisplayOnly->BytesPerPixel is 0x%I64x, which is lower than the allowed.", pPresentDisplayOnly->BytesPerPixel);
        return STATUS_INVALID_PARAMETER;
    }

    // If it is in monitor off state or source is not supposed to be visible, don't present anything to the screen
    if ((m_MonitorPowerState > PowerDeviceD0) ||
        (m_CurrentModes[pPresentDisplayOnly->VidPnSourceId].Flags.SourceNotVisible))
    {
        return STATUS_SUCCESS;
    }

    // Present is only valid if the target is actively connected to this source
    if (m_CurrentModes[pPresentDisplayOnly->VidPnSourceId].Flags.FrameBufferIsActive)
    {

        // If actual pixels are coming through, will need to completely zero out physical address next time in BlackOutScreen
        m_CurrentModes[pPresentDisplayOnly->VidPnSourceId].ZeroedOutStart.QuadPart = 0;
        m_CurrentModes[pPresentDisplayOnly->VidPnSourceId].ZeroedOutEnd.QuadPart = 0;


        D3DKMDT_VIDPN_PRESENT_PATH_ROTATION RotationNeededByFb = pPresentDisplayOnly->Flags.Rotate ?
                                                                 m_CurrentModes[pPresentDisplayOnly->VidPnSourceId].Rotation :
                                                                 D3DKMDT_VPPR_IDENTITY;
            BYTE* pDst = (BYTE*)m_CurrentModes[pPresentDisplayOnly->VidPnSourceId].FrameBuffer.Ptr;
            UINT DstBitPerPixel = BPPFromPixelFormat(m_CurrentModes[pPresentDisplayOnly->VidPnSourceId].DispInfo.ColorFormat);
            if (m_CurrentModes[pPresentDisplayOnly->VidPnSourceId].Scaling == D3DKMDT_VPPS_CENTERED)
            {
                UINT CenterShift = (m_CurrentModes[pPresentDisplayOnly->VidPnSourceId].DispInfo.Height -
                    m_CurrentModes[pPresentDisplayOnly->VidPnSourceId].SrcModeHeight)*m_CurrentModes[pPresentDisplayOnly->VidPnSourceId].DispInfo.Pitch;
                CenterShift += (m_CurrentModes[pPresentDisplayOnly->VidPnSourceId].DispInfo.Width -
                    m_CurrentModes[pPresentDisplayOnly->VidPnSourceId].SrcModeWidth)*DstBitPerPixel/8;
                pDst += (int)CenterShift/2;
            }
            return m_HardwareBlt[pPresentDisplayOnly->VidPnSourceId].ExecutePresentDisplayOnly(pDst,
                                                                    DstBitPerPixel,
                                                                    (BYTE*)pPresentDisplayOnly->pSource,
                                                                    pPresentDisplayOnly->BytesPerPixel,
                                                                    pPresentDisplayOnly->Pitch,
                                                                    pPresentDisplayOnly->NumMoves,
                                                                    pPresentDisplayOnly->pMoves,
                                                                    pPresentDisplayOnly->NumDirtyRects,
                                                                    pPresentDisplayOnly->pDirtyRect,
                                                                    RotationNeededByFb);
    }

    return STATUS_SUCCESS;
}

NTSTATUS BASIC_DISPLAY_DRIVER::StopDeviceAndReleasePostDisplayOwnership(_In_  D3DDDI_VIDEO_PRESENT_TARGET_ID TargetId,
                                                                        _Out_ DXGK_DISPLAY_INFORMATION*      pDisplayInfo)
{
    PAGED_CODE();

    BDD_ASSERT(TargetId < MAX_CHILDREN);


    D3DDDI_VIDEO_PRESENT_SOURCE_ID SourceId = FindSourceForTarget(TargetId, TRUE);

    // In case BDD is the next driver to run, the monitor should not be off, since
    // this could cause the BIOS to hang when the EDID is retrieved on Start.
    if (m_MonitorPowerState > PowerDeviceD0)
    {
        SetPowerState(TargetId, PowerDeviceD0, PowerActionNone);
    }

    // The driver has to black out the display and ensure it is visible when releasing ownership
    BlackOutScreen(SourceId);

    *pDisplayInfo = m_CurrentModes[SourceId].DispInfo;

    return StopDevice();
}

NTSTATUS BASIC_DISPLAY_DRIVER::QueryVidPnHWCapability(_Inout_ DXGKARG_QUERYVIDPNHWCAPABILITY* pVidPnHWCaps)
{
    PAGED_CODE();

    BDD_ASSERT(pVidPnHWCaps != NULL);
    BDD_ASSERT(pVidPnHWCaps->SourceId < MAX_VIEWS);
    BDD_ASSERT(pVidPnHWCaps->TargetId < MAX_CHILDREN);

    pVidPnHWCaps->VidPnHWCaps.DriverRotation             = 1; // BDD does rotation in software
    pVidPnHWCaps->VidPnHWCaps.DriverScaling              = 0; // BDD does not support scaling
    pVidPnHWCaps->VidPnHWCaps.DriverCloning              = 0; // BDD does not support clone
    pVidPnHWCaps->VidPnHWCaps.DriverColorConvert         = 1; // BDD does color conversions in software
    pVidPnHWCaps->VidPnHWCaps.DriverLinkedAdapaterOutput = 0; // BDD does not support linked adapters
    pVidPnHWCaps->VidPnHWCaps.DriverRemoteDisplay        = 0; // BDD does not support remote displays

    return STATUS_SUCCESS;
}

NTSTATUS BASIC_DISPLAY_DRIVER::GetEdid(D3DDDI_VIDEO_PRESENT_TARGET_ID TargetId)
{
    PAGED_CODE();

    BDD_ASSERT_CHK(!m_Flags.EDID_Attempted);

    NTSTATUS Status = STATUS_SUCCESS;
    RtlZeroMemory(m_EDIDs[TargetId], sizeof(m_EDIDs[TargetId]));


    m_Flags.EDID_Attempted = TRUE;

    return Status;
}

VOID BASIC_DISPLAY_DRIVER::BlackOutScreen(D3DDDI_VIDEO_PRESENT_SOURCE_ID SourceId)
{
    PAGED_CODE();


    UINT ScreenHeight = m_CurrentModes[SourceId].DispInfo.Height;
    UINT ScreenPitch = m_CurrentModes[SourceId].DispInfo.Pitch;

    PHYSICAL_ADDRESS NewPhysAddrStart = m_CurrentModes[SourceId].DispInfo.PhysicAddress;
    PHYSICAL_ADDRESS NewPhysAddrEnd;
    NewPhysAddrEnd.QuadPart = NewPhysAddrStart.QuadPart + (ScreenHeight * ScreenPitch);

    if (m_CurrentModes[SourceId].Flags.FrameBufferIsActive)
    {
        BYTE* MappedAddr = reinterpret_cast<BYTE*>(m_CurrentModes[SourceId].FrameBuffer.Ptr);

        // Zero any memory at the start that hasn't been zeroed recently
        if (NewPhysAddrStart.QuadPart < m_CurrentModes[SourceId].ZeroedOutStart.QuadPart)
        {
            if (NewPhysAddrEnd.QuadPart < m_CurrentModes[SourceId].ZeroedOutStart.QuadPart)
            {
                // No overlap
                RtlZeroMemory(MappedAddr, ScreenHeight * ScreenPitch);
            }
            else
            {
                RtlZeroMemory(MappedAddr, (UINT)(m_CurrentModes[SourceId].ZeroedOutStart.QuadPart - NewPhysAddrStart.QuadPart));
            }
        }

        // Zero any memory at the end that hasn't been zeroed recently
        if (NewPhysAddrEnd.QuadPart > m_CurrentModes[SourceId].ZeroedOutEnd.QuadPart)
        {
            if (NewPhysAddrStart.QuadPart > m_CurrentModes[SourceId].ZeroedOutEnd.QuadPart)
            {
                // No overlap
                // NOTE: When actual pixels were the most recent thing drawn, ZeroedOutStart & ZeroedOutEnd will both be 0
                // and this is the path that will be used to black out the current screen.
                RtlZeroMemory(MappedAddr, ScreenHeight * ScreenPitch);
            }
            else
            {
                RtlZeroMemory(MappedAddr, (UINT)(NewPhysAddrEnd.QuadPart - m_CurrentModes[SourceId].ZeroedOutEnd.QuadPart));
            }
        }
    }

    m_CurrentModes[SourceId].ZeroedOutStart.QuadPart = NewPhysAddrStart.QuadPart;
    m_CurrentModes[SourceId].ZeroedOutEnd.QuadPart = NewPhysAddrEnd.QuadPart;

}

NTSTATUS BASIC_DISPLAY_DRIVER::WriteHWInfoStr(_In_ HANDLE DevInstRegKeyHandle, _In_ PCWSTR pszwValueName, _In_ PCSTR pszValue)
{
    PAGED_CODE();

    NTSTATUS Status;
    ANSI_STRING AnsiStrValue;
    UNICODE_STRING UnicodeStrValue;
    UNICODE_STRING UnicodeStrValueName;

    // ZwSetValueKey wants the ValueName as a UNICODE_STRING
    RtlInitUnicodeString(&UnicodeStrValueName, pszwValueName);

    // REG_SZ is for WCHARs, there is no equivalent for CHARs
    // Use the ansi/unicode conversion functions to get from PSTR to PWSTR
    RtlInitAnsiString(&AnsiStrValue, pszValue);
    Status = RtlAnsiStringToUnicodeString(&UnicodeStrValue, &AnsiStrValue, TRUE);
    if (!NT_SUCCESS(Status))
    {
        BDD_LOG_ERROR1("RtlAnsiStringToUnicodeString failed with Status: 0x%I64x", Status);
        return Status;
    }

    // Write the value to the registry
    Status = ZwSetValueKey(DevInstRegKeyHandle,
                           &UnicodeStrValueName,
                           0,
                           REG_SZ,
                           UnicodeStrValue.Buffer,
                           UnicodeStrValue.MaximumLength);

    // Free the earlier allocated unicode string
    RtlFreeUnicodeString(&UnicodeStrValue);

    if (!NT_SUCCESS(Status))
    {
        BDD_LOG_ERROR1("ZwSetValueKey failed with Status: 0x%I64x", Status);
    }

    return Status;
}

NTSTATUS BASIC_DISPLAY_DRIVER::RegisterHWInfo()
{
    PAGED_CODE();

    NTSTATUS Status;

    // TODO: Replace these strings with proper information
    PCSTR StrHWInfoChipType = "Replace with the chip name";
    PCSTR StrHWInfoDacType = "Replace with the DAC name or identifier (ID)";
    PCSTR StrHWInfoAdapterString = "Replace with the name of the adapter";
    PCSTR StrHWInfoBiosString = "Replace with information about the BIOS";

    HANDLE DevInstRegKeyHandle;
    Status = IoOpenDeviceRegistryKey(m_pPhysicalDevice, PLUGPLAY_REGKEY_DRIVER, KEY_SET_VALUE, &DevInstRegKeyHandle);
    if (!NT_SUCCESS(Status))
    {
        BDD_LOG_ERROR2("IoOpenDeviceRegistryKey failed for PDO: 0x%I64x, Status: 0x%I64x", m_pPhysicalDevice, Status);
        return Status;
    }

    Status = WriteHWInfoStr(DevInstRegKeyHandle, L"HardwareInformation.ChipType", StrHWInfoChipType);
    if (!NT_SUCCESS(Status))
    {
        return Status;
    }

    Status = WriteHWInfoStr(DevInstRegKeyHandle, L"HardwareInformation.DacType", StrHWInfoDacType);
    if (!NT_SUCCESS(Status))
    {
        return Status;
    }

    Status = WriteHWInfoStr(DevInstRegKeyHandle, L"HardwareInformation.AdapterString", StrHWInfoAdapterString);
    if (!NT_SUCCESS(Status))
    {
        return Status;
    }

    Status = WriteHWInfoStr(DevInstRegKeyHandle, L"HardwareInformation.BiosString", StrHWInfoBiosString);
    if (!NT_SUCCESS(Status))
    {
        return Status;
    }

    // MemorySize is a ULONG, unlike the others which are all strings
    UNICODE_STRING ValueNameMemorySize;
    RtlInitUnicodeString(&ValueNameMemorySize, L"HardwareInformation.MemorySize");
    DWORD MemorySize = 0; // BDD has no access to video memory
    Status = ZwSetValueKey(DevInstRegKeyHandle,
                           &ValueNameMemorySize,
                           0,
                           REG_DWORD,
                           &MemorySize,
                           sizeof(MemorySize));
    if (!NT_SUCCESS(Status))
    {
        BDD_LOG_ERROR1("ZwSetValueKey for MemorySize failed with Status: 0x%I64x", Status);
        return Status;
    }

    return Status;
}

//
// Non-Paged Code
//
#pragma code_seg(push)
#pragma code_seg()
D3DDDI_VIDEO_PRESENT_SOURCE_ID BASIC_DISPLAY_DRIVER::FindSourceForTarget(D3DDDI_VIDEO_PRESENT_TARGET_ID TargetId, BOOLEAN DefaultToZero)
{
    UNREFERENCED_PARAMETER(TargetId);
    BDD_ASSERT_CHK(TargetId < MAX_CHILDREN);

    for (UINT SourceId = 0; SourceId < MAX_VIEWS; ++SourceId)
    {
        if (m_CurrentModes[SourceId].FrameBuffer.Ptr != NULL)
        {
            return SourceId;
        }
    }

    return DefaultToZero ? 0 : D3DDDI_ID_UNINITIALIZED;
}

VOID BASIC_DISPLAY_DRIVER::DpcRoutine(VOID)
{
    m_DxgkInterface.DxgkCbNotifyDpc((HANDLE)m_DxgkInterface.DeviceHandle);
}

BOOLEAN BASIC_DISPLAY_DRIVER::InterruptRoutine(_In_  ULONG MessageNumber)
{
    UNREFERENCED_PARAMETER(MessageNumber);

    // BDD cannot handle interrupts
    return FALSE;
}

VOID BASIC_DISPLAY_DRIVER::ResetDevice(VOID)
{
}

// Must be Non-Paged, as it sets up the display for a bugcheck
NTSTATUS BASIC_DISPLAY_DRIVER::SystemDisplayEnable(_In_  D3DDDI_VIDEO_PRESENT_TARGET_ID TargetId,
                                                   _In_  PDXGKARG_SYSTEM_DISPLAY_ENABLE_FLAGS Flags,
                                                   _Out_ UINT* pWidth,
                                                   _Out_ UINT* pHeight,
                                                   _Out_ D3DDDIFORMAT* pColorFormat)
{
    UNREFERENCED_PARAMETER(Flags);

    m_SystemDisplaySourceId = D3DDDI_ID_UNINITIALIZED;

    BDD_ASSERT((TargetId < MAX_CHILDREN) || (TargetId == D3DDDI_ID_UNINITIALIZED));

    // Find the frame buffer for displaying the bugcheck, if it was successfully mapped
    if (TargetId == D3DDDI_ID_UNINITIALIZED)
    {
        for (UINT SourceIdx = 0; SourceIdx < MAX_VIEWS; ++SourceIdx)
        {
            if (m_CurrentModes[SourceIdx].FrameBuffer.Ptr != NULL)
            {
                m_SystemDisplaySourceId = SourceIdx;
                break;
            }
        }
    }
    else
    {
        m_SystemDisplaySourceId = FindSourceForTarget(TargetId, FALSE);
    }

    if (m_SystemDisplaySourceId == D3DDDI_ID_UNINITIALIZED)
    {
        {
            return STATUS_UNSUCCESSFUL;
        }
    }

    if ((m_CurrentModes[m_SystemDisplaySourceId].Rotation == D3DKMDT_VPPR_ROTATE90) ||
        (m_CurrentModes[m_SystemDisplaySourceId].Rotation == D3DKMDT_VPPR_ROTATE270))
    {
        *pHeight = m_CurrentModes[m_SystemDisplaySourceId].DispInfo.Width;
        *pWidth = m_CurrentModes[m_SystemDisplaySourceId].DispInfo.Height;
    }
    else
    {
        *pWidth = m_CurrentModes[m_SystemDisplaySourceId].DispInfo.Width;
        *pHeight = m_CurrentModes[m_SystemDisplaySourceId].DispInfo.Height;
    }

    *pColorFormat = m_CurrentModes[m_SystemDisplaySourceId].DispInfo.ColorFormat;


    return STATUS_SUCCESS;
}

// Must be Non-Paged, as it is called to display the bugcheck screen
VOID BASIC_DISPLAY_DRIVER::SystemDisplayWrite(_In_reads_bytes_(SourceHeight * SourceStride) VOID* pSource,
                                              _In_ UINT SourceWidth,
                                              _In_ UINT SourceHeight,
                                              _In_ UINT SourceStride,
                                              _In_ INT PositionX,
                                              _In_ INT PositionY)
{

    // Rect will be Offset by PositionX/Y in the src to reset it back to 0
    RECT Rect;
    Rect.left = PositionX;
    Rect.top = PositionY;
    Rect.right =  Rect.left + SourceWidth;
    Rect.bottom = Rect.top + SourceHeight;

    // Set up destination blt info
    BLT_INFO DstBltInfo;
    DstBltInfo.pBits = m_CurrentModes[m_SystemDisplaySourceId].FrameBuffer.Ptr;
    DstBltInfo.Pitch = m_CurrentModes[m_SystemDisplaySourceId].DispInfo.Pitch;
    DstBltInfo.BitsPerPel = BPPFromPixelFormat(m_CurrentModes[m_SystemDisplaySourceId].DispInfo.ColorFormat);
    DstBltInfo.Offset.x = 0;
    DstBltInfo.Offset.y = 0;
    DstBltInfo.Rotation = m_CurrentModes[m_SystemDisplaySourceId].Rotation;
    DstBltInfo.Width = m_CurrentModes[m_SystemDisplaySourceId].DispInfo.Width;
    DstBltInfo.Height = m_CurrentModes[m_SystemDisplaySourceId].DispInfo.Height;

    // Set up source blt info
    BLT_INFO SrcBltInfo;
    SrcBltInfo.pBits = pSource;
    SrcBltInfo.Pitch = SourceStride;
    SrcBltInfo.BitsPerPel = 32;

    SrcBltInfo.Offset.x = -PositionX;
    SrcBltInfo.Offset.y = -PositionY;
    SrcBltInfo.Rotation = D3DKMDT_VPPR_IDENTITY;
    SrcBltInfo.Width = SourceWidth;
    SrcBltInfo.Height = SourceHeight;

    BltBits(&DstBltInfo,
            &SrcBltInfo,
            1, // NumRects
            &Rect);
}

#pragma code_seg(pop) // End Non-Paged Code

C++程式檔案 bdd_ddi.cxx

/******************************Module*Header*******************************\
* Module Name: BDD_DDI.cxx
*
* Basic Display Driver DDI entry points redirects
*
*
* Copyright (c) 2010 Microsoft Corporation
\**************************************************************************/

#include "BDD.hxx"


#pragma code_seg(push)
#pragma code_seg("INIT")
// BEGIN: Init Code

//
// Driver Entry point
//

extern "C"
NTSTATUS
DriverEntry(
    _In_  DRIVER_OBJECT*  pDriverObject,
    _In_  UNICODE_STRING* pRegistryPath)
{
    PAGED_CODE();


    // Initialize DDI function pointers and dxgkrnl
    KMDDOD_INITIALIZATION_DATA InitialData = {0};

    InitialData.Version = DXGKDDI_INTERFACE_VERSION;

    InitialData.DxgkDdiAddDevice                    = BddDdiAddDevice;
    InitialData.DxgkDdiStartDevice                  = BddDdiStartDevice;
    InitialData.DxgkDdiStopDevice                   = BddDdiStopDevice;
    InitialData.DxgkDdiResetDevice                  = BddDdiResetDevice;
    InitialData.DxgkDdiRemoveDevice                 = BddDdiRemoveDevice;
    InitialData.DxgkDdiDispatchIoRequest            = BddDdiDispatchIoRequest;
    InitialData.DxgkDdiInterruptRoutine             = BddDdiInterruptRoutine;
    InitialData.DxgkDdiDpcRoutine                   = BddDdiDpcRoutine;
    InitialData.DxgkDdiQueryChildRelations          = BddDdiQueryChildRelations;
    InitialData.DxgkDdiQueryChildStatus             = BddDdiQueryChildStatus;
    InitialData.DxgkDdiQueryDeviceDescriptor        = BddDdiQueryDeviceDescriptor;
    InitialData.DxgkDdiSetPowerState                = BddDdiSetPowerState;
    InitialData.DxgkDdiUnload                       = BddDdiUnload;
    InitialData.DxgkDdiQueryAdapterInfo             = BddDdiQueryAdapterInfo;
    InitialData.DxgkDdiSetPointerPosition           = BddDdiSetPointerPosition;
    InitialData.DxgkDdiSetPointerShape              = BddDdiSetPointerShape;
    InitialData.DxgkDdiIsSupportedVidPn             = BddDdiIsSupportedVidPn;
    InitialData.DxgkDdiRecommendFunctionalVidPn     = BddDdiRecommendFunctionalVidPn;
    InitialData.DxgkDdiEnumVidPnCofuncModality      = BddDdiEnumVidPnCofuncModality;
    InitialData.DxgkDdiSetVidPnSourceVisibility     = BddDdiSetVidPnSourceVisibility;
    InitialData.DxgkDdiCommitVidPn                  = BddDdiCommitVidPn;
    InitialData.DxgkDdiUpdateActiveVidPnPresentPath = BddDdiUpdateActiveVidPnPresentPath;
    InitialData.DxgkDdiRecommendMonitorModes        = BddDdiRecommendMonitorModes;
    InitialData.DxgkDdiQueryVidPnHWCapability       = BddDdiQueryVidPnHWCapability;
    InitialData.DxgkDdiPresentDisplayOnly           = BddDdiPresentDisplayOnly;
    InitialData.DxgkDdiStopDeviceAndReleasePostDisplayOwnership = BddDdiStopDeviceAndReleasePostDisplayOwnership;
    InitialData.DxgkDdiSystemDisplayEnable          = BddDdiSystemDisplayEnable;
    InitialData.DxgkDdiSystemDisplayWrite           = BddDdiSystemDisplayWrite;

    NTSTATUS Status = DxgkInitializeDisplayOnlyDriver(pDriverObject, pRegistryPath, &InitialData);
    if (!NT_SUCCESS(Status))
    {
        BDD_LOG_ERROR1("DxgkInitializeDisplayOnlyDriver failed with Status: 0x%I64x", Status);
        return Status;
    }


    return Status;
}
// END: Init Code
#pragma code_seg(pop)

#pragma code_seg(push)
#pragma code_seg("PAGE")

//
// PnP DDIs
//

VOID
BddDdiUnload(VOID)
{
    PAGED_CODE();
}

NTSTATUS
BddDdiAddDevice(
    _In_ DEVICE_OBJECT* pPhysicalDeviceObject,
    _Outptr_ PVOID*  ppDeviceContext)
{
    PAGED_CODE();

    if ((pPhysicalDeviceObject == NULL) ||
        (ppDeviceContext == NULL))
    {
        BDD_LOG_ERROR2("One of pPhysicalDeviceObject (0x%I64x), ppDeviceContext (0x%I64x) is NULL",
                        pPhysicalDeviceObject, ppDeviceContext);
        return STATUS_INVALID_PARAMETER;
    }
    *ppDeviceContext = NULL;

    BASIC_DISPLAY_DRIVER* pBDD = new(NonPagedPoolNx) BASIC_DISPLAY_DRIVER(pPhysicalDeviceObject);
    if (pBDD == NULL)
    {
        BDD_LOG_LOW_RESOURCE0("pBDD failed to be allocated");
        return STATUS_NO_MEMORY;
    }

    *ppDeviceContext = pBDD;

    return STATUS_SUCCESS;
}

NTSTATUS
BddDdiRemoveDevice(
    _In_  VOID* pDeviceContext)
{
    PAGED_CODE();

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(pDeviceContext);

    if (pBDD)
    {
        delete pBDD;
        pBDD = NULL;
    }

    return STATUS_SUCCESS;
}

NTSTATUS
BddDdiStartDevice(
    _In_  VOID*              pDeviceContext,
    _In_  DXGK_START_INFO*   pDxgkStartInfo,
    _In_  DXGKRNL_INTERFACE* pDxgkInterface,
    _Out_ ULONG*             pNumberOfViews,
    _Out_ ULONG*             pNumberOfChildren)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(pDeviceContext != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(pDeviceContext);
    return pBDD->StartDevice(pDxgkStartInfo, pDxgkInterface, pNumberOfViews, pNumberOfChildren);
}

NTSTATUS
BddDdiStopDevice(
    _In_  VOID* pDeviceContext)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(pDeviceContext != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(pDeviceContext);
    return pBDD->StopDevice();
}


NTSTATUS
BddDdiDispatchIoRequest(
    _In_  VOID*                 pDeviceContext,
    _In_  ULONG                 VidPnSourceId,
    _In_  VIDEO_REQUEST_PACKET* pVideoRequestPacket)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(pDeviceContext != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(pDeviceContext);
    if (!pBDD->IsDriverActive())
    {
        BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD);
        return STATUS_UNSUCCESSFUL;
    }
    return pBDD->DispatchIoRequest(VidPnSourceId, pVideoRequestPacket);
}

NTSTATUS
BddDdiSetPowerState(
    _In_  VOID*              pDeviceContext,
    _In_  ULONG              HardwareUid,
    _In_  DEVICE_POWER_STATE DevicePowerState,
    _In_  POWER_ACTION       ActionType)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(pDeviceContext != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(pDeviceContext);
    if (!pBDD->IsDriverActive())
    {
        // If the driver isn't active, SetPowerState can still be called, however in BDD's case
        // this shouldn't do anything, as it could for instance be called on BDD Fallback after
        // Fallback has been stopped and BDD PnP is being started. Fallback doesn't have control
        // of the hardware in this case.
        return STATUS_SUCCESS;
    }
    return pBDD->SetPowerState(HardwareUid, DevicePowerState, ActionType);
}

NTSTATUS
BddDdiQueryChildRelations(
    _In_                             VOID*                  pDeviceContext,
    _Out_writes_bytes_(ChildRelationsSize) DXGK_CHILD_DESCRIPTOR* pChildRelations,
    _In_                             ULONG                  ChildRelationsSize)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(pDeviceContext != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(pDeviceContext);
    return pBDD->QueryChildRelations(pChildRelations, ChildRelationsSize);
}

NTSTATUS
BddDdiQueryChildStatus(
    _In_    VOID*              pDeviceContext,
    _Inout_ DXGK_CHILD_STATUS* pChildStatus,
    _In_    BOOLEAN            NonDestructiveOnly)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(pDeviceContext != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(pDeviceContext);
    return pBDD->QueryChildStatus(pChildStatus, NonDestructiveOnly);
}

NTSTATUS
BddDdiQueryDeviceDescriptor(
    _In_  VOID*                     pDeviceContext,
    _In_  ULONG                     ChildUid,
    _Inout_ DXGK_DEVICE_DESCRIPTOR* pDeviceDescriptor)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(pDeviceContext != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(pDeviceContext);
    if (!pBDD->IsDriverActive())
    {
        // During stress testing of PnPStop, it is possible for BDD Fallback to get called to start then stop in quick succession.
        // The first call queues a worker thread item indicating that it now has a child device, the second queues a worker thread
        // item that it no longer has any child device. This function gets called based on the first worker thread item, but after
        // the driver has been stopped. Therefore instead of asserting like other functions, we only warn.
        BDD_LOG_WARNING1("BDD (0x%I64x) is being called when not active!", pBDD);
        return STATUS_UNSUCCESSFUL;
    }
    return pBDD->QueryDeviceDescriptor(ChildUid, pDeviceDescriptor);
}


//
// WDDM Display Only Driver DDIs
//

NTSTATUS
APIENTRY
BddDdiQueryAdapterInfo(
    _In_ CONST HANDLE                    hAdapter,
    _In_ CONST DXGKARG_QUERYADAPTERINFO* pQueryAdapterInfo)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(hAdapter != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(hAdapter);
    return pBDD->QueryAdapterInfo(pQueryAdapterInfo);
}

NTSTATUS
APIENTRY
BddDdiSetPointerPosition(
    _In_ CONST HANDLE                      hAdapter,
    _In_ CONST DXGKARG_SETPOINTERPOSITION* pSetPointerPosition)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(hAdapter != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(hAdapter);
    if (!pBDD->IsDriverActive())
    {
        BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD);
        return STATUS_UNSUCCESSFUL;
    }
    return pBDD->SetPointerPosition(pSetPointerPosition);
}

NTSTATUS
APIENTRY
BddDdiSetPointerShape(
    _In_ CONST HANDLE                   hAdapter,
    _In_ CONST DXGKARG_SETPOINTERSHAPE* pSetPointerShape)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(hAdapter != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(hAdapter);
    if (!pBDD->IsDriverActive())
    {
        BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD);
        return STATUS_UNSUCCESSFUL;
    }
    return pBDD->SetPointerShape(pSetPointerShape);
}


NTSTATUS
APIENTRY
BddDdiPresentDisplayOnly(
    _In_ CONST HANDLE                       hAdapter,
    _In_ CONST DXGKARG_PRESENT_DISPLAYONLY* pPresentDisplayOnly)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(hAdapter != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(hAdapter);
    if (!pBDD->IsDriverActive())
    {
        BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD);
        return STATUS_UNSUCCESSFUL;
    }
    return pBDD->PresentDisplayOnly(pPresentDisplayOnly);
}

NTSTATUS
APIENTRY
BddDdiStopDeviceAndReleasePostDisplayOwnership(
    _In_  VOID*                          pDeviceContext,
    _In_  D3DDDI_VIDEO_PRESENT_TARGET_ID TargetId,
    _Out_ DXGK_DISPLAY_INFORMATION*      DisplayInfo)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(pDeviceContext != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(pDeviceContext);
    return pBDD->StopDeviceAndReleasePostDisplayOwnership(TargetId, DisplayInfo);
}

NTSTATUS
APIENTRY
BddDdiIsSupportedVidPn(
    _In_ CONST HANDLE                 hAdapter,
    _Inout_ DXGKARG_ISSUPPORTEDVIDPN* pIsSupportedVidPn)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(hAdapter != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(hAdapter);
    if (!pBDD->IsDriverActive())
    {
        // This path might hit because win32k/dxgport doesn't check that an adapter is active when taking the adapter lock.
        // The adapter lock is the main thing BDD Fallback relies on to not be called while it's inactive. It is still a rare
        // timing issue around PnpStart/Stop and isn't expected to have any effect on the stability of the system.
        BDD_LOG_WARNING1("BDD (0x%I64x) is being called when not active!", pBDD);
        return STATUS_UNSUCCESSFUL;
    }
    return pBDD->IsSupportedVidPn(pIsSupportedVidPn);
}

NTSTATUS
APIENTRY
BddDdiRecommendFunctionalVidPn(
    _In_ CONST HANDLE                                  hAdapter,
    _In_ CONST DXGKARG_RECOMMENDFUNCTIONALVIDPN* CONST pRecommendFunctionalVidPn)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(hAdapter != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(hAdapter);
    if (!pBDD->IsDriverActive())
    {
        BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD);
        return STATUS_UNSUCCESSFUL;
    }
    return pBDD->RecommendFunctionalVidPn(pRecommendFunctionalVidPn);
}

NTSTATUS
APIENTRY
BddDdiRecommendVidPnTopology(
    _In_ CONST HANDLE                                 hAdapter,
    _In_ CONST DXGKARG_RECOMMENDVIDPNTOPOLOGY* CONST  pRecommendVidPnTopology)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(hAdapter != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(hAdapter);
    if (!pBDD->IsDriverActive())
    {
        BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD);
        return STATUS_UNSUCCESSFUL;
    }
    return pBDD->RecommendVidPnTopology(pRecommendVidPnTopology);
}

NTSTATUS
APIENTRY
BddDdiRecommendMonitorModes(
    _In_ CONST HANDLE                                hAdapter,
    _In_ CONST DXGKARG_RECOMMENDMONITORMODES* CONST  pRecommendMonitorModes)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(hAdapter != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(hAdapter);
    if (!pBDD->IsDriverActive())
    {
        BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD);
        return STATUS_UNSUCCESSFUL;
    }
    return pBDD->RecommendMonitorModes(pRecommendMonitorModes);
}

NTSTATUS
APIENTRY
BddDdiEnumVidPnCofuncModality(
    _In_ CONST HANDLE                                 hAdapter,
    _In_ CONST DXGKARG_ENUMVIDPNCOFUNCMODALITY* CONST pEnumCofuncModality)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(hAdapter != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(hAdapter);
    if (!pBDD->IsDriverActive())
    {
        BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD);
        return STATUS_UNSUCCESSFUL;
    }
    return pBDD->EnumVidPnCofuncModality(pEnumCofuncModality);
}

NTSTATUS
APIENTRY
BddDdiSetVidPnSourceVisibility(
    _In_ CONST HANDLE                            hAdapter,
    _In_ CONST DXGKARG_SETVIDPNSOURCEVISIBILITY* pSetVidPnSourceVisibility)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(hAdapter != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(hAdapter);
    if (!pBDD->IsDriverActive())
    {
        BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD);
        return STATUS_UNSUCCESSFUL;
    }
    return pBDD->SetVidPnSourceVisibility(pSetVidPnSourceVisibility);
}

NTSTATUS
APIENTRY
BddDdiCommitVidPn(
    _In_ CONST HANDLE                     hAdapter,
    _In_ CONST DXGKARG_COMMITVIDPN* CONST pCommitVidPn)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(hAdapter != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(hAdapter);
    if (!pBDD->IsDriverActive())
    {
        BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD);
        return STATUS_UNSUCCESSFUL;
    }
    return pBDD->CommitVidPn(pCommitVidPn);
}

NTSTATUS
APIENTRY
BddDdiUpdateActiveVidPnPresentPath(
    _In_ CONST HANDLE                                      hAdapter,
    _In_ CONST DXGKARG_UPDATEACTIVEVIDPNPRESENTPATH* CONST pUpdateActiveVidPnPresentPath)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(hAdapter != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(hAdapter);
    if (!pBDD->IsDriverActive())
    {
        BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD);
        return STATUS_UNSUCCESSFUL;
    }
    return pBDD->UpdateActiveVidPnPresentPath(pUpdateActiveVidPnPresentPath);
}

NTSTATUS
APIENTRY
BddDdiQueryVidPnHWCapability(
    _In_ CONST HANDLE                       hAdapter,
    _Inout_ DXGKARG_QUERYVIDPNHWCAPABILITY* pVidPnHWCaps)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(hAdapter != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(hAdapter);
    if (!pBDD->IsDriverActive())
    {
        BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD);
        return STATUS_UNSUCCESSFUL;
    }
    return pBDD->QueryVidPnHWCapability(pVidPnHWCaps);
}
//END: Paged Code
#pragma code_seg(pop)

#pragma code_seg(push)
#pragma code_seg()
// BEGIN: Non-Paged Code

VOID
BddDdiDpcRoutine(
    _In_  VOID* pDeviceContext)
{
    BDD_ASSERT_CHK(pDeviceContext != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(pDeviceContext);
    if (!pBDD->IsDriverActive())
    {
        BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD);
        return;
    }
    pBDD->DpcRoutine();
}

BOOLEAN
BddDdiInterruptRoutine(
    _In_  VOID* pDeviceContext,
    _In_  ULONG MessageNumber)
{
    BDD_ASSERT_CHK(pDeviceContext != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(pDeviceContext);
    return pBDD->InterruptRoutine(MessageNumber);
}

VOID
BddDdiResetDevice(
    _In_  VOID* pDeviceContext)
{
    BDD_ASSERT_CHK(pDeviceContext != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(pDeviceContext);
    pBDD->ResetDevice();
}

NTSTATUS
APIENTRY
BddDdiSystemDisplayEnable(
    _In_  VOID* pDeviceContext,
    _In_  D3DDDI_VIDEO_PRESENT_TARGET_ID TargetId,
    _In_  PDXGKARG_SYSTEM_DISPLAY_ENABLE_FLAGS Flags,
    _Out_ UINT* Width,
    _Out_ UINT* Height,
    _Out_ D3DDDIFORMAT* ColorFormat)
{
    BDD_ASSERT_CHK(pDeviceContext != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(pDeviceContext);
    return pBDD->SystemDisplayEnable(TargetId, Flags, Width, Height, ColorFormat);
}

VOID
APIENTRY
BddDdiSystemDisplayWrite(
    _In_  VOID* pDeviceContext,
    _In_  VOID* Source,
    _In_  UINT  SourceWidth,
    _In_  UINT  SourceHeight,
    _In_  UINT  SourceStride,
    _In_  UINT  PositionX,
    _In_  UINT  PositionY)
{
    BDD_ASSERT_CHK(pDeviceContext != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(pDeviceContext);
    pBDD->SystemDisplayWrite(Source, SourceWidth, SourceHeight, SourceStride, PositionX, PositionY);
}

// END: Non-Paged Code
#pragma code_seg(pop)

C++程式檔案 bdd_dmm.cxx

/******************************Module*Header*******************************\
* Module Name: bdd_dmm.hxx
*
* Basic Display Driver display-mode management (DMM) function implementations
*
*
* Copyright (c) 2010 Microsoft Corporation
\**************************************************************************/

#include "BDD.hxx"

#pragma code_seg("PAGE")

// Display-Only Devices can only return display modes of D3DDDIFMT_A8R8G8B8.
// Color conversion takes place if the app's fullscreen backbuffer has different format.
// Full display drivers can add more if the hardware supports them.
D3DDDIFORMAT gBddPixelFormats[] = {
    D3DDDIFMT_A8R8G8B8
};

// TODO: Need to also check pinned modes and the path parameters, not just topology
NTSTATUS BASIC_DISPLAY_DRIVER::IsSupportedVidPn(_Inout_ DXGKARG_ISSUPPORTEDVIDPN* pIsSupportedVidPn)
{
    PAGED_CODE();

    BDD_ASSERT(pIsSupportedVidPn != NULL);

    if (pIsSupportedVidPn->hDesiredVidPn == 0)
    {
        // A null desired VidPn is supported
        pIsSupportedVidPn->IsVidPnSupported = TRUE;
        return STATUS_SUCCESS;
    }

    // Default to not supported, until shown it is supported
    pIsSupportedVidPn->IsVidPnSupported = FALSE;

    CONST DXGK_VIDPN_INTERFACE* pVidPnInterface;
    NTSTATUS Status = m_DxgkInterface.DxgkCbQueryVidPnInterface(pIsSupportedVidPn->hDesiredVidPn, DXGK_VIDPN_INTERFACE_VERSION_V1, &pVidPnInterface);
    if (!NT_SUCCESS(Status))
    {
        BDD_LOG_ERROR2("DxgkCbQueryVidPnInterface failed with Status = 0x%I64x, hDesiredVidPn = 0x%I64x", Status, pIsSupportedVidPn->hDesiredVidPn);
        return Status;
    }

    D3DKMDT_HVIDPNTOPOLOGY hVidPnTopology;
    CONST DXGK_VIDPNTOPOLOGY_INTERFACE* pVidPnTopologyInterface;
    Status = pVidPnInterface->pfnGetTopology(pIsSupportedVidPn->hDesiredVidPn, &hVidPnTopology, &pVidPnTopologyInterface);
    if (!NT_SUCCESS(Status))
    {
        BDD_LOG_ERROR2("pfnGetTopology failed with Status = 0x%I64x, hDesiredVidPn = 0x%I64x", Status, pIsSupportedVidPn->hDesiredVidPn);
        return Status;
    }

    // For every source in this topology, make sure they don't have more paths than there are targets
    for (D3DDDI_VIDEO_PRESENT_SOURCE_ID SourceId = 0; SourceId < MAX_VIEWS; ++SourceId)
    {
        SIZE_T NumPathsFromSource = 0;
        Status = pVidPnTopologyInterface->pfnGetNumPathsFromSource(hVidPnTopology, SourceId, &NumPathsFromSource);
        if (Status == STATUS_GRAPHICS_SOURCE_NOT_IN_TOPOLOGY)
        {
            continue;
        }
        else if (!NT_SUCCESS(Status))
        {
            BDD_LOG_ERROR3("pfnGetNumPathsFromSource failed with Status = 0x%I64x. hVidPnTopology = 0x%I64x, SourceId = 0x%I64x",
                           Status, hVidPnTopology, SourceId);
            return Status;
        }
        else if (NumPathsFromSource > MAX_CHILDREN)
        {
            // This VidPn is not supported, which has already been set as the default
            return STATUS_SUCCESS;
        }
    }

    // All sources succeeded so this VidPn is supported
    pIsSupportedVidPn->IsVidPnSupported = TRUE;
    return STATUS_SUCCESS;
}

NTSTATUS BASIC_DISPLAY_DRIVER::RecommendFunctionalVidPn(_In_ CONST DXGKARG_RECOMMENDFUNCTIONALVIDPN* CONST pRecommendFunctionalVidPn)
{
    PAGED_CODE();

    BDD_ASSERT(pRecommendFunctionalVidPn == NULL);

    return STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN;
}

NTSTATUS BASIC_DISPLAY_DRIVER::RecommendVidPnTopology(_In_ CONST DXGKARG_RECOMMENDVIDPNTOPOLOGY* CONST pRecommendVidPnTopology)
{
    PAGED_CODE();

    BDD_ASSERT(pRecommendVidPnTopology == NULL);

    return STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN;
}

NTSTATUS BASIC_DISPLAY_DRIVER::RecommendMonitorModes(_In_ CONST DXGKARG_RECOMMENDMONITORMODES* CONST pRecommendMonitorModes)
{
    PAGED_CODE();

    // This is always called to recommend modes for the monitor. The sample driver doesn't provide EDID for a monitor, so 
    // the OS prefills the list with default monitor modes. Since the required mode might not be in the list, it should 
    // be provided as a recommended mode.
    return AddSingleMonitorMode(pRecommendMonitorModes);
}

// Tell DMM about all the modes, etc. that are supported
NTSTATUS BASIC_DISPLAY_DRIVER::EnumVidPnCofuncModality(_In_ CONST DXGKARG_ENUMVIDPNCOFUNCMODALITY* CONST pEnumCofuncModality)
{
    PAGED_CODE();

    BDD_ASSERT(pEnumCofuncModality != NULL);

    D3DKMDT_HVIDPNTOPOLOGY                   hVidPnTopology = 0;
    D3DKMDT_HVIDPNSOURCEMODESET              hVidPnSourceModeSet = 0;
    D3DKMDT_HVIDPNTARGETMODESET              hVidPnTargetModeSet = 0;
    CONST DXGK_VIDPN_INTERFACE*              pVidPnInterface = NULL;
    CONST DXGK_VIDPNTOPOLOGY_INTERFACE*      pVidPnTopologyInterface = NULL;
    CONST DXGK_VIDPNSOURCEMODESET_INTERFACE* pVidPnSourceModeSetInterface = NULL;
    CONST DXGK_VIDPNTARGETMODESET_INTERFACE* pVidPnTargetModeSetInterface = NULL;
    CONST D3DKMDT_VIDPN_PRESENT_PATH*        pVidPnPresentPath = NULL;
    CONST D3DKMDT_VIDPN_PRESENT_PATH*        pVidPnPresentPathTemp = NULL; // Used for AcquireNextPathInfo
    CONST D3DKMDT_VIDPN_SOURCE_MODE*         pVidPnPinnedSourceModeInfo = NULL;
    CONST D3DKMDT_VIDPN_TARGET_MODE*         pVidPnPinnedTargetModeInfo = NULL;

    // Get the VidPn Interface so we can get the 'Source Mode Set', 'Target Mode Set' and 'VidPn Topology' interfaces
    NTSTATUS Status = m_DxgkInterface.DxgkCbQueryVidPnInterface(pEnumCofuncModality->hConstrainingVidPn, DXGK_VIDPN_INTERFACE_VERSION_V1, &pVidPnInterface);
    if (!NT_SUCCESS(Status))
    {
        BDD_LOG_ERROR2("DxgkCbQueryVidPnInterface failed with Status = 0x%I64x, hFunctionalVidPn = 0x%I64x", Status, pEnumCofuncModality->hConstrainingVidPn);
        return Status;
    }

    // Get the VidPn Topology interface so we can enumerate all paths
    Status = pVidPnInterface->pfnGetTopology(pEnumCofuncModality->hConstrainingVidPn, &hVidPnTopology, &pVidPnTopologyInterface);
    if (!NT_SUCCESS(Status))
    {
        BDD_LOG_ERROR2("pfnGetTopology failed with Status = 0x%I64x, hFunctionalVidPn = 0x%I64x", Status, pEnumCofuncModality->hConstrainingVidPn);
        return Status;
    }

    // Get the first path before we start looping through them
    Status = pVidPnTopologyInterface->pfnAcquireFirstPathInfo(hVidPnTopology, &pVidPnPresentPath);
    if (!NT_SUCCESS(Status))
    {
        BDD_LOG_ERROR2("pfnAcquireFirstPathInfo failed with Status = 0x%I64x, hVidPnTopology = 0x%I64x", Status, hVidPnTopology);
        return Status;
    }

    // Loop through all available paths.
    while (Status != STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET)
    {
        // Get the Source Mode Set interface so the pinned mode can be retrieved
        Status = pVidPnInterface->pfnAcquireSourceModeSet(pEnumCofuncModality->hConstrainingVidPn,
                                                          pVidPnPresentPath->VidPnSourceId,
                                                          &hVidPnSourceModeSet,
                                                          &pVidPnSourceModeSetInterface);
        if (!NT_SUCCESS(Status))
        {
            BDD_LOG_ERROR3("pfnAcquireSourceModeSet failed with Status = 0x%I64x, hConstrainingVidPn = 0x%I64x, SourceId = 0x%I64x",
                           Status, pEnumCofuncModality->hConstrainingVidPn, pVidPnPresentPath->VidPnSourceId);
            break;
        }

        // Get the pinned mode, needed when VidPnSource isn't pivot, and when VidPnTarget isn't pivot
        Status = pVidPnSourceModeSetInterface->pfnAcquirePinnedModeInfo(hVidPnSourceModeSet, &pVidPnPinnedSourceModeInfo);
        if (!NT_SUCCESS(Status))
        {
            BDD_LOG_ERROR2("pfnAcquirePinnedModeInfo failed with Status = 0x%I64x, hVidPnSourceModeSet = 0x%I64x", Status, hVidPnSourceModeSet);
            break;
        }

        // SOURCE MODES: If this source mode isn't the pivot point, do work on the source mode set
        if (!((pEnumCofuncModality->EnumPivotType == D3DKMDT_EPT_VIDPNSOURCE) &&
              (pEnumCofuncModality->EnumPivot.VidPnSourceId == pVidPnPresentPath->VidPnSourceId)))
        {
            // If there's no pinned source add possible modes (otherwise they've already been added)
            if (pVidPnPinnedSourceModeInfo == NULL)
            {
                // Release the acquired source mode set, since going to create a new one to put all modes in
                Status = pVidPnInterface->pfnReleaseSourceModeSet(pEnumCofuncModality->hConstrainingVidPn, hVidPnSourceModeSet);
                if (!NT_SUCCESS(Status))
                {
                    BDD_LOG_ERROR3("pfnReleaseSourceModeSet failed with Status = 0x%I64x, hConstrainingVidPn = 0x%I64x, hVidPnSourceModeSet = 0x%I64x",
                                   Status, pEnumCofuncModality->hConstrainingVidPn, hVidPnSourceModeSet);
                    break;
                }
                hVidPnSourceModeSet = 0; // Successfully released it

                // Create a new source mode set which will be added to the constraining VidPn with all the possible modes
                Status = pVidPnInterface->pfnCreateNewSourceModeSet(pEnumCofuncModality->hConstrainingVidPn,
                                                                    pVidPnPresentPath->VidPnSourceId,
                                                                    &hVidPnSourceModeSet,
                                                                    &pVidPnSourceModeSetInterface);
                if (!NT_SUCCESS(Status))
                {
                    BDD_LOG_ERROR3("pfnCreateNewSourceModeSet failed with Status = 0x%I64x, hConstrainingVidPn = 0x%I64x, SourceId = 0x%I64x",
                                   Status, pEnumCofuncModality->hConstrainingVidPn, pVidPnPresentPath->VidPnSourceId);
                    break;
                }

                // Add the appropriate modes to the source mode set
                {
                    Status = AddSingleSourceMode(pVidPnSourceModeSetInterface, hVidPnSourceModeSet, pVidPnPresentPath->VidPnSourceId);
                }

                if (!NT_SUCCESS(Status))
                {
                    break;
                }

                // Give DMM back the source modes just populated
                Status = pVidPnInterface->pfnAssignSourceModeSet(pEnumCofuncModality->hConstrainingVidPn, pVidPnPresentPath->VidPnSourceId, hVidPnSourceModeSet);
                if (!NT_SUCCESS(Status))
                {
                    BDD_LOG_ERROR4("pfnAssignSourceModeSet failed with Status = 0x%I64x, hConstrainingVidPn = 0x%I64x, SourceId = 0x%I64x, hVidPnSourceModeSet = 0x%I64x",
                                   Status, pEnumCofuncModality->hConstrainingVidPn, pVidPnPresentPath->VidPnSourceId, hVidPnSourceModeSet);
                    break;
                }
                hVidPnSourceModeSet = 0; // Successfully assigned it (equivalent to releasing it)
            }
        }// End: SOURCE MODES

        // TARGET MODES: If this target mode isn't the pivot point, do work on the target mode set
        if (!((pEnumCofuncModality->EnumPivotType == D3DKMDT_EPT_VIDPNTARGET) &&
              (pEnumCofuncModality->EnumPivot.VidPnTargetId == pVidPnPresentPath->VidPnTargetId)))
        {
            // Get the Target Mode Set interface so modes can be added if necessary
            Status = pVidPnInterface->pfnAcquireTargetModeSet(pEnumCofuncModality->hConstrainingVidPn,
                                                              pVidPnPresentPath->VidPnTargetId,
                                                              &hVidPnTargetModeSet,
                                                              &pVidPnTargetModeSetInterface);
            if (!NT_SUCCESS(Status))
            {
                BDD_LOG_ERROR3("pfnAcquireTargetModeSet failed with Status = 0x%I64x, hConstrainingVidPn = 0x%I64x, TargetId = 0x%I64x",
                               Status, pEnumCofuncModality->hConstrainingVidPn, pVidPnPresentPath->VidPnTargetId);
                break;
            }

            Status = pVidPnTargetModeSetInterface->pfnAcquirePinnedModeInfo(hVidPnTargetModeSet, &pVidPnPinnedTargetModeInfo);
            if (!NT_SUCCESS(Status))
            {
                BDD_LOG_ERROR2("pfnAcquirePinnedModeInfo failed with Status = 0x%I64x, hVidPnTargetModeSet = 0x%I64x", Status, hVidPnTargetModeSet);
                break;
            }

            // If there's no pinned target add possible modes (otherwise they've already been added)
            if (pVidPnPinnedTargetModeInfo == NULL)
            {
                // Release the acquired target mode set, since going to create a new one to put all modes in
                Status = pVidPnInterface->pfnReleaseTargetModeSet(pEnumCofuncModality->hConstrainingVidPn, hVidPnTargetModeSet);
                if (!NT_SUCCESS(Status))
                {
                    BDD_LOG_ASSERTION3("pfnReleaseTargetModeSet failed with Status = 0x%I64x, hConstrainingVidPn = 0x%I64x, hVidPnTargetModeSet = 0x%I64x",
                                       Status, pEnumCofuncModality->hConstrainingVidPn, hVidPnTargetModeSet);
                    break;
                }
                hVidPnTargetModeSet = 0; // Successfully released it

                // Create a new target mode set which will be added to the constraining VidPn with all the possible modes
                Status = pVidPnInterface->pfnCreateNewTargetModeSet(pEnumCofuncModality->hConstrainingVidPn,
                                                                    pVidPnPresentPath->VidPnTargetId,
                                                                    &hVidPnTargetModeSet,
                                                                    &pVidPnTargetModeSetInterface);
                if (!NT_SUCCESS(Status))
                {
                    BDD_LOG_ERROR3("pfnCreateNewTargetModeSet failed with Status = 0x%I64x, hConstrainingVidPn = 0x%I64x, TargetId = 0x%I64x",
                                   Status, pEnumCofuncModality->hConstrainingVidPn, pVidPnPresentPath->VidPnTargetId);
                    break;
                }

                Status = AddSingleTargetMode(pVidPnTargetModeSetInterface, hVidPnTargetModeSet, pVidPnPinnedSourceModeInfo, pVidPnPresentPath->VidPnSourceId);

                if (!NT_SUCCESS(Status))
                {
                    break;
                }

                // Give DMM back the source modes just populated
                Status = pVidPnInterface->pfnAssignTargetModeSet(pEnumCofuncModality->hConstrainingVidPn, pVidPnPresentPath->VidPnTargetId, hVidPnTargetModeSet);
                if (!NT_SUCCESS(Status))
                {
                    BDD_LOG_ERROR4("pfnAssignTargetModeSet failed with Status = 0x%I64x, hConstrainingVidPn = 0x%I64x, TargetId = 0x%I64x, hVidPnTargetModeSet = 0x%I64x",
                                   Status, pEnumCofuncModality->hConstrainingVidPn, pVidPnPresentPath->VidPnTargetId, hVidPnTargetModeSet);
                    break;
                }
                hVidPnTargetModeSet = 0; // Successfully assigned it (equivalent to releasing it)
            }
            else
            {
                // Release the pinned target as there's no other work to do
                Status = pVidPnTargetModeSetInterface->pfnReleaseModeInfo(hVidPnTargetModeSet, pVidPnPinnedTargetModeInfo);
                if (!NT_SUCCESS(Status))
                {
                    BDD_LOG_ASSERTION3("pfnReleaseModeInfo failed with Status = 0x%I64x, hVidPnTargetModeSet = 0x%I64x, pVidPnPinnedTargetModeInfo = 0x%I64x",
                                        Status, hVidPnTargetModeSet, pVidPnPinnedTargetModeInfo);
                    break;
                }
                pVidPnPinnedTargetModeInfo = NULL; // Successfully released it

                // Release the acquired target mode set, since it is no longer needed
                Status = pVidPnInterface->pfnReleaseTargetModeSet(pEnumCofuncModality->hConstrainingVidPn, hVidPnTargetModeSet);
                if (!NT_SUCCESS(Status))
                {
                    BDD_LOG_ASSERTION3("pfnReleaseTargetModeSet failed with Status = 0x%I64x, hConstrainingVidPn = 0x%I64x, hVidPnTargetModeSet = 0x%I64x",
                                       Status, pEnumCofuncModality->hConstrainingVidPn, hVidPnTargetModeSet);
                    break;
                }
                hVidPnTargetModeSet = 0; // Successfully released it
            }
        }// End: TARGET MODES

        // Nothing else needs the pinned source mode so release it
        if (pVidPnPinnedSourceModeInfo != NULL)
        {
            Status = pVidPnSourceModeSetInterface->pfnReleaseModeInfo(hVidPnSourceModeSet, pVidPnPinnedSourceModeInfo);
            if (!NT_SUCCESS(Status))
            {
                BDD_LOG_ASSERTION3("pfnReleaseModeInfo failed with Status = 0x%I64x, hVidPnSourceModeSet = 0x%I64x, pVidPnPinnedSourceModeInfo = 0x%I64x",
                                    Status, hVidPnSourceModeSet, pVidPnPinnedSourceModeInfo);
                break;
            }
            pVidPnPinnedSourceModeInfo = NULL; // Successfully released it
        }

        // With the pinned source mode now released, if the source mode set hasn't been released, release that as well
        if (hVidPnSourceModeSet != 0)
        {
            Status = pVidPnInterface->pfnReleaseSourceModeSet(pEnumCofuncModality->hConstrainingVidPn, hVidPnSourceModeSet);
            if (!NT_SUCCESS(Status))
            {
                BDD_LOG_ERROR3("pfnReleaseSourceModeSet failed with Status = 0x%I64x, hConstrainingVidPn = 0x%I64x, hVidPnSourceModeSet = 0x%I64x",
                               Status, pEnumCofuncModality->hConstrainingVidPn, hVidPnSourceModeSet);
                break;
            }
            hVidPnSourceModeSet = 0; // Successfully released it
        }

        // If modifying support fields, need to modify a local version of a path structure since the retrieved one is const
        D3DKMDT_VIDPN_PRESENT_PATH LocalVidPnPresentPath = *pVidPnPresentPath;
        BOOLEAN SupportFieldsModified = FALSE;

        // SCALING: If this path's scaling isn't the pivot point, do work on the scaling support
        if (!((pEnumCofuncModality->EnumPivotType == D3DKMDT_EPT_SCALING) &&
              (pEnumCofuncModality->EnumPivot.VidPnSourceId == pVidPnPresentPath->VidPnSourceId) &&
              (pEnumCofuncModality->EnumPivot.VidPnTargetId == pVidPnPresentPath->VidPnTargetId)))
        {
            // If the scaling is unpinned, then modify the scaling support field
            if (pVidPnPresentPath->ContentTransformation.Scaling == D3DKMDT_VPPS_UNPINNED)
            {
                // Identity and centered scaling are supported, but not any stretch modes
                RtlZeroMemory(&(LocalVidPnPresentPath.ContentTransformation.ScalingSupport), sizeof(D3DKMDT_VIDPN_PRESENT_PATH_SCALING_SUPPORT));
                LocalVidPnPresentPath.ContentTransformation.ScalingSupport.Identity = 1;
                LocalVidPnPresentPath.ContentTransformation.ScalingSupport.Centered = 1;
                SupportFieldsModified = TRUE;
            }
        } // End: SCALING

        // ROTATION: If this path's rotation isn't the pivot point, do work on the rotation support
        if (!((pEnumCofuncModality->EnumPivotType != D3DKMDT_EPT_ROTATION) &&
              (pEnumCofuncModality->EnumPivot.VidPnSourceId == pVidPnPresentPath->VidPnSourceId) &&
              (pEnumCofuncModality->EnumPivot.VidPnTargetId == pVidPnPresentPath->VidPnTargetId)))
        {
            // If the rotation is unpinned, then modify the rotation support field
            if (pVidPnPresentPath->ContentTransformation.Rotation == D3DKMDT_VPPR_UNPINNED)
            {
                LocalVidPnPresentPath.ContentTransformation.RotationSupport.Identity = 1;
                // Sample supports only Rotate90
                LocalVidPnPresentPath.ContentTransformation.RotationSupport.Rotate90 = 1;
                LocalVidPnPresentPath.ContentTransformation.RotationSupport.Rotate180 = 0;
                LocalVidPnPresentPath.ContentTransformation.RotationSupport.Rotate270 = 0;

                // Since clone is not supported, should not support path-independent rotations
                LocalVidPnPresentPath.ContentTransformation.RotationSupport.Offset0 = 1;

                SupportFieldsModified = TRUE;
            }
        } // End: ROTATION

        if (SupportFieldsModified)
        {
            // The correct path will be found by this function and the appropriate fields updated
            Status = pVidPnTopologyInterface->pfnUpdatePathSupportInfo(hVidPnTopology, &LocalVidPnPresentPath);
            if (!NT_SUCCESS(Status))
            {
                BDD_LOG_ERROR2("pfnUpdatePathSupportInfo failed with Status = 0x%I64x, hVidPnTopology = 0x%I64x", Status, hVidPnTopology);
                break;
            }
        }

        // Get the next path...
        // (NOTE: This is the value of Status that will return STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET when it's time to quit the loop)
        pVidPnPresentPathTemp = pVidPnPresentPath;
        Status = pVidPnTopologyInterface->pfnAcquireNextPathInfo(hVidPnTopology, pVidPnPresentPathTemp, &pVidPnPresentPath);
        if (!NT_SUCCESS(Status))
        {
            BDD_LOG_ERROR3("pfnAcquireNextPathInfo failed with Status = 0x%I64x, hVidPnTopology = 0x%I64x, pVidPnPresentPathTemp = 0x%I64x", Status, hVidPnTopology, pVidPnPresentPathTemp);
            break;
        }

        // ...and release the last path
        NTSTATUS TempStatus = pVidPnTopologyInterface->pfnReleasePathInfo(hVidPnTopology, pVidPnPresentPathTemp);
        if (!NT_SUCCESS(TempStatus))
        {
            BDD_LOG_ERROR3("pfnReleasePathInfo failed with Status = 0x%I64x, hVidPnTopology = 0x%I64x, pVidPnPresentPathTemp = 0x%I64x", TempStatus, hVidPnTopology, pVidPnPresentPathTemp);
            Status = TempStatus;
            break;
        }
        pVidPnPresentPathTemp = NULL; // Successfully released it
    }// End: while loop for paths in topology

    // If quit the while loop normally, set the return value to success
    if (Status == STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET)
    {
        Status = STATUS_SUCCESS;
    }

    // Release any resources hanging around because the loop was quit early.
    // Since in normal execution everything should be released by this point, TempStatus is initialized to a bogus error to be used as an
    //  assertion that if anything had to be released now (TempStatus changing) Status isn't successful.
    NTSTATUS TempStatus = STATUS_NOT_FOUND;

    if ((pVidPnSourceModeSetInterface != NULL) &&
        (pVidPnPinnedSourceModeInfo != NULL))
    {
        TempStatus = pVidPnSourceModeSetInterface->pfnReleaseModeInfo(hVidPnSourceModeSet, pVidPnPinnedSourceModeInfo);
        BDD_ASSERT_CHK(NT_SUCCESS(TempStatus));
    }

    if ((pVidPnTargetModeSetInterface != NULL) &&
        (pVidPnPinnedTargetModeInfo != NULL))
    {
        TempStatus = pVidPnTargetModeSetInterface->pfnReleaseModeInfo(hVidPnTargetModeSet, pVidPnPinnedTargetModeInfo);
        BDD_ASSERT_CHK(NT_SUCCESS(TempStatus));
    }

    if (pVidPnPresentPath != NULL)
    {
        TempStatus = pVidPnTopologyInterface->pfnReleasePathInfo(hVidPnTopology, pVidPnPresentPath);
        BDD_ASSERT_CHK(NT_SUCCESS(TempStatus));
    }

    if (pVidPnPresentPathTemp != NULL)
    {
        TempStatus = pVidPnTopologyInterface->pfnReleasePathInfo(hVidPnTopology, pVidPnPresentPathTemp);
        BDD_ASSERT_CHK(NT_SUCCESS(TempStatus));
    }

    if (hVidPnSourceModeSet != 0)
    {
        TempStatus = pVidPnInterface->pfnReleaseSourceModeSet(pEnumCofuncModality->hConstrainingVidPn, hVidPnSourceModeSet);
        BDD_ASSERT_CHK(NT_SUCCESS(TempStatus));
    }

    if (hVidPnTargetModeSet != 0)
    {
        TempStatus = pVidPnInterface->pfnReleaseTargetModeSet(pEnumCofuncModality->hConstrainingVidPn, hVidPnTargetModeSet);
        BDD_ASSERT_CHK(NT_SUCCESS(TempStatus));
    }

    BDD_ASSERT_CHK(TempStatus == STATUS_NOT_FOUND || Status != STATUS_SUCCESS);

    return Status;
}

NTSTATUS BASIC_DISPLAY_DRIVER::SetVidPnSourceVisibility(_In_ CONST DXGKARG_SETVIDPNSOURCEVISIBILITY* pSetVidPnSourceVisibility)
{
    PAGED_CODE();

    BDD_ASSERT(pSetVidPnSourceVisibility != NULL);
    BDD_ASSERT((pSetVidPnSourceVisibility->VidPnSourceId < MAX_VIEWS) ||
               (pSetVidPnSourceVisibility->VidPnSourceId == D3DDDI_ID_ALL));

    UINT StartVidPnSourceId = (pSetVidPnSourceVisibility->VidPnSourceId == D3DDDI_ID_ALL) ? 0 : pSetVidPnSourceVisibility->VidPnSourceId;
    UINT MaxVidPnSourceId = (pSetVidPnSourceVisibility->VidPnSourceId == D3DDDI_ID_ALL) ? MAX_VIEWS : pSetVidPnSourceVisibility->VidPnSourceId + 1;

    for (UINT SourceId = StartVidPnSourceId; SourceId < MaxVidPnSourceId; ++SourceId)
    {
        if (pSetVidPnSourceVisibility->Visible)
        {
            m_CurrentModes[SourceId].Flags.FullscreenPresent = TRUE;
        }
        else
        {
            BlackOutScreen(SourceId);
        }

        // Store current visibility so it can be dealt with during Present call
        m_CurrentModes[SourceId].Flags.SourceNotVisible = !(pSetVidPnSourceVisibility->Visible);
    }

    return STATUS_SUCCESS;
}

// NOTE: The value of pCommitVidPn->MonitorConnectivityChecks is ignored, since BDD is unable to recognize whether a monitor is connected or not
// The value of pCommitVidPn->hPrimaryAllocation is also ignored, since BDD is a display only driver and does not deal with allocations
NTSTATUS BASIC_DISPLAY_DRIVER::CommitVidPn(_In_ CONST DXGKARG_COMMITVIDPN* CONST pCommitVidPn)
{
    PAGED_CODE();

    BDD_ASSERT(pCommitVidPn != NULL);
    BDD_ASSERT(pCommitVidPn->AffectedVidPnSourceId < MAX_VIEWS);

    NTSTATUS                                 Status;
    SIZE_T                                   NumPaths = 0;
    D3DKMDT_HVIDPNTOPOLOGY                   hVidPnTopology = 0;
    D3DKMDT_HVIDPNSOURCEMODESET              hVidPnSourceModeSet = 0;
    CONST DXGK_VIDPN_INTERFACE*              pVidPnInterface = NULL;
    CONST DXGK_VIDPNTOPOLOGY_INTERFACE*      pVidPnTopologyInterface = NULL;
    CONST DXGK_VIDPNSOURCEMODESET_INTERFACE* pVidPnSourceModeSetInterface = NULL;
    CONST D3DKMDT_VIDPN_PRESENT_PATH*        pVidPnPresentPath = NULL;
    CONST D3DKMDT_VIDPN_SOURCE_MODE*         pPinnedVidPnSourceModeInfo = NULL;

    // Check this CommitVidPn is for the mode change notification when monitor is in power off state.
    if (pCommitVidPn->Flags.PathPoweredOff)
    {
        // Ignore the commitVidPn call for the mode change notification when monitor is in power off state.
        Status = STATUS_SUCCESS;
        goto CommitVidPnExit;
    }

    // Get the VidPn Interface so we can get the 'Source Mode Set' and 'VidPn Topology' interfaces
    Status = m_DxgkInterface.DxgkCbQueryVidPnInterface(pCommitVidPn->hFunctionalVidPn, DXGK_VIDPN_INTERFACE_VERSION_V1, &pVidPnInterface);
    if (!NT_SUCCESS(Status))
    {
        BDD_LOG_ERROR2("DxgkCbQueryVidPnInterface failed with Status = 0x%I64x, hFunctionalVidPn = 0x%I64x", Status, pCommitVidPn->hFunctionalVidPn);
        goto CommitVidPnExit;
    }

    // Get the VidPn Topology interface so can enumerate paths from source
    Status = pVidPnInterface->pfnGetTopology(pCommitVidPn->hFunctionalVidPn, &hVidPnTopology, &pVidPnTopologyInterface);
    if (!NT_SUCCESS(Status))
    {
        BDD_LOG_ERROR2("pfnGetTopology failed with Status = 0x%I64x, hFunctionalVidPn = 0x%I64x", Status, pCommitVidPn->hFunctionalVidPn);
        goto CommitVidPnExit;
    }

    // Find out the number of paths now, if it's 0 don't bother with source mode set and pinned mode, just clear current and then quit
    Status = pVidPnTopologyInterface->pfnGetNumPaths(hVidPnTopology, &NumPaths);
    if (!NT_SUCCESS(Status))
    {
        BDD_LOG_ERROR2("pfnGetNumPaths failed with Status = 0x%I64x, hVidPnTopology = 0x%I64x", Status, hVidPnTopology);
        goto CommitVidPnExit;
    }

    if (NumPaths != 0)
    {
        // Get the Source Mode Set interface so we can get the pinned mode
        Status = pVidPnInterface->pfnAcquireSourceModeSet(pCommitVidPn->hFunctionalVidPn,
                                                          pCommitVidPn->AffectedVidPnSourceId,
                                                          &hVidPnSourceModeSet,
                                                          &pVidPnSourceModeSetInterface);
        if (!NT_SUCCESS(Status))
        {
            BDD_LOG_ERROR3("pfnAcquireSourceModeSet failed with Status = 0x%I64x, hFunctionalVidPn = 0x%I64x, SourceId = 0x%I64x", Status, pCommitVidPn->hFunctionalVidPn, pCommitVidPn->AffectedVidPnSourceId);
            goto CommitVidPnExit;
        }

        // Get the mode that is being pinned
        Status = pVidPnSourceModeSetInterface->pfnAcquirePinnedModeInfo(hVidPnSourceModeSet, &pPinnedVidPnSourceModeInfo);
        if (!NT_SUCCESS(Status))
        {
            BDD_LOG_ERROR2("pfnAcquirePinnedModeInfo failed with Status = 0x%I64x, hFunctionalVidPn = 0x%I64x", Status, pCommitVidPn->hFunctionalVidPn);
            goto CommitVidPnExit;
        }
    }
    else
    {
        // This will cause the successful quit below
        pPinnedVidPnSourceModeInfo = NULL;
    }

    if (m_CurrentModes[pCommitVidPn->AffectedVidPnSourceId].FrameBuffer.Ptr &&
        !m_CurrentModes[pCommitVidPn->AffectedVidPnSourceId].Flags.DoNotMapOrUnmap)
    {
        Status = UnmapFrameBuffer(m_CurrentModes[pCommitVidPn->AffectedVidPnSourceId].FrameBuffer.Ptr,
                                  m_CurrentModes[pCommitVidPn->AffectedVidPnSourceId].DispInfo.Pitch * m_CurrentModes[pCommitVidPn->AffectedVidPnSourceId].DispInfo.Height);
        m_CurrentModes[pCommitVidPn->AffectedVidPnSourceId].FrameBuffer.Ptr = NULL;
        m_CurrentModes[pCommitVidPn->AffectedVidPnSourceId].Flags.FrameBufferIsActive = FALSE;

        if (!NT_SUCCESS(Status))
        {
            goto CommitVidPnExit;
        }
    }

    if (pPinnedVidPnSourceModeInfo == NULL)
    {
        // There is no mode to pin on this source, any old paths here have already been cleared
        Status = STATUS_SUCCESS;
        goto CommitVidPnExit;
    }

    Status = IsVidPnSourceModeFieldsValid(pPinnedVidPnSourceModeInfo);
    if (!NT_SUCCESS(Status))
    {
        goto CommitVidPnExit;
    }

    // Get the number of paths from this source so we can loop through all paths
    SIZE_T NumPathsFromSource = 0;
    Status = pVidPnTopologyInterface->pfnGetNumPathsFromSource(hVidPnTopology, pCommitVidPn->AffectedVidPnSourceId, &NumPathsFromSource);
    if (!NT_SUCCESS(Status))
    {
        BDD_LOG_ERROR2("pfnGetNumPathsFromSource failed with Status = 0x%I64x, hVidPnTopology = 0x%I64x", Status, hVidPnTopology);
        goto CommitVidPnExit;
    }

    // Loop through all paths to set this mode
    for (SIZE_T PathIndex = 0; PathIndex < NumPathsFromSource; ++PathIndex)
    {
        // Get the target id for this path
        D3DDDI_VIDEO_PRESENT_TARGET_ID TargetId = D3DDDI_ID_UNINITIALIZED;
        Status = pVidPnTopologyInterface->pfnEnumPathTargetsFromSource(hVidPnTopology, pCommitVidPn->AffectedVidPnSourceId, PathIndex, &TargetId);
        if (!NT_SUCCESS(Status))
        {
            BDD_LOG_ERROR4("pfnEnumPathTargetsFromSource failed with Status = 0x%I64x, hVidPnTopology = 0x%I64x, SourceId = 0x%I64x, PathIndex = 0x%I64x",
                            Status, hVidPnTopology, pCommitVidPn->AffectedVidPnSourceId, PathIndex);
            goto CommitVidPnExit;
        }

        // Get the actual path info
        Status = pVidPnTopologyInterface->pfnAcquirePathInfo(hVidPnTopology, pCommitVidPn->AffectedVidPnSourceId, TargetId, &pVidPnPresentPath);
        if (!NT_SUCCESS(Status))
        {
            BDD_LOG_ERROR4("pfnAcquirePathInfo failed with Status = 0x%I64x, hVidPnTopology = 0x%I64x, SourceId = 0x%I64x, TargetId = 0x%I64x",
                            Status, hVidPnTopology, pCommitVidPn->AffectedVidPnSourceId, TargetId);
            goto CommitVidPnExit;
        }

        Status = IsVidPnPathFieldsValid(pVidPnPresentPath);
        if (!NT_SUCCESS(Status))
        {
            goto CommitVidPnExit;
        }

        Status = SetSourceModeAndPath(pPinnedVidPnSourceModeInfo, pVidPnPresentPath);
        if (!NT_SUCCESS(Status))
        {
            goto CommitVidPnExit;
        }

        Status = pVidPnTopologyInterface->pfnReleasePathInfo(hVidPnTopology, pVidPnPresentPath);
        if (!NT_SUCCESS(Status))
        {
            BDD_LOG_ERROR3("pfnReleasePathInfo failed with Status = 0x%I64x, hVidPnTopoogy = 0x%I64x, pVidPnPresentPath = 0x%I64x",
                            Status, hVidPnTopology, pVidPnPresentPath);
            goto CommitVidPnExit;
        }
        pVidPnPresentPath = NULL; // Successfully released it
    }

CommitVidPnExit:

    NTSTATUS TempStatus;

    if ((pVidPnSourceModeSetInterface != NULL) &&
        (hVidPnSourceModeSet != 0) &&
        (pPinnedVidPnSourceModeInfo != NULL))
    {
        TempStatus = pVidPnSourceModeSetInterface->pfnReleaseModeInfo(hVidPnSourceModeSet, pPinnedVidPnSourceModeInfo);
        NT_ASSERT(NT_SUCCESS(TempStatus));
    }

    if ((pVidPnInterface != NULL) &&
        (pCommitVidPn->hFunctionalVidPn != 0) &&
        (hVidPnSourceModeSet != 0))
    {
        TempStatus = pVidPnInterface->pfnReleaseSourceModeSet(pCommitVidPn->hFunctionalVidPn, hVidPnSourceModeSet);
        NT_ASSERT(NT_SUCCESS(TempStatus));
    }

    if ((pVidPnTopologyInterface != NULL) &&
        (hVidPnTopology != 0) &&
        (pVidPnPresentPath != NULL))
    {
        TempStatus = pVidPnTopologyInterface->pfnReleasePathInfo(hVidPnTopology, pVidPnPresentPath);
        NT_ASSERT(NT_SUCCESS(TempStatus));
    }

    return Status;
}

NTSTATUS BASIC_DISPLAY_DRIVER::UpdateActiveVidPnPresentPath(_In_ CONST DXGKARG_UPDATEACTIVEVIDPNPRESENTPATH* CONST pUpdateActiveVidPnPresentPath)
{
    PAGED_CODE();

    BDD_ASSERT(pUpdateActiveVidPnPresentPath != NULL);

    NTSTATUS Status = IsVidPnPathFieldsValid(&(pUpdateActiveVidPnPresentPath->VidPnPresentPathInfo));
    if (!NT_SUCCESS(Status))
    {
        return Status;
    }

    // Mark the next present as fullscreen to make sure the full rotation comes through
    m_CurrentModes[pUpdateActiveVidPnPresentPath->VidPnPresentPathInfo.VidPnSourceId].Flags.FullscreenPresent = TRUE;

    m_CurrentModes[pUpdateActiveVidPnPresentPath->VidPnPresentPathInfo.VidPnSourceId].Rotation = pUpdateActiveVidPnPresentPath->VidPnPresentPathInfo.ContentTransformation.Rotation;

    return STATUS_SUCCESS;
}

//
// Private BDD DMM functions
//

NTSTATUS BASIC_DISPLAY_DRIVER::SetSourceModeAndPath(CONST D3DKMDT_VIDPN_SOURCE_MODE* pSourceMode,
                                                    CONST D3DKMDT_VIDPN_PRESENT_PATH* pPath)
{
    PAGED_CODE();

    CURRENT_BDD_MODE* pCurrentBddMode = &m_CurrentModes[pPath->VidPnSourceId];

    NTSTATUS Status = STATUS_SUCCESS;
    pCurrentBddMode->Scaling = pPath->ContentTransformation.Scaling;
    pCurrentBddMode->SrcModeWidth = pSourceMode->Format.Graphics.PrimSurfSize.cx;
    pCurrentBddMode->SrcModeHeight = pSourceMode->Format.Graphics.PrimSurfSize.cy;
    pCurrentBddMode->Rotation = pPath->ContentTransformation.Rotation;


    if (!pCurrentBddMode->Flags.DoNotMapOrUnmap)
    {
        // Map the new frame buffer
        BDD_ASSERT(pCurrentBddMode->FrameBuffer.Ptr == NULL);
        Status = MapFrameBuffer(pCurrentBddMode->DispInfo.PhysicAddress,
                                pCurrentBddMode->DispInfo.Pitch * pCurrentBddMode->DispInfo.Height,
                                &(pCurrentBddMode->FrameBuffer.Ptr));
    }

    if (NT_SUCCESS(Status))
    {

        pCurrentBddMode->Flags.FrameBufferIsActive = TRUE;
        BlackOutScreen(pPath->VidPnSourceId);

        // Mark that the next present should be fullscreen so the screen doesn't go from black to actual pixels one dirty rect at a time.
        pCurrentBddMode->Flags.FullscreenPresent = TRUE;
    }

    return Status;
}


NTSTATUS BASIC_DISPLAY_DRIVER::IsVidPnPathFieldsValid(CONST D3DKMDT_VIDPN_PRESENT_PATH* pPath) const
{
    PAGED_CODE();

    if (pPath->VidPnSourceId >= MAX_VIEWS)
    {
        BDD_LOG_ERROR2("VidPnSourceId is 0x%I64x is too high (MAX_VIEWS is 0x%I64x)",
                        pPath->VidPnSourceId, MAX_VIEWS);
        return STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE;
    }
    else if (pPath->VidPnTargetId >= MAX_CHILDREN)
    {
        BDD_LOG_ERROR2("VidPnTargetId is 0x%I64x is too high (MAX_CHILDREN is 0x%I64x)",
                        pPath->VidPnTargetId, MAX_CHILDREN);
        return STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET;
    }
    else if (pPath->GammaRamp.Type != D3DDDI_GAMMARAMP_DEFAULT)
    {
        BDD_LOG_ERROR1("pPath contains a gamma ramp (0x%I64x)", pPath->GammaRamp.Type);
        return STATUS_GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED;
    }
    else if ((pPath->ContentTransformation.Scaling != D3DKMDT_VPPS_IDENTITY) &&
             (pPath->ContentTransformation.Scaling != D3DKMDT_VPPS_CENTERED) &&
             (pPath->ContentTransformation.Scaling != D3DKMDT_VPPS_NOTSPECIFIED) &&
             (pPath->ContentTransformation.Scaling != D3DKMDT_VPPS_UNINITIALIZED))
    {
        BDD_LOG_ERROR1("pPath contains a non-identity scaling (0x%I64x)", pPath->ContentTransformation.Scaling);
        return STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED;
    }
    else if ((pPath->ContentTransformation.Rotation != D3DKMDT_VPPR_IDENTITY) &&
             (pPath->ContentTransformation.Rotation != D3DKMDT_VPPR_ROTATE90) &&
             (pPath->ContentTransformation.Rotation != D3DKMDT_VPPR_NOTSPECIFIED) &&
             (pPath->ContentTransformation.Rotation != D3DKMDT_VPPR_UNINITIALIZED))
    {
        BDD_LOG_ERROR1("pPath contains a not-supported rotation (0x%I64x)", pPath->ContentTransformation.Rotation);
        return STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED;
    }
    else if ((pPath->VidPnTargetColorBasis != D3DKMDT_CB_SCRGB) &&
             (pPath->VidPnTargetColorBasis != D3DKMDT_CB_UNINITIALIZED))
    {
        BDD_LOG_ERROR1("pPath has a non-linear RGB color basis (0x%I64x)", pPath->VidPnTargetColorBasis);
        return STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE;
    }
    else
    {
        return STATUS_SUCCESS;
    }
}

NTSTATUS BASIC_DISPLAY_DRIVER::IsVidPnSourceModeFieldsValid(CONST D3DKMDT_VIDPN_SOURCE_MODE* pSourceMode) const
{
    PAGED_CODE();

    if (pSourceMode->Type != D3DKMDT_RMT_GRAPHICS)
    {
        BDD_LOG_ERROR1("pSourceMode is a non-graphics mode (0x%I64x)", pSourceMode->Type);
        return STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE;
    }
    else if ((pSourceMode->Format.Graphics.ColorBasis != D3DKMDT_CB_SCRGB) &&
             (pSourceMode->Format.Graphics.ColorBasis != D3DKMDT_CB_UNINITIALIZED))
    {
        BDD_LOG_ERROR1("pSourceMode has a non-linear RGB color basis (0x%I64x)", pSourceMode->Format.Graphics.ColorBasis);
        return STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE;
    }
    else if (pSourceMode->Format.Graphics.PixelValueAccessMode != D3DKMDT_PVAM_DIRECT)
    {
        BDD_LOG_ERROR1("pSourceMode has a palettized access mode (0x%I64x)", pSourceMode->Format.Graphics.PixelValueAccessMode);
        return STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE;
    }
    else
    {
        for (UINT PelFmtIdx = 0; PelFmtIdx < ARRAYSIZE(gBddPixelFormats); ++PelFmtIdx)
        {
            if (pSourceMode->Format.Graphics.PixelFormat == gBddPixelFormats[PelFmtIdx])
            {
                return STATUS_SUCCESS;
            }
        }

        BDD_LOG_ERROR1("pSourceMode has an unknown pixel format (0x%I64x)", pSourceMode->Format.Graphics.PixelFormat);
        return STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE;
    }
}



// Add more mode from the table.
struct SampleSourceMode
{
    UINT ModeWidth;
    UINT ModeHeight;
};

// The driver will advertise all modes that fit within the actual required mode (see AddSingleSourceMode below)
const static SampleSourceMode C_SampleSourceMode[] = {{800,600},{1024,768},{1152,864},{1280,800},{1280,1024},{1400,1050},{1600,1200},{1680,1050},{1920,1200}};
const static UINT C_SampleSourceModeMax = sizeof(C_SampleSourceMode)/sizeof(C_SampleSourceMode[0]);

NTSTATUS BASIC_DISPLAY_DRIVER::AddSingleSourceMode(_In_ CONST DXGK_VIDPNSOURCEMODESET_INTERFACE* pVidPnSourceModeSetInterface,
                                                   D3DKMDT_HVIDPNSOURCEMODESET hVidPnSourceModeSet,
                                                   D3DDDI_VIDEO_PRESENT_SOURCE_ID SourceId)
{
    PAGED_CODE();

    // There is only one source format supported by display-only drivers, but more can be added in a 
    // full WDDM driver if the hardware supports them
    for (UINT PelFmtIdx = 0; PelFmtIdx < ARRAYSIZE(gBddPixelFormats); ++PelFmtIdx)
    {
        // Create new mode info that will be populated
        D3DKMDT_VIDPN_SOURCE_MODE* pVidPnSourceModeInfo = NULL;
        NTSTATUS Status = pVidPnSourceModeSetInterface->pfnCreateNewModeInfo(hVidPnSourceModeSet, &pVidPnSourceModeInfo);
        if (!NT_SUCCESS(Status))
        {
            // If failed to create a new mode info, mode doesn't need to be released since it was never created
            BDD_LOG_ERROR2("pfnCreateNewModeInfo failed with Status = 0x%I64x, hVidPnSourceModeSet = 0x%I64x", Status, hVidPnSourceModeSet);
            return Status;
        }

        // Populate mode info with values from current mode and hard-coded values
        // Always report 32 bpp format, this will be color converted during the present if the mode is < 32bpp
        pVidPnSourceModeInfo->Type = D3DKMDT_RMT_GRAPHICS;
        pVidPnSourceModeInfo->Format.Graphics.PrimSurfSize.cx = m_CurrentModes[SourceId].DispInfo.Width;
        pVidPnSourceModeInfo->Format.Graphics.PrimSurfSize.cy = m_CurrentModes[SourceId].DispInfo.Height;
        pVidPnSourceModeInfo->Format.Graphics.VisibleRegionSize = pVidPnSourceModeInfo->Format.Graphics.PrimSurfSize;
        pVidPnSourceModeInfo->Format.Graphics.Stride = m_CurrentModes[SourceId].DispInfo.Pitch;
        pVidPnSourceModeInfo->Format.Graphics.PixelFormat = gBddPixelFormats[PelFmtIdx];
        pVidPnSourceModeInfo->Format.Graphics.ColorBasis = D3DKMDT_CB_SCRGB;
        pVidPnSourceModeInfo->Format.Graphics.PixelValueAccessMode = D3DKMDT_PVAM_DIRECT;

        // Add the mode to the source mode set
        Status = pVidPnSourceModeSetInterface->pfnAddMode(hVidPnSourceModeSet, pVidPnSourceModeInfo);
        if (!NT_SUCCESS(Status))
        {
            // If adding the mode failed, release the mode, if this doesn't work there is nothing that can be done, some memory will get leaked
            NTSTATUS TempStatus = pVidPnSourceModeSetInterface->pfnReleaseModeInfo(hVidPnSourceModeSet, pVidPnSourceModeInfo);
            UNREFERENCED_PARAMETER(TempStatus);
            NT_ASSERT(NT_SUCCESS(TempStatus));

            if (Status != STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET)
            {
                BDD_LOG_ERROR3("pfnAddMode failed with Status = 0x%I64x, hVidPnSourceModeSet = 0x%I64x, pVidPnSourceModeInfo = 0x%I64x", Status, hVidPnSourceModeSet, pVidPnSourceModeInfo);
                return Status;
            }
        }
    }

    UINT WidthMax = m_CurrentModes[SourceId].DispInfo.Width;
    UINT HeightMax = m_CurrentModes[SourceId].DispInfo.Height;

    // Add all predefined modes that fit within the bounds of the required (POST) mode
    for (UINT ModeIndex = 0; ModeIndex < C_SampleSourceModeMax; ++ModeIndex)
    {
        if (C_SampleSourceMode[ModeIndex].ModeWidth > WidthMax)
        {
            break;
        }
        else if (C_SampleSourceMode[ModeIndex].ModeWidth == WidthMax)
        {
            if(C_SampleSourceMode[ModeIndex].ModeHeight >= HeightMax)
            {
                break;
            }
        }
        else
        {
            if(C_SampleSourceMode[ModeIndex].ModeHeight > HeightMax)
            {
                continue;
            }
        }

        // There is only one source format supported by display-only drivers, but more can be added in a 
        // full WDDM driver if the hardware supports them
        for (UINT PelFmtIdx = 0; PelFmtIdx < ARRAYSIZE(gBddPixelFormats); ++PelFmtIdx)
        {
            // Create new mode info that will be populated
            D3DKMDT_VIDPN_SOURCE_MODE* pVidPnSourceModeInfo = NULL;
            NTSTATUS Status = pVidPnSourceModeSetInterface->pfnCreateNewModeInfo(hVidPnSourceModeSet, &pVidPnSourceModeInfo);
            if (!NT_SUCCESS(Status))
            {
                // If failed to create a new mode info, continuing to the next mode and trying again isn't going to be at all helpful, so return
                // Also, mode doesn't need to be released since it was never created
                BDD_LOG_ERROR2("pfnCreateNewModeInfo failed with Status = 0x%I64x, hVidPnSourceModeSet = 0x%I64x", Status, hVidPnSourceModeSet);
                return Status;
            }

            // Populate mode info with values from mode at ModeIndex and hard-coded values
            // Always report 32 bpp format, this will be color converted during the present if the mode at ModeIndex was < 32bpp
            pVidPnSourceModeInfo->Type = D3DKMDT_RMT_GRAPHICS;
            pVidPnSourceModeInfo->Format.Graphics.PrimSurfSize.cx = C_SampleSourceMode[ModeIndex].ModeWidth;
            pVidPnSourceModeInfo->Format.Graphics.PrimSurfSize.cy = C_SampleSourceMode[ModeIndex].ModeHeight;
            pVidPnSourceModeInfo->Format.Graphics.VisibleRegionSize = pVidPnSourceModeInfo->Format.Graphics.PrimSurfSize;
            pVidPnSourceModeInfo->Format.Graphics.Stride = 4*C_SampleSourceMode[ModeIndex].ModeWidth;
            pVidPnSourceModeInfo->Format.Graphics.PixelFormat = gBddPixelFormats[PelFmtIdx];
            pVidPnSourceModeInfo->Format.Graphics.ColorBasis = D3DKMDT_CB_SCRGB;
            pVidPnSourceModeInfo->Format.Graphics.PixelValueAccessMode = D3DKMDT_PVAM_DIRECT;

            // Add the mode to the source mode set
            Status = pVidPnSourceModeSetInterface->pfnAddMode(hVidPnSourceModeSet, pVidPnSourceModeInfo);
            if (!NT_SUCCESS(Status))
            {
                if (Status != STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET)
                {
                    BDD_LOG_ERROR3("pfnAddMode failed with Status = 0x%I64x, hVidPnSourceModeSet = 0x%I64x, pVidPnSourceModeInfo = 0x%I64x", Status, hVidPnSourceModeSet, pVidPnSourceModeInfo);
                }

                // If adding the mode failed, release the mode, if this doesn't work there is nothing that can be done, some memory will get leaked, continue to next mode anyway
                Status = pVidPnSourceModeSetInterface->pfnReleaseModeInfo(hVidPnSourceModeSet, pVidPnSourceModeInfo);
                BDD_ASSERT_CHK(NT_SUCCESS(Status));
            }
        }
    }


    return STATUS_SUCCESS;
}


// Add the current mode information (acquired from the POST frame buffer) as the target mode.
NTSTATUS BASIC_DISPLAY_DRIVER::AddSingleTargetMode(_In_ CONST DXGK_VIDPNTARGETMODESET_INTERFACE* pVidPnTargetModeSetInterface,
                                                   D3DKMDT_HVIDPNTARGETMODESET hVidPnTargetModeSet,
                                                   _In_opt_ CONST D3DKMDT_VIDPN_SOURCE_MODE* pVidPnPinnedSourceModeInfo,
                                                   D3DDDI_VIDEO_PRESENT_SOURCE_ID SourceId)
{
    PAGED_CODE();

    D3DKMDT_VIDPN_TARGET_MODE* pVidPnTargetModeInfo = NULL;
    NTSTATUS Status = pVidPnTargetModeSetInterface->pfnCreateNewModeInfo(hVidPnTargetModeSet, &pVidPnTargetModeInfo);
    if (!NT_SUCCESS(Status))
    {
        // If failed to create a new mode info, mode doesn't need to be released since it was never created
        BDD_LOG_ERROR2("pfnCreateNewModeInfo failed with Status = 0x%I64x, hVidPnTargetModeSet = 0x%I64x", Status, hVidPnTargetModeSet);
        return Status;
    }

    pVidPnTargetModeInfo->VideoSignalInfo.VideoStandard = D3DKMDT_VSS_OTHER;
    UNREFERENCED_PARAMETER(pVidPnPinnedSourceModeInfo);
    pVidPnTargetModeInfo->VideoSignalInfo.TotalSize.cx = m_CurrentModes[SourceId].DispInfo.Width;
    pVidPnTargetModeInfo->VideoSignalInfo.TotalSize.cy = m_CurrentModes[SourceId].DispInfo.Height;
    pVidPnTargetModeInfo->VideoSignalInfo.ActiveSize = pVidPnTargetModeInfo->VideoSignalInfo.TotalSize;
    pVidPnTargetModeInfo->VideoSignalInfo.VSyncFreq.Numerator = D3DKMDT_FREQUENCY_NOTSPECIFIED;
    pVidPnTargetModeInfo->VideoSignalInfo.VSyncFreq.Denominator = D3DKMDT_FREQUENCY_NOTSPECIFIED;
    pVidPnTargetModeInfo->VideoSignalInfo.HSyncFreq.Numerator = D3DKMDT_FREQUENCY_NOTSPECIFIED;
    pVidPnTargetModeInfo->VideoSignalInfo.HSyncFreq.Denominator = D3DKMDT_FREQUENCY_NOTSPECIFIED;
    pVidPnTargetModeInfo->VideoSignalInfo.PixelRate = D3DKMDT_FREQUENCY_NOTSPECIFIED;
    pVidPnTargetModeInfo->VideoSignalInfo.ScanLineOrdering = D3DDDI_VSSLO_PROGRESSIVE;
    // We add this as PREFERRED since it is the only supported target
    pVidPnTargetModeInfo->Preference = D3DKMDT_MP_PREFERRED;

    Status = pVidPnTargetModeSetInterface->pfnAddMode(hVidPnTargetModeSet, pVidPnTargetModeInfo);
    if (!NT_SUCCESS(Status))
    {
        if (Status != STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET)
        {
            BDD_LOG_ERROR3("pfnAddMode failed with Status = 0x%I64x, hVidPnTargetModeSet = 0x%I64x, pVidPnTargetModeInfo = 0x%I64x", Status, hVidPnTargetModeSet, pVidPnTargetModeInfo);
        }
        else
        {
            Status = STATUS_SUCCESS;
        }

        // If adding the mode failed, release the mode, if this doesn't work there is nothing that can be done, some memory will get leaked
        NTSTATUS TempStatus = pVidPnTargetModeSetInterface->pfnReleaseModeInfo(hVidPnTargetModeSet, pVidPnTargetModeInfo);
        UNREFERENCED_PARAMETER(TempStatus);
        NT_ASSERT(NT_SUCCESS(TempStatus));
        return Status;
    }
    else
    {
        // If AddMode succeeded with something other than STATUS_SUCCESS treat it as such anyway when propagating up
        return STATUS_SUCCESS;
    }
}


NTSTATUS BASIC_DISPLAY_DRIVER::AddSingleMonitorMode(_In_ CONST DXGKARG_RECOMMENDMONITORMODES* CONST pRecommendMonitorModes)
{
    PAGED_CODE();

    D3DKMDT_MONITOR_SOURCE_MODE* pMonitorSourceMode = NULL;
    NTSTATUS Status = pRecommendMonitorModes->pMonitorSourceModeSetInterface->pfnCreateNewModeInfo(pRecommendMonitorModes->hMonitorSourceModeSet, &pMonitorSourceMode);
    if (!NT_SUCCESS(Status))
    {
        // If failed to create a new mode info, mode doesn't need to be released since it was never created
        BDD_LOG_ERROR2("pfnCreateNewModeInfo failed with Status = 0x%I64x, hMonitorSourceModeSet = 0x%I64x", Status, pRecommendMonitorModes->hMonitorSourceModeSet);
        return Status;
    }

    D3DDDI_VIDEO_PRESENT_SOURCE_ID CorrespondingSourceId = FindSourceForTarget(pRecommendMonitorModes->VideoPresentTargetId, TRUE);

    // Since we don't know the real monitor timing information, just use the current display mode (from the POST device) with unknown frequencies
    pMonitorSourceMode->VideoSignalInfo.VideoStandard = D3DKMDT_VSS_OTHER;
    pMonitorSourceMode->VideoSignalInfo.TotalSize.cx = m_CurrentModes[CorrespondingSourceId].DispInfo.Width;
    pMonitorSourceMode->VideoSignalInfo.TotalSize.cy = m_CurrentModes[CorrespondingSourceId].DispInfo.Height;
    pMonitorSourceMode->VideoSignalInfo.ActiveSize = pMonitorSourceMode->VideoSignalInfo.TotalSize;
    pMonitorSourceMode->VideoSignalInfo.VSyncFreq.Numerator = D3DKMDT_FREQUENCY_NOTSPECIFIED;
    pMonitorSourceMode->VideoSignalInfo.VSyncFreq.Denominator = D3DKMDT_FREQUENCY_NOTSPECIFIED;
    pMonitorSourceMode->VideoSignalInfo.HSyncFreq.Numerator = D3DKMDT_FREQUENCY_NOTSPECIFIED;
    pMonitorSourceMode->VideoSignalInfo.HSyncFreq.Denominator = D3DKMDT_FREQUENCY_NOTSPECIFIED;
    pMonitorSourceMode->VideoSignalInfo.PixelRate = D3DKMDT_FREQUENCY_NOTSPECIFIED;
    pMonitorSourceMode->VideoSignalInfo.ScanLineOrdering = D3DDDI_VSSLO_PROGRESSIVE;

    // We set the preference to PREFERRED since this is the only supported mode
    pMonitorSourceMode->Origin = D3DKMDT_MCO_DRIVER;
    pMonitorSourceMode->Preference = D3DKMDT_MP_PREFERRED;
    pMonitorSourceMode->ColorBasis = D3DKMDT_CB_SRGB;
    pMonitorSourceMode->ColorCoeffDynamicRanges.FirstChannel = 8;
    pMonitorSourceMode->ColorCoeffDynamicRanges.SecondChannel = 8;
    pMonitorSourceMode->ColorCoeffDynamicRanges.ThirdChannel = 8;
    pMonitorSourceMode->ColorCoeffDynamicRanges.FourthChannel = 8;

    Status = pRecommendMonitorModes->pMonitorSourceModeSetInterface->pfnAddMode(pRecommendMonitorModes->hMonitorSourceModeSet, pMonitorSourceMode);
    if (!NT_SUCCESS(Status))
    {
        if (Status != STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET)
        {
            BDD_LOG_ERROR3("pfnAddMode failed with Status = 0x%I64x, hMonitorSourceModeSet = 0x%I64x, pMonitorSourceMode = 0x%I64x",
                            Status, pRecommendMonitorModes->hMonitorSourceModeSet, pMonitorSourceMode);
        }
        else
        {
            Status = STATUS_SUCCESS;
        }

        // If adding the mode failed, release the mode, if this doesn't work there is nothing that can be done, some memory will get leaked
        NTSTATUS TempStatus = pRecommendMonitorModes->pMonitorSourceModeSetInterface->pfnReleaseModeInfo(pRecommendMonitorModes->hMonitorSourceModeSet, pMonitorSourceMode);
        UNREFERENCED_PARAMETER(TempStatus);
        NT_ASSERT(NT_SUCCESS(TempStatus));
        return Status;
    }
    else
    {
        // If AddMode succeeded with something other than STATUS_SUCCESS treat it as such anyway when propagating up
        return STATUS_SUCCESS;
    }
}

C++程式檔案 bdd_util.cxx

/******************************Module*Header*******************************\
* Module Name: bdd_util.cxx
*
* Basic Display Driver utility functions
*
* Created: 29-Mar-2011
* Author: Amos Eshel [amosesh]
*
* Copyright (c) 2011 Microsoft Corporation
\**************************************************************************/

#include "BDD.hxx"


#pragma code_seg("PAGE")

//
// EDID validation
//

BOOLEAN IsEdidHeaderValid(_In_reads_bytes_(EDID_V1_BLOCK_SIZE) const BYTE* pEdid)
{
    PAGED_CODE();

    static const UCHAR EDID1Header[8] = {0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0};
    return memcmp(pEdid, EDID1Header, sizeof(EDID1Header)) == 0;
}

BOOLEAN IsEdidChecksumValid(_In_reads_bytes_(EDID_V1_BLOCK_SIZE) const BYTE* pEdid)
{
    PAGED_CODE();

    BYTE CheckSum = 0;
    for (const BYTE* pEdidStart = pEdid; pEdidStart < (pEdid + EDID_V1_BLOCK_SIZE); ++pEdidStart)
    {
        CheckSum += *pEdidStart;
    }

    return CheckSum == 0;
}

//
// Frame buffer map/unmap
//

NTSTATUS
MapFrameBuffer(
    _In_                       PHYSICAL_ADDRESS    PhysicalAddress,
    _In_                       ULONG               Length,
    _Outptr_result_bytebuffer_(Length) VOID**              VirtualAddress)
{
    PAGED_CODE();

    //
    // Check for parameters
    //
    if ((PhysicalAddress.QuadPart == (ULONGLONG)0) ||
        (Length == 0) ||
        (VirtualAddress == NULL))
    {
        BDD_LOG_ERROR3("One of PhysicalAddress.QuadPart (0x%I64x), Length (0x%I64x), VirtualAddress (0x%I64x) is NULL or 0",
                        PhysicalAddress.QuadPart, Length, VirtualAddress);
        return STATUS_INVALID_PARAMETER;
    }

    *VirtualAddress = MmMapIoSpaceEx(PhysicalAddress,
                                   Length,
                                   PAGE_READWRITE | PAGE_WRITECOMBINE);
    if (*VirtualAddress == NULL)
    {
        // The underlying call to MmMapIoSpace failed. This may be because, MmWriteCombined
        // isn't supported, so try again with MmNonCached

        *VirtualAddress = MmMapIoSpaceEx(PhysicalAddress,
                                       Length,
                                       PAGE_READWRITE | PAGE_NOCACHE);
        if (*VirtualAddress == NULL)
        {
            BDD_LOG_LOW_RESOURCE1("MmMapIoSpace returned a NULL buffer when trying to allocate 0x%I64x bytes", Length);
            return STATUS_NO_MEMORY;
        }
    }

    return STATUS_SUCCESS;
}

NTSTATUS
UnmapFrameBuffer(
    _In_reads_bytes_(Length) VOID* VirtualAddress,
    _In_                ULONG Length)
{
    PAGED_CODE();


    //
    // Check for parameters
    //
    if ((VirtualAddress == NULL) && (Length == 0))
    {
        // Allow this function to be called when there's no work to do, and treat as successful
        return STATUS_SUCCESS;
    }
    else if ((VirtualAddress == NULL) || (Length == 0))
    {
        BDD_LOG_ERROR2("Only one of Length (0x%I64x), VirtualAddress (0x%I64x) is NULL or 0",
                        Length, VirtualAddress);
        return STATUS_INVALID_PARAMETER;
    }

    MmUnmapIoSpace(VirtualAddress,
                   Length);

    return STATUS_SUCCESS;
}

最後找出diagnostic tool關於 video & graphic card部分的測試

參考文章:

微軟 Getting started with Windows drivers
Steward
小z说:内核驱动的编译环境
VS2012开发驱动小Tip之:更改KMDF版本以支持XP系统

[轉] 關於用戶模式驅動程序框架(UMDF)的一點感想

$
0
0

(轉錄自: accessory )

  
  最近又要在WINDOWS下面寫點驅動,於是學習了下目前比較流行的WDF,主要是UMDF。因為以前用過一陣WDM,所以對KERNEL MODE的KMDF興趣不是特別大。
  
  WDF的全稱是Windows Driver Foundation,其中又包括了UMDF和KMDF。前者是USER LEVEL的驅動,後者是KERNEL LEVEL。在WDF出來之前,流行的WINDOWS驅動結構叫做WDM(WINDOWS DRIVER MODEL)。在WDM之前,WIN NT和WIN 95的驅動模式是不一樣的.WIN NT的比較規範,什麼東西都只能在內核做.NT這一套一直發展下來,變成了後來的WDM,WDF。不知道未來又會出現啥新名詞.WIN 95那一套很多地方限制沒有那麼嚴,這同時也造成了OS不夠穩定。在WIN 95之前,就是WIN 3.X和純真的DOS年代了。現在的BIOS依然純真,不過馬上就要變成複雜的UEFI了…. =。=
  
  回歸正題,以前剛學WDM的時候,就被告知一切對硬件的操作都必須要在KERNEL LEVEL做才行。於是形成了一個思維定勢,只有KERNEL LEVEL才可以操作硬件。多年以後,開始學習LINUX ,驚訝的發現LINUX下面是可以允許USER LEVEL APP直接訪問硬件端口的。再看INTEL手冊,發現硬件並沒有設計成只能在KERNEL LEVEL才能訪問…這時才明白,原來WINDOWS是故意那麼設計的…
  
  LINUX下的USER LEVEL APP也是有ROOT權限才可以訪問硬件端口。同時也有一些限制,比如處理中斷和DMA。不過能訪問端口本身已經提供了很多方便。有的時候,只想寫個小程序訪問2 ,3個IO端口。這個在LINUX下很簡單,在WINDOWS下就要自己寫驅動,或者藉助其他工具和庫了。
  
  後來某一天,驚聞WINDOWS最新的UMDF也是在USER LEVEL下的驅動。於是心想,是不是WINDOWS終於想明白,要跟LINUX學,允許USER LEVEL APP訪問端口了呢?本來我以為是這樣的,但是一直沒仔細看UMDF。今天看了一下後,找了半天,也沒有看到類似LINUX那樣可以直接訪問端口的東西。最後發現,UMDF還是要用FILE OBJECT去訪問那些硬件……也就是說WINDOWS下直接在USER LEVEL APP訪問IO端口的夢想又泡湯了…
   
看來WINDOWS並沒有更改他們的設計觀念,UMDF也和LINUX下的東西有很大區別。當然,UMDF本身還是有一些優點的,讓某些驅動的編寫容易了很多,不用再工作在內核下,不再受很多限制了。另外一個有點驚訝的是,UMDF居然是基於COM的。當然也沒有用到COM的全部功能,只是用了一點。不過這個也可以看出,COM這個東西還是比較成功的。感覺UMDF這個東西就是一個APP和DEVICE DRIVER的混合體.UMDF可以提供一些設備的符號鏈接,接受其他用戶程序的調用,這點上比較像個驅動。但是在UMDF真正要訪問硬件的時候,它又不能自己幹,還要調用底層的KERNEL DEVICE DRIVER。還要用啥FILE OBJECT之類的。從這個角度看,UMDF也很像一個用戶程序。
  
WDF最早也是跟著VISTA出來的。話說VISTA從技術上說還是不錯的,可惜用戶體驗方面做的差了點,變成了一個失敗的產品。又一次驗證了光有技術是不夠的……


[轉]凝視散記: COM – Microsoft物件導向開發架構的基礎

$
0
0

(轉錄自 凝視、散記 )

COM是Microsoft物件導向架構的基礎,本文主要是重點介紹COM的理論及運作機制。基礎理論包括:COM的概念、應用及定義。運作機制探討COM底層的運作,將提到:COM的介面、識別及呼叫,更進一步探討跨行程及跨機器時如何呼叫。最後很簡要的介紹COM以外另外CORBA及JAVA的做法。

什麼是元件(Component)?

元件是一種有公開的屬性、方法、事件,可以在應用程式中呼叫的二進位檔案。主要的目的是為了在程式開發時可以「重複使用」這些已經編譯好的檔案,以提高開發效率降低成本。此外,因為元件的發行與使用都是二進位檔案,所以在安裝及使用時都是看不到原始碼的,也可以達到保護source code的目的。再就Internet的應用而言,由於元件已經經過編譯,所以在執行的速度上會比ASP指令檔(Script)來得快,因此較佳的執行效能也是使用元件的原因之一。

如果我們將應用程式想成是一件產品;元件就好像是產品的零件。當你完成產品的需求規劃後,不需要自行撰寫所有的程式,只要選取或購買符合需求的元件進行組裝便能完成專案。這種系統開發的哲學就是所謂的CBSE—Component Based System Engineering。CBSE發展應用程式的流程如下:定義應用程式的功能à設計使用者介面à查詢軟體元件目錄尋找符合功能的軟體元件à查詢各軟體元件的介面(interface)à撰寫應用程式組合各軟體元件à輸入各軟體元件的介面值à呼叫各軟體元件à完成系統功能。

什麼是COM (Component Object Model)

每個元件都可視為是一段獨立的副程式,讓應用程式透過Run Time時與元件的相互連結而完成系統功能,便是Microsoft發展COM主要的目的。Microsoft COM的架構也是CBSE的實現,程式開發者只要有軟體元件的清單及清楚定義的介面,便可在應用程式中重複使用這些現成的元件。

1991年Microsoft提出一項新的規格 OLE (Object Linking and Embedding) 。Microsoft當時的OLE 1.0,只提供處理「複合文件」﹙compound document﹚的功能,這種以文件為中心﹙非以應用程式為中心﹚,能夠在單一文件儲存如文字、圖形、視訊與聲音等多重格式資料的規格並未被廣泛接受。到了1993年Microsoft公佈OLE 2.0版的規格,就是COM 架構,包含更多功能,最重要的,COM規範了程式間互動﹙interoperability﹚的方法與介面,融入了封裝、多型等物件導向的觀念。

其實Microsoft大部份的產品都已COM化。如:IE、OutLook、Excel等不僅都是可以獨立使用的軟體,也都是component。只要知道它們提供了哪些 API﹙Application Program Interface﹚,你便可以在程式中把它們當成元件來呼叫。你要做的只是翻翻programmer guide,查出所提供的 method,然後就可以在程式中呼叫它們來完成工作。 而且你寫的軟體也可以成為別人軟體的元件或一項功能(feature) 。在使用元件之前,必須執行「服務註冊」的指令﹙在Dos模式之下執行Regsvr32 myCom.dll﹚,向作業系統「註冊」該元件。然後就可以在有支援 COM/DCOM的開發工具,如:VB, Delphi, Visual FoxPro, C++ Builder, Active Server Page (ASP)中來使用已註冊的元件。

COM的定義

COM是一套規範,規定了製作具有動態交換能力元件的方法。它制定了用戶端程式與及元件間溝通的介面,以達到相互操作﹙Interoperability﹚所應共同遵循的一套標準。讓二進位物件程式的設計、使用以及獨立開發成為可行。事實上,COM並不只是一份規格,它還實作了一組API,叫做COM程式庫,提供所有用戶端程式與元件都會用到的元件管理服務。

COM元件包含以Win32動態連結程式庫( DLL )及以 .EXE形式存在的可執行檔,遵循COM的標準所撰寫出來的元件具備以下幾點的特性:

  • 可以用二進位的格式傳送。
  • 具有語言獨立性:可以使用任何語言撰寫,也可以呼叫任何語言的元件。
  • 是動態連結的形式。
  • 具位置透通性﹙location transparent﹚:用戶端程式可以將遠端機器上的元件當成本地端機器上的元件一樣使用。
  • 與用戶端程式獨立:可以在不干擾舊有用戶端程式的前提下進行新物件的改版。

COM的運作機制

每個COM物件都提供一個以上的介面( Interface ),每個介面都提供一些方法。client端只能透過其介面呼叫COM物件介面內的方法,來使用該物件的服務,而無法直接存取物件內的資料。

COM的介面指的是記憶體中一個包含函式指標陣列的Virtual function table,每一個陣列元素儲存指向元件所實作的函式位址的指標。COM所用來定義介面的標準為IDL—介面定義語言﹙Interface Definition Language﹚。IDL是Open Source Foundation為了在分散式計算環境中使用RPC﹙Remote Procedure Call﹚所制定的。

COM規定所有的介面都必須繼承IUnknown介面﹙註﹚,因此每個COM介面都一定包含:QueryInterface、AddRef與Release等三個函式。

每個COM Object都是屬於特定類別( class )的個體( instance )。我們必須先經由COM程式庫得知道物件的類別,才能開始執行該物件的真正個體。因為client端在使用COM物件介面的方法前,必須先取得該介面的指標﹙Pointer﹚。因此當client端尚未取得此物件所提供的任何介面指標時,會利用COM程式庫中的CoCreateInstance函式﹙註﹚來取得物件的第一個介面指標。當server便開始執行此類別的實作後,client端便可以直接向該物件要求其它所提供的介面的指標,進而使用該物件的任何方法或服務。

每個物件會利用CLSID作為鍵值在Windows的Registry登錄資料庫中記錄元件所在的DLL檔案名稱,以便讓CoCreateInstance利用CLSID作機碼查得檔案的名稱。 Registry是Windows作業系統用來紀錄系統軟、硬體、設定、以及使用者的相關資訊,必要時可以在Registry中新增紀錄或讀取資料。COM的用戶端程式就是利用Registry找出所要使用的元件。

Client端在建立物件透過CoCreateInstance取得該物件第一個介面指標後,就可以呼叫QueryInterface,並傳入該介面的IID,利用IUnknown::QueryInterface,向物件要求取得其他方法所在介面的指標。如果成功的話,就可以使用傳回的指標,便得知物件所提供某個特定介面。

介面一旦公佈就不能再更改了。如果必須異動,只能再定義一個新的介面,新的介面可以繼承原來舊的介面,不過介面的唯一識別碼IID,就是Globally Unique Identifier ( GUID )一定是唯一的,不可以重複。

GUID ( Globally Unique Identifier )是為了讓全球程式設計師所設計的COM物件及介面不必依賴集中管理就能擁有唯一的識別。我們可以藉助Microsoft的提供的工具程式GUIDGEN.EXE或UUIDGEN.EXE來產生唯一的GUID。COM介面的GUID可由IID這個特別資料型別來表示。而Class也有自己的GUID,為了與IID區別,COM另外定義了CLSID這個資料型別,專門用來放置類別的識別碼。

使用COM物件時,客戶端會強制該物件的新個體開始執行。該如何管理物件的執行?而且通常同時會有許多client端程式使用同一物件,物件應該在何時停止執行?

這個問題由IUnknown介面所提供的AddRef以及Release所maintain的 Reference Counter而獲得解決。每個COM物件都有自己的Reference Counter,每當物件傳遞出一個介面的指標時,Reference Counter便會加一。每結束一個使用介面時,必須透過介面指標呼叫Release, Reference Counter便會減一。當物件的Reference Counter等於0的時候,物件本身便會destroy。

COM的類型

每個COM物件的實作部分都位於伺服器( Server )中。Server中含有真正用來實作物件方法的程式碼,並且負責維護物件的資料。COM有三種類型:

  • In-process Server:物件的實作係位於動態連結函式庫中,執行時與client端為於同一process中
  • Local Server:物件的實作位於與客戶端相同的機器中,但是在分開的行程內。
  • Remote Server:物件的實作位於與客戶端不同的機器內所執行的DLL或獨立行程中。跨機器的分散式系統必須借助Distributed COM ( DCOM ) ﹙註﹚的支援才得以完成。DCOM是一個高階的網路協定,建立於COM之上的規格和服務,讓位於不同電腦上的行程內之COM元件可以互相運作

對client端而言,不管物件實作是在哪一種伺服器中,都是透過介面指標,來啟動物件、取得物件介面的指標、叫用方法、釋放指標,沒有任何差別。不過server上的機制便有所不同。

第一種情形:In-process Server:COM物件的實作位於行程中的伺服器﹙如圖一﹚。
client端的介面指標直接指向物件的介面。透過vtable裡的函數指標直接呼叫物件的方法。

圖一

第二種情形:Local Server:COM物件的實作位於本地端的不同行程中﹙如圖二﹚。
由於vtable裡的函數指標無法跨行程,因此client端的介面指標無法直接指到物件的介面,因此會改為指向client端行程內的proxy介面,由proxy介面將存取要求透過RPC轉送給stub。client端將proxy介面是為object而真正的object則將stub視為client。

圖二

第三種情形:Remote Server:COM物件的實作是位於不同機器上﹙如圖三﹚。
在DCOM中所加入支援遠端伺服器的架構,client、proxy及stub間的互動與Local Server類似。

圖三

無論是跨行程或是跨機器的情形,在伺服器與client端中,都需要傳輸參數與資料的標準格式,以及進行溝通。proxy將呼叫的參數包裝為標準格式的動作稱為marshaling,透過RPC傳輸給stub,stub將收的到要求,卸除改為接收行程適用格式的相反動作,則稱為unmarshaling。Proxy與Stub的存在主要就是為了進行marshaling與unmarshaling。

Proxy與Stub的建立只要使用IDL ( Interface Description Language )來撰寫介面,就可以讓MIDL ( Microsoft IDL )編輯器幫我們產生proxy與stub DLL的程式,不需要自己動手撰寫這些程式。

元件開發的不同架構

在Microsoft的架構之下你可以利用VB及VC來開發元件.利用VB的ActiveX DLL來開發元件是最快速的方式。而MTS提供元件的管理以及交易(Transaction)的管理.這種架構在Windows 2000上已經整合為COM+。這也是Microsoft分散式應用系統設計的開發架構。

目前在元件的開發方面發展,有三大陣營 -
DCOM (Distributed Component Object Model)、CORBA (Common Object Request Broker Architecture ) 和 Java RMI (Remote Method Invocation)

Microsoft的 DCOM 就是根據其原本非分散式物件模組 COM 所進化而來的,它已被定為新一代電腦語言的發展基礎。DCOM 其實是二次元層次 (Binary Level) 的分散式物件介面標準。它既可支援 NT 及 Windows 98,此元件更可伸延到Internet之策略,Active X 就是其中的表表者。

CORBA 是由七百多間業內的公司參與之 OMG (Object Management Group) 所制定的標準,其目的是要整合大部份的物件系統。這種物件管理架構 (OMA,Object Management Architecture) 的主要溝通機制,其中物件與物件間之溝通是透過物件訊息仲裁者 (ORB)。而 ORB 是在主從架構間提供一仲裁服務,決定訊息的流向。CORBA 規範介於物件介面的層次 (Interface Level),而物件間就以介面描述語言 (IDL, Interface Definition Language) 來描述。將介面 (Interface) 與實作 (Implementation) 分開,提供多重的繼承架構及動-靜態介面的召喚方式。

Java 是昇陽微系統 (Sun Microsystems) 所提出結合了程式語言和虛擬機器 (Virtual Machine) 的分散式物件環境。與 DCOM 和 CORBA 比較,它不能採用多種不同的程式語言來發展,不過由於其虛擬機器的環境,使 Java 容易地跨平臺運行。

到底什麼是 .NET Framework 呢 ?

$
0
0

微軟有個願景,他幫全世界的網民化個大餅,這個大餅叫做 .NET。
為了這個遠景,所以微軟在 Windows 加上一層平台,因為是要達成 .NET 的遠景,所以取名叫做 .NET Framework。

那這層平台式做什麼的。
PC 什麼都沒裝,只有 BIOS 的運作,所以您可以用組合語言燒在晶片下一些指令教電腦做事。

您也可以裝上 DOS ,這時候可以執行一些 DOS 的指令或 DOS 版的程式。
如果你要執行 Windows 的程式,那您必須在 DOS 上加裝 Windows 系統。
同樣的,如果您要執行 .NET 的程式,那麼您就必須在 Windows 上加裝 .NET Framework。

.NET Framework相當於 J2EE,其中 .NET Framework 裡面有個 CLR (Common Language Runtime),就類似於 Java Virtual Machine。其他的 ADO.NET, ASP.NET, XML…可以想像是 .NET Framework裡的模組,讓您可以更快的完成程式開發。

至於您所說的 VB.NET, ADO.NET…,這些都是 .NET Framework 的一部份。

簡單的說,以前寫 ASP ,您可以使用 ADO 來存取資料庫,在 .NET Framework 有一個更方便更強的存取方式,就是透過 ADO.NET。

以前寫 Windows 上執行的程式用 VB6,如果要寫 .NET Framework 上執行的程式,那就要用 VB.NET。

例外 .NET Framework 是跨語言的,所以 VB, C , C#, Perl…共有 26 種語言可用。

.NET Framework是由Microsoft專為Windows所開發的平台,解決同一程式在不同版本或是不同台電腦的Windows作業系統中的相容性。.NET也提供程式開發工具(Visual Studio),以強大的物件導向功能與多樣化的類別庫彙整多種程式語言,像是C、VB.NET、C++、Python。.NET Framework的概念其實與Oracle的JAVA很相近,彼此也是競爭對手。

然後
可以看這篇文章:
ASP.NET and ASP.NET Core, .NET Framework and .NET core and .NET Standard

參考:
.NET Framework 使用者入門

[轉] 筆記更新 – Windows顯示驅動 (WDDM) 編程初步

$
0
0

Windows顯示驅動(WDDM)編程初步(1)

轉載自【作者:張佩】【原文:http://www.yiiyee.cn/Blog/wddm1/
WDDM Frame

Windows顯示驅動從Vista開始,使用新的WDDM編程框架,稱為Windows Display Driver Model。也有一種最初的名稱是LDDM,L代表Longhorn,但後來微軟在所有產品線上都不再使用Longhorn代號,故而改成現在的名稱。雖然在有些地方還能看到LDDM的說法,但應理解成舊文檔的遺存,不應該做概念上的區分。

WDDM框架是一種典型的小端口(miniport)驅動框架。 NT系統中的所有小端口框架,都是基於WDM框架來實現的,但小端口框架對外提供了更高級的接口,以簡化編程的難度,並提高穩定性。如下圖所示,中間的WDDM是系統提供的編程框架,我們基於這個框架,編寫裡面的小端口驅動,也就是顯示驅動。

顯示驅動類型

現在的顯卡設備,可以按照功能將它分成顯示和計算兩類。大部分的顯卡是用來連接顯示器顯示圖片和動畫用的,也有些顯卡主要確實用來做科學計算用的。顯卡處理器(GPU)對浮點運算有較強的能力,而主機處理器(CPU)處理浮點運算的能力較弱。而在科學計算領域,浮點運算是非常重要的內容,所以工業界就想到利用GPU進行科學運算。

應該說,所有的顯卡都既能夠支持顯示,又能夠支持運算。只是看它偏向哪個方面,為哪個功能做優化罷了。對於偏重計算的顯卡,就不必配置多個顯示接口,圖像處理的模塊就不用很高級;相反,對於圖形功能偏重的顯卡,它就必須要大數據帶寬,大顯存,支持多種類型的接口,能夠實現鋸齒優化等等。

針對我們的驅動來講,如果一個顯示驅動,既支持顯卡的顯示功能,又支持運算功能,稱為全功能驅動(Complete function);如果只支持顯示,不支持運算,就是Display Only驅動;如果只支持運算,不支持顯示功能,就是Render Only驅動。

微軟在Win8的系統上,為所有不同類型的顯卡,編寫了Display Only和Render Only驅動。在未安裝廠商驅動或者廠商驅動被破壞、禁用的情況下,系統會默認選擇使用Display Only驅動來顯示桌面內容。但一般系統不會選擇安裝Render Only驅動,那樣就什麼都看不到了。 Render Only驅動的具體應用場景,我到目前還沒有看到。可能在Render Only的數據服務器顯卡上會被運用。

我的這份顯示驅動初步教材,就是基於微軟公開的Display Only驅動項目KMDOD來寫的。不會涉及數據Render部分。其實可以很方便地把一個Display only的驅動拓展到Complete驅動,在講完所有內容後,會有一小部分內容做介紹。

KMDOD項目可以從MSDN代碼網站上下載到,地址:Windows-driver-samples KMDOD

初始化

如果不更改編譯配置,WDDM驅動的默認啟動函數是DriverEntry。這是驅動對像初始化的地方,一般對於小端口驅動而言,它需要調用框架的初始化函數。 WDDM框架的初始化函數是DxgkInitializeDisplayOnlyDriver。從內核編程好幫手WDK中,可以找到它的聲明:

NTSTATUS DxgkInitializeDisplayOnlyDriver(
  _In_ PDRIVER_OBJECT DriverObject,
  _In_ PUNICODE_STRING RegistryPath,
  _In_ PKMDDOD_INITIALIZATION_DATA KmdDodInitializationData
);

初始化函數會完成驅動對象的初始化,所以前面兩個參數是入口函數的輸入參數。在全文最後的實驗章節中,會介紹如何查看驅動對象,就能夠比較清晰地看到WDDM框架對顯示驅動對象所進行的初始化作業了。此外,初始化函數還要完成顯示驅動相關的初始化。顯示驅動傳入一個函數結構體參數,類型是KMDDOD_INITIALIZATION_DATA,結構體裡麵包含的是顯示驅動向框架提供的一系列回調函數(Callback Function)。框架會在合適的時候調用這些回調函數,完成對應功能。

結構體定義如下:

InitializationData

KMDOD項目沒有實現結構體中列舉的所有回調函數,所以它不能支持WDDM提供的全部和Display相關的功能。比如D3D用戶程序通過DC句柄和顯示驅動進行交互的escape回調函數,這裡就沒有實現。對於沒有實現的回調函數,在結構體中的對應函數指針應被初始化為NULL。

第一個參數Version用來標識你所編寫的顯示驅動使用哪個版本的WDDM。 WDDM一共有四個版本:1.0(Vista & Vista SP1);1.1(Win7);1.2(Win8);1.3(Win Blue)。 KMDOD這個項目中使用的是Win8版本:DXGKDDI_INTERFACE_VERSION_WIN8。 已更新為DXGKDDI_INTERFACE_VERSION_WDDM2_1

為了完成結構體初始化,我們要首先實現這些函數。在具體列舉實現代碼之前,把這些回調函數做一個簡單的分類和介紹是有必要的。

Pnp和power函數

所有現代的物理設備都必須處理Pnp和Power事件。 Pnp事件對應了設備插拔、開始、移除、停止等,以及作為總線設備需要提供的子設備(顯示器)枚舉等;Power事件對應上電、掉電操作,以及查詢設備是否運行進行電源操作。

另外把卸載回調函數也歸入其中。卸載函數在驅動被停止,沒有任何外部模塊引用的時候,系統會嘗試將驅動卸載,這時候卸載回調被調用。

InitialData.DxgkDdiAddDevice = BddDdiAddDevice;
InitialData.DxgkDdiStartDevice = BddDdiStartDevice;
InitialData.DxgkDdiStopDevice = BddDdiStopDevice;
InitialData.DxgkDdiStopDeviceAndReleasePostDisplayOwnership = BddDdiStopDeviceAndReleasePostDisplayOwnership;
InitialData.DxgkDdiResetDevice = BddDdiResetDevice;
InitialData.DxgkDdiRemoveDevice = BddDdiRemoveDevice;
InitialData.DxgkDdiQueryChildRelations = BddDdiQueryChildRelations;
InitialData.DxgkDdiQueryChildStatus = BddDdiQueryChildStatus;
InitialData.DxgkDdiQueryDeviceDescriptor = BddDdiQueryDeviceDescriptor;
InitialData.DxgkDdiSetPowerState = BddDdiSetPowerState;
InitialData.DxgkDdiUnload = BddDdiUnload;
InitialData.DxgkDdiQueryAdapterInfo = BddDdiQueryAdapterInfo;

顯示函數

顯卡驅動的主要功能是配置物理設備,讓它能夠輸出圖片和動畫到外部顯示設備上。和這個功能相關的函數有很多,它包括對鼠標位置的更新,顯示器Mode的枚舉和設置等函數:

    InitialData.DxgkDdiSetPointerPosition = BddDdiSetPointerPosition;
    InitialData.DxgkDdiSetPointerShape = BddDdiSetPointerShape;
    InitialData.DxgkDdiIsSupportedVidPn = BddDdiIsSupportedVidPn;
    InitialData.DxgkDdiRecommendFunctionalVidPn = BddDdiRecommendFunctionalVidPn;
    InitialData.DxgkDdiEnumVidPnCofuncModality = BddDdiEnumVidPnCofuncModality;
    InitialData.DxgkDdiSetVidPnSourceVisibility = BddDdiSetVidPnSourceVisibility;
    InitialData.DxgkDdiCommitVidPn = BddDdiCommitVidPn;
    InitialData.DxgkDdiUpdateActiveVidPnPresentPath = BddDdiUpdateActiveVidPnPresentPath;
    InitialData.DxgkDdiRecommendMonitorModes = BddDdiRecommendMonitorModes;

硬件操作

最後是和物理設備交互的一些函數,首先是中斷處理函數,然後有獲取設備屬性,讀寫設備幀內存、顯示桌面內容(Present)等函數。

InitialData.DxgkDdiDpcRoutine = BddDdiDpcRoutine;
InitialData.DxgkDdiInterruptRoutine = BddDdiInterruptRoutine;
InitialData.DxgkDdiQueryVidPnHWCapability = BddDdiQueryVidPnHWCapability;
InitialData.DxgkDdiPresentDisplayOnly = BddDdiPresentDisplayOnly;
InitialData.DxgkDdiSystemDisplayEnable = BddDdiSystemDisplayEnable;
InitialData.DxgkDdiSystemDisplayWrite = BddDdiSystemDisplayWrite;

功能函數

這部分是顯示驅動作為一個驅動來講,它所實現的一般意義上的功能支持函數。這部分我只列了一個,是用戶程序和內核驅動交互用的IO控制函數。

InitialData.DxgkDdiDispatchIoRequest = BddDdiDispatchIoRequest;

完整的初始化函數:

extern "C"
NTSTATUS
DriverEntry(
    _In_ DRIVER_OBJECT* pDriverObject,
    _In_ UNICODE_STRING* pRegistryPath)
{
    PAGED_CODE();

    // Initialize DDI function pointers and dxgkrnl
    KMDDOD_INITIALIZATION_DATA InitialData = {0};

    InitialData.Version = DXGKDDI_INTERFACE_VERSION_WIN8;

    InitialData.DxgkDdiAddDevice = BddDdiAddDevice;
    //…… 其它的回調函數賦值過程,上面已全部列舉,此處省略

    NTSTATUS Status = DxgkInitializeDisplayOnlyDriver(pDriverObject, pRegistryPath, &amp;InitialData);
    if (!NT_SUCCESS(Status))
    {
        BDD_LOG_ERROR1("DxgkInitializeDisplayOnlyDriver failed with Status: 0x%I64x", Status);
    }

    return Status;
}

可選的擴展

初始化部分到此本來可以結束,繼續講下面的回調函數實現。但其實依然可以擴展一下,不熟悉WDM的讀者可以跳過。針對所有的端口驅動框架都基於WDM來實現的這個事實,如果有的小端口驅動有必要想直接操作IRP的話,應該怎麼實現呢?

其實非常簡單。在DxgkInitializeDisplayOnlyDriver被調用過之後,驅動對象的初始化已經完成了。這時候我們可以對框架的分發函數進行Hook。

比如有一個很重要的功能,很多設備驅動都要做的。就是它希望自己能夠得到系統關機的訊息。通過一般意義上的PNP和Power事件,是沒有辦法得到系統關機訊息的。辦法是註冊自己的IRP_MJ_SHUTDOWN分發函數來接收此訊息。

下面是簡要的實現代碼:

// 下面代碼應在DxgkInitializeDisplayOnlyDriver被調用過後執行
pOldFunc = pDriverObject-&gt;MajorFunction[IRP_MJ_SHUTDOWN]; // 保存框架有可能已實現的函數
pDriverObject-&gt;MajorFunction[IRP_MJ_SHUTDOWN] = BddDdiShutdown;

// 此外還需要在StartDevice函數中調用IoRegisterShutdownNotificatin函數,本文不再繼續演示

NTSTATUS BddDdiShutdown (PDEVICE_OBJECT pDev, PIRP irp)
{
// do something you wanted here

if (pOldFunc)
return pOldFunc (pDev, irp);
else
return STATUS_SUCCESS;
}

Callback function 回調函數實現

KMDOD項目定義了一個顯示驅動類,來封裝和實際功能相關的所有具體操作。這樣一來,大部分回調函數的實現都比較簡單。這個類是BASIC_DISPLAY_DRIVER,我們在第二節會具體地講它。

生命週期

WDDM框架在設計的時候,是能夠支持多個底層物理設備的。換句話說,如果系統中存在多個顯卡設備,WDDM框架都能夠很好地支持它們同時或者分別工作。作為這個支持的一部分,在PNP操作的起始,也就是AddDevice回調函數被調用的時候,框架要求顯示驅動返回一個當前物理設備的Context,作為識別此物理設備的標識。

那麼KMDOD也是可以支持多個顯卡設備的,所以為每個設備創建一個BASIC_DISPLAY_DRIVER對象,作為Context返回給框架。框架在以後調用任何一個回調函數時,都會把這個Context作為其中的一個輸入參數來使用。

WDDM Life

所以我們看,DriverEntry是驅動的開始,卸載函數是驅動的結束;AddDevice函數是設備工作的開始,RemoveDevice是設備結束工作的標識。我們可以用下面的框圖來描述這個概念。

設備的Context作為一個標識物理顯卡的變量,在設備的PNP週期裡面一直運作著。當關機、設備禁用或者其他變故發生的時候,RemoveDevice回調被執行,顯示驅動將負責刪除它所創建的設備Context。

下面是 AddDevice 和 RemoveDevice 這兩個回調函數的實現。

NTSTATUS
BddDdiAddDevice(
    _In_ DEVICE_OBJECT* pPhysicalDeviceObject,
    _Outptr_ PVOID* ppDeviceContext)
{
    PAGED_CODE();

    if ((pPhysicalDeviceObject == NULL) ||
        (ppDeviceContext == NULL))
    {
        BDD_LOG_ERROR2("One of pPhysicalDeviceObject (0x%I64x), ppDeviceContext (0x%I64x) is NULL",
                        pPhysicalDeviceObject, ppDeviceContext);
        return STATUS_INVALID_PARAMETER;
    }
    *ppDeviceContext = NULL;

    BASIC_DISPLAY_DRIVER* pBDD = new(NonPagedPoolNx) BASIC_DISPLAY_DRIVER(pPhysicalDeviceObject);
    if (pBDD == NULL)
    {
        BDD_LOG_LOW_RESOURCE0("pBDD failed to be allocated");
        return STATUS_NO_MEMORY;
    }

    *ppDeviceContext = pBDD;

    return STATUS_SUCCESS;
}

NTSTATUS
BddDdiRemoveDevice(
    _In_ VOID* pDeviceContext)
{
    PAGED_CODE();

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast&lt;BASIC_DISPLAY_DRIVER*&gt;(pDeviceContext);

    if (pBDD)
    {
        delete pBDD;
        pBDD = NULL;
    }

    return STATUS_SUCCESS;
}

其它回調函數實現
其它回調函數的實現,這裡僅僅以StartDevice為例講解。函數原型定義如下:

NTSTATUS DxgkDdiStartDevice(
  _In_ const PVOID MiniportDeviceContext,
  _In_ PDXGK_START_INFO DxgkStartInfo,
  _In_ PDXGKRNL_INTERFACE DxgkInterface,
  _Out_ PULONG NumberOfVideoPresentSources,
  _Out_ PULONG NumberOfChildren
)

第一個參數即設備Context,毫無疑問,它就是我們剛剛在AddDevice中創建的BASIC_DISPLAY_DRIVER對象。所以我們第一步需要獲取對象指針,並且調用到BASIC_DISPLAY_DRIVER裡面的startDevice函數中去。其實現如下:

NTSTATUS
BddDdiStartDevice(
    _In_ VOID* pDeviceContext,
    _In_ DXGK_START_INFO* pDxgkStartInfo,
    _In_ DXGKRNL_INTERFACE* pDxgkInterface,
    _Out_ ULONG* pNumberOfViews,
    _Out_ ULONG* pNumberOfChildren)
{
    PAGED_CODE();
    BDD_ASSERT_CHK(pDeviceContext != NULL);

    BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast&lt;BASIC_DISPLAY_DRIVER*&gt;(pDeviceContext);
    return pBDD-&gt;StartDevice(pDxgkStartInfo, pDxgkInterface, pNumberOfViews, pNumberOfChildren);
}

其它的回調函數,實現方法和StartDevice很類似。唯一的區別是,如果StartDevice調用失敗的話,也就是設備啟動失敗的話,道理上講,很多後續的函數都不應該被調用,因為既然設備沒有啟動,就不應該有任何針對於它的動作存在。所以這些函數被調用的時候,很多都會先確認一下,設備是否處於啟動狀態(IsDriverActive),就是判斷StartDevice是否執行成功。

Windows显示驱动(WDDM)编程初步(2)

轉載自作者:張佩【原文:http://www.yiiyee.cn/Blog/wddm2/

第二部分專門只講VIDPN。這是後面內容的基礎。 WDDM框架用VIDPN這個概念,來描述它所要處理的顯示關係。

1. VIDPN

VIDPN的全稱是Video Present Network,這個因為詞組不太好翻譯(直譯可以是:視頻提交網絡,但頗為難聽),所以一般都直接講它的英文。

VIDPN是WDDM引入的概念,用來描述通過顯卡設備(Adapter)建立的若干個顯示源(Source)和若干個目標接口(Target)之間的關係。系統按照VIDPN所定義的方式,將一個或多個顯示源(Source)通過顯卡設備,輸出到這些目標接口(Target)上。

1.1 基本元素

組成VIDPN網絡的,是下面這樣一些基本元素:

  • 顯示源(Source):這是一系列可提交到顯卡設備,並顯示出來的圖像內容。
  • 源模式(Source Mode):一個源(Source)需要一個模式(Mode)來定義它的屬性,包括長、寬、顏色格式和比特深度(Bit Depth)等。
  • 源模式集合(Source Mode Set):一個源(Source)的所有模式(Mode)的集合(Set),稱為源模式集合。網絡中所有源的模式集合,稱為集合集(Sets)。
  • 目標(Target):一系列物理接口,顯卡設備通過它們來輸出源內容。可以把它們理解成顯卡上的多個接口。雖然不是很準確,但如果你願意,也可以直接把它們理解成接在顯卡設備上的若干個顯示器設備——這里之所以講不是很準確,因為從接口到最終顯示,中間還存在顯示器設備對圖像內容的處理。作為顯示器設備,另有元素Monitor Source Mode Set來標識它所能夠支持的Mode。但KMDOD項目中並沒有用到這個元素,它默認由顯示設備自己處理Target Mode和Monitor Mode間關係。
  • 目標模式(Target Mode):一個目標(Target)也需要一個模式(Mode)來定義它的屬性,它比源更複雜一些,除了長、寬、顏色格式和深度外,還有刷新率、緩衝區長度(Pitch)等。
  • 目標模式集合(Source Mode Set):一個目標(Target)的所有模式(Mode)集合(Set),就是源模式集合。網絡中所有目標的模式集合,稱為集合集(Sets)。
  • 確定的模式(Pinned Mode):在一條Path中,不管Source還是Target,在另一方Mode確定的情況下,選擇出來的能夠和另一方相匹配的一個模式,稱為確定的模式(Pinned Mode)。 Pin是釘子的意思,即表示板上釘釘的Mode。
  • 路徑(Path):把一個源(Source)和一個目標(Target)之間的連接,稱為一個路徑(Path)。 VIDPN網絡中可能存在若干個Path,但至少有一個Path。
  • 拓撲結構(Topology):由若干個Path組成的結構,稱為拓撲結構。

從MSDN文檔中還會看到其它的一些元素,如輸出設備(也就是顯示設備或Monitor)、輸出Codec(用作輸出信號或格式轉換)等,但上面這些卻是組成VIDPN最基本和重要的元素。 KMDOD項目中也並未用到此外的其它元素。

1.2 拓撲結構

下面介紹幾個VIDPN網絡拓撲結構的例子,都取自MSDN文檔。

1.2.1 示例1

下面列幾張VIDPN的拓撲圖,解釋其中所包含的上述基本元素。

在上面的這個VIDPN的拓撲結構中,它有兩個源(source1、source2)和三個目標(DVI、HD15、S-Video)。這是一種老式顯卡,可以看到接口類型都比較舊。它提供了三種輸出頭,但只能支持兩個輸出源。這樣一個源(Source1)輸出到DVI口,另外一個源(Source2)以克隆模式(Clone Mode)同時輸出到HD15和S-Video兩個口上。兩個源之間則是擴展模式(External Mode)。

Path1: Source 1 -> DVI
Path2: Source 2 -> HD15
Path3: Source 2 -> S-video

三條Path組成了這個VIDPN的拓撲結構。每個Source 和Target都各有它們的mode 和mode set。在最後確定的時候,它們各自的pinned mode將被運用。

1.2.2 示例2

示例1中講到一個VIDPN,兩個Source對應到三個Target。那麼Source和Target的數量各由什麼決定的呢?從下面的圖中可以清楚看到,Source的數量是由顯卡內部的Codec決定的。

顯卡內部因為只有兩個Codec,所以它僅能提供兩條Surface處理的管線(PipeLine)。這和顯卡本身的處理能力有關係。但是一個被處理過的Surface卻可以被輕易地輸出到多個Target,這一步僅僅需要少量的硬件連線就可以實現了。所以Target的數量,取決於顯卡自己的設計和定位,提供比Source數量更多的Target頭也是可以的。

1.3 元素關係

MSDN用下圖解釋了這麼多VIDPN元素之間的對應關係。

這是一張一對多和多對一的結構圖。理解它們的關係很重要。總結而言,可把上面的四種關係翻譯成下面的文字:

一個VIDPN,包含一個拓撲結構;一個拓撲結構包含若干條Path;每個Path中,包含一個Target和一個Source;一個Target只能在唯一的Path中;但一個Source可以存在於若干個Path中(Clone模式,見示例1)。
一個VIDPN包含多個Source Mode Set;一個Source Mode Set由多個Source Mode組成。
一個VIDPN包含多個Target Mode Set;一個Target Mode Set由多個Target Mode組成。
一個VIDPN包含多個Monitor source mode set;一個Monitor source mode set由多個Monitor source mode組成。

1.4 框架接口

前文我們介紹了框架和小端口之間的關係,框架對小端口提供了強有力的支持,勝過再造的力量。這個再造的力量,源自於框架向小端口所提供的各種接口。所謂接口,其實就是框架業已實現的種種功能,而以函數形式提供給小端口驅動使用。形式上,它們被封裝在若干個不同的數據結構中。

框架為小端口驅動提供的各種接口,其組織上,基本是根據VIDPN的各主要元素來劃分的,下面會看到。我們會對這些接口做簡單介紹,並列出接口的詳細定義。

1.4.1 框架回調接口

框架回調接口是唯一由WDDM框架直接向小端口驅動提供的接口。其它接口都是直接或間接地,可以通過框架回調接口獲取到。之所以把這個接口稱為框架回調接口,因為它提供的所有函數名稱,都依照DxgkCbXXX這樣的格式命名。 Cb是Callback的縮寫。在WDDM的語境中,我們把這個接口中的所有函數,稱為由框架提供的回調函數。

這個接口由DDI函數DxgkDdiStartDevice在調用的時候,作為參數傳入。它的定義如下:

typedef struct _DXGKRNL_INTERFACE {
  ULONG                                 Size;
  ULONG                                 Version;
  HANDLE                                DeviceHandle;
  DXGKCB_EVAL_ACPI_METHOD               DxgkCbEvalAcpiMethod;
  DXGKCB_GET_DEVICE_INFORMATION         DxgkCbGetDeviceInformation;
  DXGKCB_INDICATE_CHILD_STATUS          DxgkCbIndicateChildStatus;
  DXGKCB_MAP_MEMORY                     DxgkCbMapMemory;
  DXGKCB_QUEUE_DPC                      DxgkCbQueueDpc;
  DXGKCB_QUERY_SERVICES                 DxgkCbQueryServices;
  DXGKCB_READ_DEVICE_SPACE              DxgkCbReadDeviceSpace;
  DXGKCB_SYNCHRONIZE_EXECUTION          DxgkCbSynchronizeExecution;
  DXGKCB_UNMAP_MEMORY                   DxgkCbUnmapMemory;
  DXGKCB_WRITE_DEVICE_SPACE             DxgkCbWriteDeviceSpace;
  DXGKCB_IS_DEVICE_PRESENT              DxgkCbIsDevicePresent;
  DXGKCB_GETHANDLEDATA                  DxgkCbGetHandleData;
  DXGKCB_GETHANDLEPARENT                DxgkCbGetHandleParent;
  DXGKCB_ENUMHANDLECHILDREN             DxgkCbEnumHandleChildren;
  DXGKCB_NOTIFY_INTERRUPT               DxgkCbNotifyInterrupt;
  DXGKCB_NOTIFY_DPC                     DxgkCbNotifyDpc;
  DXGKCB_QUERYVIDPNINTERFACE            DxgkCbQueryVidPnInterface;
  DXGKCB_QUERYMONITORINTERFACE          DxgkCbQueryMonitorInterface;
  DXGKCB_GETCAPTUREADDRESS              DxgkCbGetCaptureAddress;
  DXGKCB_LOG_ETW_EVENT                  DxgkCbLogEtwEvent;
  DXGKCB_EXCLUDE_ADAPTER_ACCESS         DxgkCbExcludeAdapterAccess;
#if DXGKDDI_INTERFACE_VERSION &gt;= DXGKDDI_INTERFACE_VERSION_WIN8)
  DXGKCB_CREATECONTEXTALLOCATION        DxgkCbCreateContextAllocation;
  DXGKCB_DESTROYCONTEXTALLOCATION       DxgkCbDestroyContextAllocation;
  DXGKCB_SETPOWERCOMPONENTACTIVE        DxgkCbSetPowerComponentActive;
  DXGKCB_SETPOWERCOMPONENTIDLE          DxgkCbSetPowerComponentIdle;
  DXGKCB_ACQUIRE_POST_DISPLAY_OWNERSHIP DxgkCbAcquirePostDisplayOwnership;
  DXGKCB_POWERRUNTIMECONTROLREQUEST     DxgkCbPowerRuntimeControlRequest;
  DXGKCB_SETPOWERCOMPONENTLATENCY       DxgkCbSetPowerComponentLatency;
  DXGKCB_SETPOWERCOMPONENTRESIDENCY     DxgkCbSetPowerComponentResidency;
#endif 
} DXGKRNL_INTERFACE, *PDXGKRNL_INTERFACE;

1.4.2 VIDPN接口

這個接口用來管理VIDPN相關的操作。可通過框架回調接口中的DxgkCbQueryVidPnInterface獲取到,示例代碼如下:

// 函數:IsSupportedVidPn
CONST DXGK_VIDPN_INTERFACE* pVidPnInterface;
NTSTATUS Status = m_DxgkInterface.DxgkCbQueryVidPnInterface(
                    pIsSupportedVidPn-&gt;hDesiredVidPn, //
                    DXGK_VIDPN_INTERFACE_VERSION_V1,
                    pVidPnInterface);

結構體定義如下:

typedef struct _DXGK_VIDPN_INTERFACE {
  DXGK_VIDPN_INTERFACE_VERSION Version;
  DXGKDDI_VIDPN_GETTOPOLOGY pfnGetTopology;
  DXGKDDI_VIDPN_ACQUIRESOURCEMODESET pfnAcquireSourceModeSet;
  DXGKDDI_VIDPN_RELEASESOURCEMODESET pfnReleaseSourceModeSet;
  DXGKDDI_VIDPN_CREATENEWSOURCEMODESET pfnCreateNewSourceModeSet;
  DXGKDDI_VIDPN_ASSIGNSOURCEMODESET pfnAssignSourceModeSet;
  DXGKDDI_VIDPN_ASSIGNMULTISAMPLINGMETHODSET pfnAssignMultisamplingMethodSet;
  DXGKDDI_VIDPN_ACQUIRETARGETMODESET pfnAcquireTargetModeSet;
  DXGKDDI_VIDPN_RELEASETARGETMODESET pfnReleaseTargetModeSet;
  DXGKDDI_VIDPN_CREATENEWTARGETMODESET pfnCreateNewTargetModeSet;
  DXGKDDI_VIDPN_ASSIGNTARGETMODESET pfnAssignTargetModeSet;
} DXGK_VIDPN_INTERFACE;

1.4.3 Topology接口

這個接口用來管理VIDPN的拓撲結構。可以通過調用VIDPN接口的pfnGetTopology函數得到。示例代碼如下:

    // 函數:IsSupportedVidPn
    D3DKMDT_HVIDPNTOPOLOGY hVidPnTopology;
    CONST DXGK_VIDPNTOPOLOGY_INTERFACE* pVidPnTopologyInterface;
    Status = pVidPnInterface-&gt;pfnGetTopology(
                pIsSupportedVidPn-&gt;hDesiredVidPn,
                &amp;hVidPnTopology,
                &amp;pVidPnTopologyInterface);

結構體定義如下:

typedef struct _DXGK_VIDPNTOPOLOGY_INTERFACE {
  DXGKDDI_VIDPNTOPOLOGY_GETNUMPATHS pfnGetNumPaths;
  DXGKDDI_VIDPNTOPOLOGY_GETNUMPATHSFROMSOURCE pfnGetNumPathsFromSource;
  DXGKDDI_VIDPNTOPOLOGY_ENUMPATHTARGETSFROMSOURCE pfnEnumPathTargetsFromSource;
  DXGKDDI_VIDPNTOPOLOGY_GETPATHSOURCEFROMTARGET pfnGetPathSourceFromTarget;
  DXGKDDI_VIDPNTOPOLOGY_ACQUIREPATHINFO pfnAcquirePathInfo;
  DXGKDDI_VIDPNTOPOLOGY_ACQUIREFIRSTPATHINFO pfnAcquireFirstPathInfo;
  DXGKDDI_VIDPNTOPOLOGY_ACQUIRENEXTPATHINFO pfnAcquireNextPathInfo;
  DXGKDDI_VIDPNTOPOLOGY_UPDATEPATHSUPPORTINFO pfnUpdatePathSupportInfo;
  DXGKDDI_VIDPNTOPOLOGY_RELEASEPATHINFO pfnReleasePathInfo;
  DXGKDDI_VIDPNTOPOLOGY_CREATENEWPATHINFO pfnCreateNewPathInfo;
  DXGKDDI_VIDPNTOPOLOGY_ADDPATH pfnAddPath;
  DXGKDDI_VIDPNTOPOLOGY_REMOVEPATH pfnRemovePath;
} DXGK_VIDPNTOPOLOGY_INTERFACE;

1.4.4 Source Mode Set接口

這個接口用來管理VIDPN中的Source Mode Set。可以通過調用VIDPN接口的pfnAcquireSourceModeSet函數得到。示例代碼如下:

//函數:CommitVidPn
CONST DXGK_VIDPNSOURCEMODESET_INTERFACE* pVidPnSourceModeSetInterface = NULL;
Status = pVidPnInterface-&gt;pfnAcquireSourceModeSet(
pCommitVidPn-&gt;hFunctionalVidPn,
       pCommitVidPn-&gt;AffectedVidPnSourceId,
       &amp;hVidPnSourceModeSet,
       &amp;pVidPnSourceModeSetInterface);

結構體定義如下:

typedef struct _DXGK_VIDPNSOURCEMODESET_INTERFACE {
  DXGKDDI_VIDPNSOURCEMODESET_GETNUMMODES pfnGetNumModes;
  DXGKDDI_VIDPNSOURCEMODESET_ACQUIREFIRSTMODEINFO pfnAcquireFirstModeInfo;
  DXGKDDI_VIDPNSOURCEMODESET_ACQUIRENEXTMODEINFO pfnAcquireNextModeInfo;
  DXGKDDI_VIDPNSOURCEMODESET_ACQUIREPINNEDMODEINFO pfnAcquirePinnedModeInfo;
  DXGKDDI_VIDPNSOURCEMODESET_RELEASEMODEINFO pfnReleaseModeInfo;
  DXGKDDI_VIDPNSOURCEMODESET_CREATENEWMODEINFO pfnCreateNewModeInfo;
  DXGKDDI_VIDPNSOURCEMODESET_ADDMODE pfnAddMode;
  DXGKDDI_VIDPNSOURCEMODESET_PINMODE pfnPinMode;
} DXGK_VIDPNSOURCEMODESET_INTERFACE;

1.4.5 Target Mode Set 接口

這個接口用來管理VIDPN中的Target Mode Set。可以通過調用VIDPN接口的pfnAcquireTargetModeSet函數得到。示例代碼如下:

// 函數:EnumVidPnCofuncModality
CONST DXGK_VIDPNTARGETMODESET_INTERFACE* pVidPnTargetModeSetInterface = NULL;
Status = pVidPnInterface-&gt;pfnAcquireTargetModeSet(
                   pEnumCofuncModality-&gt;hConstrainingVidPn,
                   pVidPnPresentPath-&gt;VidPnTargetId,
                   &amp;hVidPnTargetModeSet,
                   &amp;pVidPnTargetModeSetInterface)

結構體定義如下:

typedef struct _DXGK_VIDPNTARGETMODESET_INTERFACE {
  DXGKDDI_VIDPNTARGETMODESET_GETNUMMODES pfnGetNumModes;
  DXGKDDI_VIDPNTARGETMODESET_ACQUIREFIRSTMODEINFO pfnAcquireFirstModeInfo;
  DXGKDDI_VIDPNTARGETMODESET_ACQUIRENEXTMODEINFO pfnAcquireNextModeInfo;
  DXGKDDI_VIDPNTARGETMODESET_ACQUIREPINNEDMODEINFO pfnAcquirePinnedModeInfo;
  DXGKDDI_VIDPNTARGETMODESET_RELEASEMODEINFO pfnReleaseModeInfo;
  DXGKDDI_VIDPNTARGETMODESET_CREATENEWMODEINFO pfnCreateNewModeInfo;
  DXGKDDI_VIDPNTARGETMODESET_ADDMODE pfnAddMode;
  DXGKDDI_VIDPNTARGETMODESET_PINMODE pfnPinMode;
} DXGK_VIDPNTARGETMODESET_INTERFACE;

1.4.6 Monitor接口

除了上面這些由框架提供的常用接口外,還有一些未曾在KMDOD項目中用到的接口:

Monitor接口:DXGK_MONITOR_INTERFACE

這個接口可通過框架回調接口中的 DxgkCbQueryMonitorInterface 回調函數獲取。通過這個接口,可以獲取Monitor的Monitor Source Mode Set接口和刷新率範圍。它有兩個版本,版本2中增加了函數,用來獲取Windows操作系統為Monitor增加的必須支持的Mode。

Monitor Source Mode Set接口:DXGK_MONITORSOURCEMODESET_INTERFACE

這個接口可通過Monitor接口中的接口函數pfnAcquireMonitorSourceModeSet獲取,它提供了操作顯示器Mode相關的函數。

關於 Linear Aperture-Space Segments

$
0
0

轉錄自 小小paul

線性aperture空間段類似於線性內存空間段.但是線性aperture段只是一段虛擬地址空間而不能真正保存數據。爲了保存數據必須分配系統內存頁,而這段虛擬地址空間也必須被重定向到這些頁上。小端口驅動必須實現 DxgkDdiBuildPagingBuffer中的DXGK_OPERATION_MAP_APERTURE_SEGMENT 和 DXGK_OPERATION_UNMAP_APERTURE_SEGMENT操作來處理地址的重定向而且必須按照DriverEntry of Display Miniport Driver描述的方式暴露這個函數。DxgkDdiBuildPagingBuffer會收到需要被重定向的地址範圍和被分配的系統物理內存頁所引用的MDL。

顯卡小驅動程序通常通過編程一個頁表來完成地址空間範圍的重定向,而視頻內存管理器並不知道這個頁表。

驅動程序必須在DXGK_SEGMENTDESCRIPTOR結構的Flags成員中設置Aperture位域標誌來指定一個線性Aperture空間段。驅動程序還可以設置以下位域標誌,以表明額外的段支持:

CpuVisible表示這個段CPU可以訪問
CacheCoherent表示段與段重定向到的頁面保持CPU緩存一致性。
下圖顯示了線性Aperture空間段的可視化表示。

oversea remote debug 遠端除錯 怎麼做呢? Windows

$
0
0

通常有兩種方式建立兩地的遠端Debug, 第一種是 teamviewer, 第二種windbg

第一種 teamviewer

就是透過網路的方式可以讓你看到另一台電腦的螢幕, 並且能夠操作那台電腦, 但缺點是如果你的issue必須重新開機或是該issue跟網路與顯示有關的話, 通常連線就可能中斷, 無法繼續分析下去!

這時候就是透過第二種方式吧, windbg remote debugging

微軟其實也有寫得很詳細, 大致上就是建立如下圖的環境

解釋一下,
Debugging Server負責處理整個debug活動順利進行的平台: Host Computer
Debugging Client負責從遠端來控制debug session進行: Remote Computer
為了建立遠端的除錯, 第一個要設定好 debugging server, 然後才讓Client從遠端啟動.

這個地方要特別說一下, Target就是運行著要被除錯程式碼的電腦!
Target與 Host Computer 有時可以是同一台電腦, 有時候必須是分開的兩部電腦, 主要取決於你要除厝的程式碼是kernel mode還是user mode, 講到這裡大家應該可猜出答案, 對啦! 如果是kernel mode系統可能會隨時掛掉, 因此建議要分兩台電腦, 如果是user mode的debugging, 是可以勉強在同一台電腦OK的

介於兩端的連線協定, 有多種選擇: TCP, NPIPE, SPIPE, SSL,或是 COM Port.
假設我透過TCP連線, 並且使用winDbg軟體在 debugging Server/Client兩台電腦上, 作遠端kernel-mode除錯, 可以參考以下步驟:

1. 在 Host電腦, 開啟 windbg 並建立與Target之間的 kernel-mode debugging session

兩種方式:
#1. WinDbg Menu:
如果Windbg處於休眠狀態你可以選單File下拉, 選 Kernel Debug (或直接快捷鍵CTRL+K), 然後當 Kernel Debugging 對話窗出現時, 點選你建立的連線方式: NET, 1394, USB, COM, or Local.

#2. Command Prompt:
如果透過指令介面, 啟動WinDbg後輸入以下指令:

  • windbg [-y SymbolPath] -k net:port=PortNumber,key=Key[,target=TargetIPAddress|TargetMachineName]
  • windbg [-y SymbolPath] -k 1394:channel=1394Channel[,symlink=1394Protocol]
  • windbg [-y SymbolPath] -k usb:targetname=USBString
  • windbg [-y SymbolPath] -k com:port=ComPort,baud=BaudRate
  • windbg [-y SymbolPath] -k com:pipe,port=\\VMHost\pipe\PipeName[,resets=0][,reconnect]
  • windbg [-y SymbolPath] -k com:modem
  • windbg [-y SymbolPath] -kl
  • windbg [-y SymbolPath] -k

2. 暫時中斷,

從Debug選單選取Break, 或直接按 CTRL-Break.

3. 在視窗 Debugger Command Window, 輸入指令做連線


指令:
.server tcp:port=5005
(這個 port number 5005 可以根據你的狀況改變)

4. WinDbg將會回應如下面訊息

dbgcmd

Server started.  Client can connect with any of these command lines
0: <debugger> -remote tcp:Port=5005,Server=YourHostComputer

5. 到了遠端電腦(remote computer)

開啟 WinDbg, 從選單File下拉, 選取 Connect to Remote Session

6. 在 Connection String 底下,

輸入
tcp:Port=5005,Server=YourHostComputer
這裡的 YourHostComputer 就必須視HOST電腦的名稱(debugging server).
再按 OK

Windbg 指令與分析之教學筆記

$
0
0

微軟說明windbg:
https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/

影片教學: WinDbg Basics for Malware Analysis

影片教學: Windows Kernel Programming Tutorials for Beginners

關於symbol
WinDbg 設定 symbol file path 的四種方法
https://blog.yowko.com/windbg-symbol-file-path/
使用SymChk獲取符號文件
https://www.twblogs.net/a/5b8145992b71772165abcb1a

關於PDB file
https://blog.yowko.com/about-pdb/

關於breakpoint
https://blog.csdn.net/wingeek/article/details/4025475

windbg記憶體斷點學習總結
https://www.itread01.com/content/1549593020.html
https://www.itread01.com/content/1548387551.html

Windbg專欄部落格
https://blog.csdn.net/hgy413/category_1143065.html
windbg-!dh、!lmi(显示映像的头部)
https://blog.csdn.net/hgy413/article/details/9332005
WinDbg 命令三部曲:(一)WinDbg 命令手册
https://www.cnblogs.com/gaochundong/p/windbg_cheat_sheet.html

BSOD情況分析
https://www.tonylin.idv.tw/dokuwiki/doku.php/cpp:wdm:generic:trace_bsod_with_windbg

如何進安全模式
幾種 Windows 10 進入安全模式的方法
因為有時候無法開機的原因,可能是系統中的某些驅動程式出了問題,或是在載入某些開機時要一起載入的程式,導致無法開機。而當我們選用安全模式啟動時,僅載入開機時最必要的檔案,其它多餘的檔案、驅動程式一律都不載入,因此如果無法進入Windows桌面是這方面問題所導致的,那麼用安全模式就應該可以順利排除問題,讓你可以進入Windows系統桌面。

小軟體BlueScreenview
http://www.nirsoft.net/utils/blue_screen_view.html

小軟體 Windows Repair
小軟體 CCleaner

SysWow64檔夾,是64位Windows用來存放32位元Windows系統檔的地方。
https://blog.xuite.net/auster.lai/twblog/123089423-%E4%BB%80%E9%BA%BC%E6%98%AFSysWow64

win10的快速啟動是什麼
https://kknews.cc/zh-tw/digital/qoax3mb.html

9招解決 Windows 系統問題或故障,不用靠別人、自己來搞定
https://www.techbang.com/posts/10091-9-tips-to-self-resolve-windows-system-failure-my-own-system-doctor-pchome-196-special-feature

ACPI指令
https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/acpi-debugging

微軟教學: Getting Started with WinDbg (Kernel-Mode)
https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/getting-started-with-windbg–kernel-mode-

指令k
Show stacks

  • ~*k: List all threads’ stacks
  • ~*kv: List all threads’s stacks with 3 function arguments
  • k: List current thread’s stack
  • kv: List current thread’s stack with 3 function arguments
  • kp: List current thread’s stack with all function arguments (need symbol)

https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/k–kb–kc–kd–kp–kp–kv–display-stack-backtrace-
 

指令!address
https://blog.csdn.net/hgy413/article/details/7562746

指令findstack
!findstack擴展查找包含指定符號或模塊的所有stack
https://docs.microsoft.com/zh-hk/windows-hardware/drivers/debugger/-findstack

微軟說明:debugger commands
https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/commands
指令 lm
List Loaded Modules
指令 ld
Load Symbols
指令 x
x [Options] Module!Symbol 模式匹配符号信息
x *! 列出所有模块
x ntdll!* 列出 ntdll 模块

指令dt (Display Type)
dt ntdll!*IMAGE* :查找有什麼Headers
以下例子是 notepad在memory查找出PE header的位址, 然後透過dt指令就可以查到定義的結構內容:

教學 Remote debugging

微軟教學影片 Videos: Debugging KMDF Drivers
https://docs.microsoft.com/zh-tw/windows-hardware/drivers/wdf/debugging-kernel-mode-driver-framework-drivers
1)
!load wdfkd.dll
!chain
!wdfhelp
!wdflogdump
!wdfldr
!wdftmffile

先說一些有用的工具:Useful tools

  • Process explorer: Process Explorer是一個類似Windows「工作管理員」的程式,主要功能就是列出目前電腦中正在運行的全部程式以及跟運行中程式相關的全部詳細資料,讓我們可以在簡單的圖表與清單中,找出哪些程式佔用最多CPU資源、哪些執行序用掉最多記憶體以及其他更深入的資訊,方便對整個Windows系統做更完整的監控與管理。

    Process Explorer算是一個進階版的「工作管理員」,它可以列出比原本工作管理員還更多、更詳細的各式資料,包含執行檔的圖示、完整的影像與程式所在路徑、記憶統計、權限與安全性屬性…等,更可針對單一處理程序列出使用中的DLL檔,或透過搜尋的方式找出你要的DLL或相關資源,對於程式開發者或電腦管理原來說,應該會是個很方便的實用工具。

  • Process dump: Procdump是一個輕量級的Sysinternal團隊開發的命令行工具, 它的主要目的是監控應用程序的CPU異常動向, 並在此異常時生成crash dump文件, 供研發人員和管理員確定問題發生的原因.你還可以把它作為生成dump的工具使用在其他的腳本中.

    ProcDump與Procexp一樣是Windows平台上的內部調試工具,Procexp採用圖形界面體現系統整體及個別進程的性能信息,而ProcDump則與我們在Unix平台上使用的性能分析工具類似使用CLI命令行界面。

  • LiveKD:使用Microsoft內核調試器檢查系統。
  • NotMyFault:NotMyFault 是一款用來檢測臭蟲(Bug)並除錯的工具,由微軟公司的 Sysinternals 小組發佈。允許使用者主動以多種方式來損毀、當機或凍結系統。使用者可以透過當機後產生的傾印檔案轉存映射來分析 bug 臭蟲。另外,NotMyFault 還能更改系統當機時的預設顏色,使之不再侷限於「藍底白字(BSOD)」。
    警告:極易造成資料遺失。不建議將此工具用於在有生產用途的電腦上作檢測、除錯。使用本工具有著極大的風險,欲使用嘗試者請自行承擔風險。

    NotMyFault 使用系統驅動程式(myfault.sys – 命名是因為該驅動程式是損毀/當機的實際罪魁禍首)在記憶體中四處胡鬧,並做您命令它做的其它壞事。它也可以用於導致系統當機(所以您可以練習除錯問題)或造成重大的記憶體流失。

 

Windbg basic operations

  • Open crash dump with File > Open crash dump
  • Load symbols
  • .symfix C:\symbols: Set local symbol folder
  • .reload: Reload symbols
  • .sympath: Show current symbol path

 

List threads

  • ~: List all threads

 

Switch thread

  • ~0s: Change to thread 0
  • ~1s: Change to thread 1

 

Show environment

  • lm: List modules
  • lmv: List module versions
  • lmvm: List specific module versions

 

Crash dump automatic analysis

  • !analyze –v: Analyze a crash
  • !analyze –v –hang: Analyze a hang

 

Show exception context

  • Get context address from kv
  • .cxr <address>
    • If there is “???” shown in the result of .cxr, it indicates an invalid address access or memory corruption

 

Disassemble code

  • u: Disassemble code at the address
  • ub: Disassemble code before that address

 

CPU high analysis

  • !runaway 7: Check thread used CPU time and total elapsed time
    • If thread CPU used time occupies a large portion of total execution time, it might indicate a CPU high symptom.

 

Analyze deadlock

  • !analyze –v –hang
    • However, it might not give correct result
  • !locks
    • Show current locks
  • !cs –s –l –o
    • Show critical section owner
  • ub <return address for EnterCriticalSection line>
    • Show the critical section it is going to enter

 

Analyze Stack overflow (recursive)

  • ~*k
    • Shows a lot of same functions in call stack
  • !teb
    • Show stack base and limit
  • dps <stack limit> <stack base>
    • Show stack range content

 

Analyze .NET framework process crash

  • There is “mscorwks” in call stack
  • Show .NET version
    • lmvm mscorwks
  • Load sos extension dll
    • .load C:\Windows\Microsoft.NET\<version>\SOS.dll
    • .unload sos
  • Print .NET exception with sos extension dll
    • !pe

 

Analyze WoW64 process

  • There is “wow64” in call stack
  • Show current machine type
    • .effmach
  • Set machine type to x86
    • .effmach x86
  • !sw can switch between x86 and x64
  • .load wow64exts
    • Then we can see x86 call stacks
  • To see MessageBox parameters
    • du <argument address>

 

Analyze memory leak

  • Turn on gflags by executing Windows Kits > Global Flags > System Registry > Create user mode stack trace database
  • Show current gflag
    • !gflag
  • Show heaps
    • !heap –s
    • !heap –k –h <heap id>

 

Analyze infinite wait

  • Analyze with !analyze
    • !analyze –v –hang
  • Show WaitForMultipleObjects arguments
    • 1st argument = number of handles
    • 2nd argument = handle array
    • dp <2nd argument address>
  • Show each handle
    • !handle <handle address> f

 

Analyze kernel dump

  • List current process
    • !process
  • List all processes
    • !process 0 0
    • !process 0 7: with call stack
  • Get IRP list from output of !process <process address>
  • Show IRP
    • !irp <irp address>
  • Get device object from “Device” in IRP list, then show its info
    • !devobj <device obj address>
  • Get file object from “File” in IRP list, then show its info
    • !fileobj <file obj address>
  • Show thread info
    • !thread
    • !thread <thread address>
  • Change thread
    • .thread <thread address>
  • Show page table entry
    • !pte <address>
  • Check if is running on VM
    • lmvm vmmemctl: for VMware

 

Analyze kernel dump for memory problem

  • Show memory
    • !vm
  • If NonPagedPool Usage is close to NonPagedPool Max, or if PagedPool Usage is closed to PagedPool Max, there could be memory stress problem.
    • !poolused 2: non-paged memory
    • !poolused 4: paged memory
  • Show running processors and threads
    • !running
    • !running –i –t: include idle processor, and show call stack
  • Show system info
    • !sysinfo cpuinfo
    • !sysinfo

 

Analyze complete kernel dump

  • There is “Kernel Complete Dump File: Full address space is available” in the output of windbg
  • Change process
    • .process /r /P <process address>
  • Change thread
    • .thread <thread address>

Window 10 如何開機進Safe mode, Uefi, WinPE ? 什麼是WinRE?

$
0
0

首先要知道這個東西, Windows 修復環境 (Windows RE)
此畫面就是WinRE的環境, 常用於開機異常或開機進階選項, 微軟說明進入WinRE方法

WinRE為Vista版本開始有的修復功能, 官方說明: 「Windows 修復環境」 (WinRE) 是一種復原環境, 可以修復無法開機之作業系統的常見原因。 WinRE 是以 Windows 預先安裝環境 (Windows PE) 為基礎, 並可使用其他驅動程式、語言、Windows PE 選用元件和其他疑難排解和診斷工具進行自訂。

如何進winPE

其實微軟的邏輯還是一樣, 就是WinPE是一套小型的作業系統, 用以安裝與修復等功能
先進入WinRE之後, 選擇命令提示字元(command prompt)
會跳出X:\ 的partition的 command line 就是我們熟悉的WinPE介面囉!

指令WinPE裡關機: wpeutil shutdown
指令WinPE裡 重新啟動: wpeutil reboot

如何進safe mode安全模式

微軟Windows 2000/XP/Vista/7的安全模式可以透過在啟動時不斷按F8鍵進入。Windows 8以後的Windows作業系統預設無法通過F8進入安全模式。
方法一: 先進WinRE的畫面, 然後選 Startup settings

會看到這個畫面

重新開機之後,會看到選項清單。
選取 4 或按 F4 以在 [安全模式] 中啟動電腦。
選取 5 或按 F5 來使用 [安全模式 (含網路功能)]。

方法二: 透過 msconfig設定
使用這個方法要注意,是固定性的修改、而不是一次性的,所以如果沒有改回來,就會變成每次開機都是安全模式。

方法三: 透過 bcdedit 設定
使用這個方法要注意,是固定性的修改、而不是一次性的,所以如果沒有改回來,就會變成每次開機都是安全模式。
啟用安全模式 – 輸入:
bcdedit /set {default} safeboot minimal
啟用安全模式含網路功能 – 輸入:
bcdedit /set {default} safeboot network
關閉安全模式 – 輸入:
bcdedit /deletevalue {default} safeboot


EFI/UEFI Shell 的常用命令列表 Command list

$
0
0

早期Legacy BIOS會在DOS環境下, 透過指令方式, 或集結成批次檔 *.bat 以完成特定的自動化需求; 現在推動UEFI環境, 這是Built-in的 EFI shell, 要自動化也是需要批次檔 *nsh

當系統開機到 EFI shell時, 此時嵌入式作業系統會透過搜尋已定義好的path 找尋此檔 startup.nsh, 所以基本上來說Startup.nsh相當於DOS/Windows環境下的autoexec.bat,當跑完這個startup script之後, 會顯示如下的指令提示介面: (翻譯引用自Intel文件:Basic Instructions for Using the Extensible Firmware Interface (EFI))
Shell>

下表引用自UEFI和Legacy BIOS的差異

Legacy BIOS UEFI
發明年代 1975 by IBM 2002 by Intel
程式語言 組合語言 C語言
CPU mode 16-bit mode(Real mode) 32/64-bit mode(Flat mode)
記憶體定址空間 ~1 MB >4 GB
支援硬碟分割格式 MBR GPT
支援硬碟最大容量 ~2 TB ~9 ZetaBytes
支援硬碟主分割區數量 4 128
Boot Speed
Boot Manager Boot loader in MBR Boot loaders in ESP
硬體擴充方式 Option ROM EFI driver
硬體資源控制 Interrupt Protocol/Driver
安全機制 TPM Secure Boot
VGA BIOS VBIOS GOP
Bootable USB  Flash drive DOS EFI Shell

統一可延伸韌體介面(Unified Extensible Firmware Interface, UEFI)是一種個人電腦系統規格,用來定義作業系統與系統韌體之間的軟體介面,作為BIOS的替代方案。可延伸韌體介面負責加電自檢(POST)、連繫作業系統以及提供連接作業系統與硬體的介面。(參考自wiki)

可擴展韌體介面在軟體堆疊裡的位置

EFI開機管理員與 EFI drivers的溝通方式

EFI/UEFI Shell Command

列出EFI/UEFI 裡面 EFI Shell界面支持的命令。
對這些命令無法執行或出錯,在EFI shell提示符下輸入help命令。

可以使用的一些命令如下:

“Boot Commands — EFI Shell”.
“Configuration Commands — EFI Shell”.
“Device, Driver, and Handle Commands — EFI Shell”.
“Filesystem Commands — EFI Shell”.
“Memory Commands — EFI Shell”.
“Shell Navigation and Other Commands — EFI Shell”.
“Shell Script Commands / Programming Constructs — EFI Shell”.

Command Description
Boot Commands — EFI Shell. 

Commands related to nPartition booting. n分區啟動的相關命令

autoboot Set/view autoboot timeout variable.
bcfg Display/modify the driver/boot configuration.
boottest Set/view BootTest bits.
dbprofile Display/modify direct boot profiles for use by lanboot.
lanboot Boot over the LAN.
reconfigreset Reset the system (nPartition) for reconfiguration; the nPartition remains inactive, in the shutdown for reconfig state.
reset Reset the system (nPartition).
search Connect drivers for bootables devices.
Configuration Commands — EFI Shell. 

Commands for changing and retrieving system (nPartition) information.

acpiconfig Set/view ACPI configuration mode.
cellconfig Deconfigure/reconfigure cells. (Set cell use-on-next-boot values.)
cpuconfig Deconfigure/reconfigure processors and processor cores..
date Display the current date or set the date of the system (nPartition).
dimmconfig Deconfigure/reconfigure memory (DIMMs).
err Display/change the error level.
errdump View/clear logs.
fru View FRU data.
info Display hardware information.
monarch Set/view a monarch processor.
palproc Make a PAL call.
romdrivers Enable/disable PCI expansion ROM drivers.
rootcell Set/view preferred root cells. (Set nPartition core cell choices.)
salproc Make a SAL call.
tftp Performs TFTP operation to a bootp/DHCP enabled Unix boot server.
time Display the current time or set the time of the system (nPartition). EFI time is set and presented in GMT (Greenwich mean time).
variable Save/restore specific EFI variables.
ver Display the version information.
Device, Driver, and Handle Commands — EFI Shell. 

Commands for managing devices, drivers, and handles.

baud View serial port com settings.
connect Bind a driver to a device.
dblk Hex dump of BlkIo devices.
devices Display devices managed by EFI drivers.
devtree Display tree of devices.
dh Dump handle info.
disconnect Disconnect driver(s) from device(s).
drivers Display list of drivers.
drvcfg Invoke the Driver Config Protocol.
drvdiag Invoke the Driver Diagnostics Protocol.
guid Dump known GUID IDs.
lanaddress Display MAC address.
load Load EFI drivers.
map Map shortname to device path.
openinfo Display the open protocols for given handle.
pci Display PCI devices or PCI function configuration space.
reconnect Reconnect driver(s) from a device.
unload Unload a protocol image.
Filesystem Commands — EFI Shell. 

Commands for managing files, directories, and attributes.

attrib Display/change the attributes of files/directories.
cd Update/view the current directory.
comp Compare the contents of two files.
cp Copy one or more files/directories to another location.
edit Edit an ASCII or UNICODE file in full screen.
eficompress Compress infile and write to outfile.
efidecompress Decompress infile and write to outfile.
hexedit Edit a file, block device, or memory region using hex.
ls Display a list of files and subdirectories in a directory.
mkdir Create one or more directories.
mount Mount a filesystem on a block device.
rm Delete one or more files/directories.
setsize Set the size of a file.
touch Update time of file/directory with current time.
type Display the contents of a file.
vol Display volume information of the file system.
Memory Commands — EFI Shell. 

Commands for listing and managing memory, EFI variables, and NVRAM details.

default Set the default NVRAM values.
dmem Dump memory or memory mapped IO.
dmpstore Display all EFI variables.
memmap Display the memory map.
mm Display/modify MEM/IO/PCI.
pdt View/clear nPartition or cell memory page deallocation table (PDT).
Shell Navigation and Other Commands — EFI Shell. 

Commands for basic EFI Shell navigation and customization.

alias Set/get alias settings.
cls Clear the standard output with an optional background color.
exit Exit EFI Shell environment.
getmtc Display current monotonic counter value.
help or ? Display help.
mode Display the mode of the console output device.
set Set/Get environment variable.
xchar Turn on/off extended character features.
Shell Script Commands / Programming Constructs — EFI Shell. 

EFI shell-script commands.

echo Echo message to stdout or toggle script echo.
else Script-only: Use with IF THEN.
endfor Script-only: Delimiter for FOR loop construct.
endif Script-only: Delimiter for IF THEN construct.
for Script-only: Loop construct.
goto Script-only: Jump to label location in script.
if Script-only: IF THEN construct.
input Take user input and place in EFI variable.
pause Script-only: Prompt to quit or continue.
stall Stall the processor for some microseconds.

其他指令

STALL (以微秒為單位的延遲)

Note:
1. STALL in emulation NT environment will sleep for ‘microseconds’.
2. STALL in some other platforms will wait for ‘microseconds’.
3. Microseconds is in decimal units.
Examples:
Shell> stall 1000000 //表示Stall for 1000000 uS

Dism備份還原相關指令整理 (ScratchDir目錄必須存在,避免錯誤)

$
0
0

先在備份存儲分區下新建文件夾X:\sources和X:\temp (X表示備份位置,文件夾名字自定,英文)

進入winPE後,命令提示符

使用diskpart確定路徑(不同的環境下顯示的盤符可能不同)
diskpart
Diskpart>在這行字符後面直接輸入diskpart命令回車即可執行相應的操作。

幾個基本命令如下(注:// 前面為命令,//後面為命令解釋):
list disk // 列出所有接在電腦上的存儲設備,並為每個存儲設備用數字編號,通常主硬盤編號為0。
select disk N // 選中編號為N的磁盤
list part // 列出選中磁盤上所有的分區
select part N // 選中編號為N的分區
detail part // 顯示選中分區的詳細信息。其中“LTR”即為分區盤符。
exit // 退出Diskpart

這裡再介紹兩條命令(非Diskpart命令):
cd /d xxxxxx // 進入xxxx目錄。如,進入C:,則輸入cd /d C:
dir /a // 顯示當前目錄中的文件結構。

第一次備份:
Dism /Capture-image /Imagefile:X:\sources\install.wim /Scratchdir:X:\temp (/compress:maximum) /Capturedir:C:\ /Name:xxxx /Description:xxxxxx

後期備份:
Dism /Append-Image /Imagefile:X:\sources\install.wim /Scratchdir:X:\temp
(compress:maximum) /Capturedir:C:\ /Name:xxxx /Description:xxxx

/Compress:Maximum 高壓,當然對應用時更多,這一句為可選參數,默認為fast
/Description:xxxxx 可選參數,添加描述方便後期處理使用
(C表示系統安裝所在分區,xxxx中不要出現空格,每個參數以/開始、用空格隔開)

獲取WIM文件映像信息:
例如:查看 G:\sources\install.wim 映像信息
Dism /Get-WimInfo /Wimfile:G:\sources\install.wim

命令解釋:
/Get-WimInfo //顯示有關 WIM 文件中的映像的信息。
/Wimfile //指定 WIM 文件路徑。
    可以在後面添加索引號或名稱來查看某次備份的詳細信息。還可以在後面添加 >d:\list.txt 把信息導出為記事本,方便查看。如:
Dism /Get-WimInfo /Wimfile:G:\sources\install.wim /Index:1 >d:\list.txt
或:Dism /Get-WimInfo /Wimfile:G:\sources\install.wim /Name:Win8Pro-1 >d:\list.txt
    在一個映像中如果有兩個卷映像同名,就不能用指定名稱來查看這兩個卷映像的詳細信息。

具有多個卷映像的 WIM 文件中卷映像的處理:
例如:刪除 G:\sources\install.wim 中的第二次備份:               
Dism /Delete-Image /ImageFile:G:\sources\install.wim /Index:2
命令解釋:
/Delete-Image // 從具有多個卷映像的 WIM 文件刪除指定的捲映像。
    此命令僅刪除卷映像名稱與描述,而不會刪除卷映像數據。可用於防止誤應用該卷映像。刪除指定的捲映像僅刪除了名稱與描述,而不會刪除卷映像數據,所以不會減小 WIM 文件的體積。

用 /Export-Image 命令從具有多個卷映像的 WIM 文件中提取需要保留的單獨卷映像,以減小 WIM 文件的體積。
例如:從 G:\sources\install.wim 中提取第二次備份到 G:\sources 中,保存為 install-02.wim):
Dism /Export-Image /SourceImageFile:G:\sources\install.wim /SourceIndex:2 /DestinationImageFile:G:\sources\install-02.wim
命令解釋:
/Export-Image – 將指定映像的副本導出到其他文件。
/SourceImageFile – 指定映像文件來源路徑。
/SourceIndex – 指定來源索引。
    如果 Win8Pro.wim 中有五個備份,我們只想保留其中第二與第五個備份,同樣可以用 /Export-Image 命令把其中 Index:2 與 Index:5 提取出來,保存為 Win8.wim。命令如下:
Dism /Export-Image /SourceImageFile:G:\sources\install.wim /SourceIndex:2 /DestinationImageFile:G:\sources\install-02.wim
Dism /Export-Image /SourceImageFile:G:\sources\install.wim /SourceIndex:5 /DestinationImageFile:G:\sources\install-02.wim
     install02.wim 中就包涵有兩個卷映像,卷映像的索引號會發生改變,但名稱不會變。

把 Windows 系統(WIM 映像)快速安裝到任何分區:
把 G:\sources\install.wim 中第二次備份還原到 C 分區,指定臨時目錄為 G:\temp
Dism /Apply-Image /ImageFile:G:\sources\install.wim /Index:2 /ScratchDir:G:\temp /ApplyDir:C:\ /Verify
 /Apply-Image //應用一個映像。
/ApplyDir //指定應用目錄。
/Index // 指定索引。此項不能省略。
    因 Dism 安裝 WIM 映像不會像 Ghost 那樣格式化磁盤,所以如果需要可以自己格式化系統盤。
    修改映像路徑與應用目錄可把任意一個系統備份的捲映像還原到任意一個分區。修改卷映像索引號或卷映像名稱可以還原備份映像中的任意一個備份。
    
也可以用此命令把 Windows 系統(WIM 映像)快速安裝到任何分區。
例如在 PE 中把 Win8Pro 安裝到 C 分區(設 Win8ISO 用虛擬光驅加載的盤符為 E):
Dism /Apply-Image /ImageFile:E:\sources\install.wim /Index:1 /Scratchdir:X:\temp /ApplyDir:C:\
    由於 Windows 系統原始(WIM 映像)中沒有啟動引導文件,需要添加啟動引導:
bcdboot C:\windows /s C: /l zh-cn
    如果是把 Windows 8 安裝到 USB 設備中作 Windows To Go,也應添加啟動引導:
bcdboot X:\windows /s X: /l zh-cn /f ALL (X為 USB 設備的盤符)。

附錄:一些可用選項

/Verify – 指定校驗。用於檢查錯誤和文件重複。
/CheckIntegrity – 用於在捕捉、卸載、導出和提交操作中使用 .wim 文件時檢測和跟踪 .wim 文件的損壞情況。
    用於在 DISM 檢測到 .wim 文件在應用和裝載操作中發生損壞時停止操作。
/ScratchDir – 指定暫存目錄的路徑。此目錄必須存在。該目錄必須位於本地。
/Compress – 用於指定對初始捕捉操作使用的壓縮類型。
    maximum 選項能提供最佳壓縮效果,但是捕捉映像所用的時間較長。
    fast 選項能提供更快速的映像壓縮,但生成的文件比使用 maximum 選項壓縮的文件大。這也是在未指定參數時所用的默認壓縮類型。
    none 選項不會壓縮捕捉的映像。
/ConfigFile – 指定映像捕捉和壓縮命令排除列表配置文件的位置。

默認的排除列表——默認情況下,DISM.exe 工具將排除以下文件:

[ExclusionList]
\$ntfs.log
\hiberfil.sys
\pagefile.sys
\swapfile.sys
“\System Volume Information”
\RECYCLER
\Windows\CSC
[CompressionExclusionList]
*.mp3
*.zip
*.cab
\WINDOWS\inf\*.pnf

 

/ScratchDir – 指定暫存目錄的路徑。

創建或處理 Windows 映像時,你應使用帶有 DISM 的 /ScratchDir 選項,在不同的驅動器上創建臨時目錄。臨時目錄適用於許多 DISM 操作,包括捕獲映像、安裝語言包、更新或在 Windows 映像中安裝或刪除 Windows 功能。先將一些文件擴展到此臨時目錄,然後再將它們應用於 Windows 映像。

/ScratchDir
    指定用來解壓縮服務文件的臨時目錄。此目錄必須存在。
    指定在服務期間提取臨時使用的文件時要使用的臨時目錄。該目錄必須位於本地。
    安裝完成後,不再需要此目錄的內容,可以將其刪除。
    如果你不使用 /ScratchDir 選項設置臨時目錄路徑, Windows PE 將默認創建 32-MB 臨時目錄。
    如果未指定臨時目錄,將使用 \Windows\%Temp% 目錄,以及每次運行 DISM 時隨機生成的十六進制值的子目錄名稱。每次操作後,都會刪除暫存目錄中的項。
    作為最佳做法,你應使用 /ScratchDir 選項,轉而在其他有足夠空間支持任何映像管理和你所執行的服務操作的分區上指定目錄

Viewing all 57 articles
Browse latest View live
<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>