VsCode配置Wdk7600開發環境 以及 "自動編譯" 和 sources文件簡單介紹..
一丶 簡介
雖然Wdk7600
已經過時,但還是有很多項目是使用Wdk7600
編寫的. 而很多老項目配置環境有很多種方式. 如配置在visual studio 中編寫
. 配置在 notepad++
中編寫. 搜索全網也沒看到有VsCode
配置的方式. 索性這裏就寫一下.
注意: 不討論文章技術.對你有用你就看,對你無用就無需看. 且 不要站在現在很多人都用Vs2019 vs2022的IDE去寫項目的角度去看. 個人寫代碼用什麼IDE都可以. Vs2019也不錯.也很推薦.
但本文章也主要講解WDK7600的配置. 很多企業人員有很多項目爲了穩定不會貿然升級驅動. 所以WDK7600用的還是蠻多.
1.2 軟件安裝
如果配置此環境請下好以下軟件.
-
1.WDK7600(在官網中表示爲
wdk 7.1.0
) Wdk7600導航連接 -
2.VsCode VsCode 官網
-
3.VsCode中的C++插件
請將 Wdk7600 安裝到默認目錄 等熟悉後可以將其修改爲你自定義的目錄. 或者先通讀此片文章之後再進行配置.
1.3 開發環境配置步驟
- 1.添加路徑包含項
點擊設置-設置裏面搜索 System include
然後在裏面添加項.
C:\WinDDK\7600.16385.1\inc\crt
C:\WinDDK\7600.16385.1\inc\ddk
C:\WinDDK\7600.16385.1\inc\api
- 2.新建一個驅動文件,和對應sources文件,查看是否可以使用驅動文件
sources文件內容如下:
TARGETNAME=TestDriver
TARGETPATH=.
TARGETTYPE=DRIVER
MSC_WARNING_LEVEL= /W3 /WX
SOURCES= Driver.c
driver.c文件內容如下
#include <ntifs.h>
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject,
PUNICODE_STRING RegistryPath) {
DbgPrint("Hello, world!\n");
return STATUS_SUCCESS;
}
在命令行中啓動 編譯環境. 然後cd到驅動文件所在目錄. 直接輸入 build -cez 或者 bld即可.
可以看到可以正常輸出. 到這一步說明 VsCode的開發環境已經配置好了. 可以放心寫代碼了.
但有一點不足, 每次編譯都要另外一個CMD窗口啓動嗎? 這樣顯得會很麻煩. 如果能繼承到VsCode中那麼是不是就很好了.
1.4 集成終端編譯
1.4.1 集成任務
這一點經過我的研究已經實現. 我們需要使用VsCode中的 任務
在VsCode中有一個 終端,終端選項中有一個配置任務. (task) 我們只需要生成一個task. 然後將task替換爲我給的即可.
注意,這裏使用的路徑是默認路徑,
如果你修改過wdk的安裝目錄.請手動更改此json.
{
"version": "2.0.0",
"tasks": [
{
"label": "BuildDebug64_Win7OrHigh",
"type": "shell",
"command": "C:\\Windows\\System32\\cmd.exe",
"args": [
"/k",
"pushd \".\" && call \"C:\\WinDDK\\7600.16385.1\\bin\\setenv.bat \" C:\\WinDDK\\7600.16385.1\\ chk x64 WIN7\" && popd"
],
"group": "build",
"presentation": {
"reveal": "always"
},
"problemMatcher": "$msCompile"
},
{
"label": "BuildRelease64_Win7OrHigh",
"type": "shell",
"command": "C:\\Windows\\System32\\cmd.exe",
"args": [
"/k",
"pushd \".\" && call \"C:\\WinDDK\\7600.16385.1\\bin\\setenv.bat \" C:\\WinDDK\\7600.16385.1\\ fre x64 WIN7\" && popd"
],
"group": "build",
"presentation": {
"reveal": "always"
},
"problemMatcher": "$msCompile"
},
{
"label": "BuildDebug32_Win7OrHigh",
"type": "shell",
"command": "C:\\Windows\\System32\\cmd.exe",
"args": [
"/k",
"pushd \".\" && call \"C:\\WinDDK\\7600.16385.1\\bin\\setenv.bat \" C:\\WinDDK\\7600.16385.1\\ chk x86 WIN7\" && popd"
],
"group": "build",
"presentation": {
"reveal": "always"
},
"problemMatcher": "$msCompile"
},
{
"label": "BuildRelease32_Win7OrHigh",
"type": "shell",
"command": "C:\\Windows\\System32\\cmd.exe",
"args": [
"/k",
"pushd \".\" && call \"C:\\WinDDK\\7600.16385.1\\bin\\setenv.bat \" C:\\WinDDK\\7600.16385.1\\ fre x86 WIN7\" && popd"
],
"group": "build",
"presentation": {
"reveal": "always"
},
"problemMatcher": "$msCompile"
},
{
"label": "BuildDebug_WinXP",
"type": "shell",
"command": "C:\\Windows\\System32\\cmd.exe",
"args": [
"/k",
"pushd \".\" && call \"C:\\WinDDK\\7600.16385.1\\bin\\setenv.bat \" C:\\WinDDK\\7600.16385.1\\ chk x86 WXP \" && popd"
],
"group": "build",
"presentation": {
"reveal": "always"
},
"problemMatcher": "$msCompile"
},
{
"label": "BuildRelease_WinXP",
"type": "shell",
"command": "C:\\Windows\\System32\\cmd.exe",
"args": [
"/k",
"pushd \".\" && call \"C:\\WinDDK\\7600.16385.1\\bin\\setenv.bat \" C:\\WinDDK\\7600.16385.1\\ fre x86 WXP \" && popd"
],
"group": "build",
"presentation": {
"reveal": "always"
},
"problemMatcher": "$msCompile"
}
]
}
1.4.2 設置爲全局任務.
上面生成好的task.json 請放到
C:\Users\YourComputerName\AppData\Roaming\Code\User
最後 在終端中運行任務. 任務則選擇你配置好的編譯環境即可.
在終端的右側 則有你運行的任務. 現在你可以在你想要的任務中 運行 bld 命令進行編譯.
再也不需要 單獨打開一個cmd窗口進行編譯了.
如你想要編譯 win7release64版本則切換到此任務編譯即可. 像編譯32位版本則切換到32即可.
二丶Sources 文件編程
2.1 INCLUDES 字段
主要作用: 處理Include與CPP文件分離得情況
場景:
目錄A存放着 xxx.h文件.
目錄B(主目錄)存放着 xxx.cpp得實現文件
目錄B中有目錄A
那麼對應sources應該改爲如下:
TARGETNAME=testDriver1
TARGETPATH=.
TARGETTYPE=DRIVER
MSC_WARNING_LEVEL= /W3 /WX
INCLUDES= \
./test1
SOURCES= Driver.cpp\
test.cpp
目錄A則是 test1 這裏主要使用了 INCLUDES
命令指明瞭 .h所在得目錄.
當然也可以指向系統得目錄.
INCLUDES= $(INCLUDES) \
$(DDK_INC_PATH); \
..\common; \
..\..\util;
INCLUDES = $(DDK_INC_PATH);\
DDK_INC_PATH == WDKROOT\inc\ddk
-
目錄A(test1)中 .h文件內容如下:
#ifdef __cplusplus extern "C" { #endif #include <ntifs.h> #include <ntddk.h> #include <Ntstrsafe.h> #include "ntimage.h" #ifdef __cplusplus } #endif PVOID testprint();
-
主目錄實現.cpp內容如下
driver.c
#include <ntifs.h> #include <ntddk.h> #include "test.h" VOID DriverUnLoad( PDRIVER_OBJECT DriverObject) { KdPrint(("Exit")); } extern "C" NTSTATUS DriverEntry( PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pReg) { UNREFERENCED_PARAMETER(pDriverObj); UNREFERENCED_PARAMETER(pReg); KdBreakPoint(); testprint(); //引用了Test.h中的函數 return STATUS_SUCCESS; }
test.cpp
#include "test.h" PVOID testprint() { DbgPrint("testprint"); return NULL; }
如果sources中不使用 INCLUDES
知名.則會報錯,無法找到xxx.h 亦或者 .h和.cpp都放在同一目錄下.(主目錄下) 則不需要使用 INCLUDES
2.2 i386_SOURCES 32位驅動使用內聯彙編
- 用法一,cpp中使用內聯彙編.
在32位驅動中可以讓我們使用 內聯彙編.
設: test.cpp test.h driver.cpp都在同一目錄. 所以不需要使用 INCLUDES字段了.
例子如下:
Driver.cpp
#include <ntifs.h>
#include <ntddk.h>
#include "test.h"
VOID DriverUnLoad(
PDRIVER_OBJECT DriverObject)
{
KdPrint(("Exit"));
}
extern "C" NTSTATUS DriverEntry(
PDRIVER_OBJECT pDriverObj,
PUNICODE_STRING pReg)
{
UNREFERENCED_PARAMETER(pDriverObj);
UNREFERENCED_PARAMETER(pReg);
KdBreakPoint();
test();
return STATUS_SUCCESS;
}
test.h
#ifdef __cplusplus
extern "C"
{
#endif
#include <ntifs.h>
#include <ntddk.h>
#include <Ntstrsafe.h>
#include "ntimage.h"
#ifdef __cplusplus
}
#endif
PVOID test();
test.cpp (內部使用了內聯彙編)
#include "test.h"
PVOID __declspec(naked) test()
{
_asm {
mov eax,eax
ret
}
}
- 用法二 直接指定 .asm文件
上面是使用得內聯彙編,如果我們想將自己寫好得 純 asm文件也參與編譯.
那麼需要寫爲如下:
I386_SOURCES= i386\test86.asm
注意: xxx.asm一定要在 i386目錄下. 如果沒有此目錄我們需要新建一個目錄. 存放我們得.asm文件.
如果想要使用 xx.asm中的函數. 那麼只需要聲明即可.
extern "C" int __cdecl MyAdd(int x, int y);
test.asm 如下:
.386
.model flat,stdcall
option casemap:none
.const
.data
.code
MyAdd proc c ,n1:DWORD,n2:DWORD
mov eax,n1
add eax,n2
ret
MyAdd endp
End
2.3 AMD64_SOURCES 使用64位彙編
上面講了32位彙編的使用,在64位下.已經無法使用內聯彙編了. 需要我們單獨提供彙編然後參與編譯.
這裏就使用到了 AMD64_SOURCES
注意: xxx.asm 必須放在相對於主目錄下的 asm64目錄下.
文件內容如下:
TARGETNAME=UseX86AndX64Asm
TARGETPATH=Build
TARGETTYPE=DRIVER
USER_C_FLAGS=$(USER_C_FLAGS) /FAcs
LINKER_FLAGS=/INTEGRITYCHECK
INCLUDES = .
C_DEFINES=$(C_DEFINES) /wd4996 /wd4995
AMD64_SOURCES = amd64\myAdd.asm
SOURCES=start.cpp \
start.cpp 是驅動的代碼,(DriverEntry) 如果想要在DriverEntry中使用那麼我們就要聲明 xxx.asm中的函數纔可以. 且 需要聲明爲 fastcall.
asm測試代碼
.CODE
myAdd PROC
add rcx,rdx
mov rax,rcx
ret
myAdd ENDP
END
start.cpp實現.
#include "start.h"
#include <ntstrsafe.h>
VOID DriverUnload(PDRIVER_OBJECT pDriverObj)
{
KdPrint(("Unload Driver\n"));
}
extern "C" long long myAdd(long long a, long long b);
extern "C" NTSTATUS DriverEntry(
IN PDRIVER_OBJECT pRootDriverObj,
IN PUNICODE_STRING pRegPath)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
pRootDriverObj->DriverUnload = DriverUnload;
ULONG majIndex = 0;
KdBreakPoint();
myAdd(1, 2);
return status;
}
目錄結構爲:
RootDir
amd64(DIR)
myadd.asm
start.cpp (驅動入口代碼)
2.4 多驅動編譯
如果驅動項目較多,想一下全部進行編譯. 那麼就需要使用 DIRS字段.
編譯方法如下.
首先建立一個DIRS文件. 文件的內容指明你想編譯的驅動的文件目錄即可.
但是你的目錄裏面要指明sources
文件.亦或者是新的DIRS
.
DIRS
DIRS= \
A \
B \
C
上述意思代表編譯 A B C 三個文件目錄下的驅動
如果A目錄下有SOURCES則會讀取SOURCES文件進行編譯.A目錄. 如果B目錄又有內嵌的文件夾且有DIRS 那麼會優先讀取DIRS繼續尋找B目錄中的內嵌文件夾.直到找到有SOURCES存在的目錄進行編譯.
2.5 編譯等級設置
如果你想讓你的驅動編譯的時候檢測嚴格一點.則可以在SOURCES中定義如下字段.
MSC_WARNING_LEVEL= /W3 /WX
/W3 是警告級別 /W1 /W2 /W3 /W4 /W4等級最爲嚴格. 如果參數不使用則需要使用
UNREFERENCED_PARAMETER(pDriverObj); 來進行包含 否則在/w4登記下無法編譯通過.
/WX 是警告視爲錯誤.
2.6 將驅動編譯爲庫
驅動代碼也可以變成庫代碼,可以給別的驅動使用. 在高版本中的VS則直接生成即可. wdk7600則必須我們使用 sources指定了.
分爲以下幾點講解.
我們可以將我們的驅動編譯爲庫. 這裏涉及到庫開發.
分別是:
- 驅動中如何生成庫
- 驅動中如何使用自定義的庫
2.6.1 驅動中如何生成庫
首先如果你是以C/C++ 開發的話 那麼就要給一個.h和一個.cpp文件.
假設以 test.h test.cpp爲例
那麼驅動的sources文件內容應該如下:
TARGETNAME=test
TARGETPATH=.
TARGETTYPE=LIBRARY
DRIVERTYPE=FS
MSC_WARNING_LEVEL= /W3 /WX
INCLUDES= \
./test
SOURCES= test.cpp
其中我的目錄結構爲:
ROOTDIR
test(DIR)
test.h
test.cpp
test.h如下:
#ifdef __cplusplus
extern "C"
{
#endif
#include <ntddk.h>
#ifdef __cplusplus
}
#endif
class test
{
private:
/* data */
public:
test(/* args */);
~test();
PVOID testprint();
};
test.cpp
#include "test.h"
test::test(/* args */)
{
}
test::~test()
{
}
PVOID test::testprint()
{
DbgPrint("testprint");
return NULL;
}
生成後則會生成test.lib庫
2.6.2 驅動中使用庫
使用庫就很簡單了.將頭文件拷貝過來.
然後在SOURCE裏面指明即可.
sources如下:
TARGETNAME=test1
TARGETPATH=.
TARGETTYPE=DRIVER
MSC_WARNING_LEVEL= /W3 /WX
INCLUDES= \
./test
TARGETLIBS = .\libs\test.lib
SOURCES= Driver.cpp
test.h同上一樣.
Driver.cpp如下:
#include "test\test.h"
VOID DriverUnLoad(
PDRIVER_OBJECT DriverObject)
{
KdPrint(("Exit"));
}
extern "C" NTSTATUS DriverEntry(
PDRIVER_OBJECT pDriverObj,
PUNICODE_STRING pReg)
{
UNREFERENCED_PARAMETER(pDriverObj);
UNREFERENCED_PARAMETER(pReg);
KdBreakPoint();
pDriverObj->DriverUnload = DriverUnLoad;
test t;
t.testprint();
return STATUS_SUCCESS;
}
如果是C語言則直接編譯即可.
關於TARGETLIBS 還可以包含路徑.
例如如下:
TARGETLIBS = $(DDK_LIB_PATH)\xxx1.lib\
$(DDK_LIB_PATH)\xxx2.lib\
例如包含 ntstrsafe.lib庫
TARGETLIBS= $(DDK_LIB_PATH)\ntstrsafe.lib
系統提供的路徑有如下:
DDK_LIB_PATH == WDKROOT\lib\Version\*
SDK_LIB_PATH == WDKROOT\lib\Version\*
CRT_LIB_PATH
2.7 C常量定義
在SOURCES文件中可以使用 C_DEFINES
它的意思則是等價於你在.c文件中使用了#define來聲明宏
TARGETNAME=test1
TARGETPATH=.
TARGETTYPE=DRIVER
MSC_WARNING_LEVEL= /W3 /WX
!IFDEF DDKBUILDENV
C_DEFINES=$(C_DEFINES) -DDDK_BUILD
!ENDIF
INCLUDES= \
./test
TARGETLIBS = .\libs\test.lib
SOURCES= Driver.cpp
例子2:
C_DEFINES=$(C_DEFINES) /wd4996
2.8 SOURCES指明編譯的文件 以及條件宏
WDK中找的. 可以爲驅動編譯資源 可以定義兩個SOURCE分別指向要編譯的文件
然後最終引用
例子:
!if $(IA64)
xxxxx 條件使用 IA64
!endif
DIR_SOURCES=wacompen.c \
wacompen.rc \
oempen.c \
errcodes.mc
STB_SOURCES=hid.c \
pnp.c \
serial.c \
errlog.c
SOURCES= $(DIR_SOURCES) $(STB_SOURCES)