在 Windows 驅動開發中,將策略性、演算法類的核心邏輯同時編譯為核心模式庫(.lib/.sys)與使用者模式 DLL,可大幅提升開發測試效率。本文詳細說明如何透過 SOURCES 與 .def 檔案實現雙目標編譯。
關鍵在於定義 KERNEL_BUILD 巨集:
TARGETTYPE=EXPORT_DRIVER),產出 .sys 與 .lib。TARGETTYPE=DYNLINK),產出 .dll 與 .lib,並連結 CRT(USE_MSVCRT=1)。# 核心開關:定義為 1 時編譯核心庫,註解掉則編譯使用者 DLL
KERNEL_BUILD=1
TARGETNAME=Sample
DLLDEF=Sample.def # 匯出函數定義檔
TARGETPATH=../../bin # .sys 或 .dll 輸出目錄
TARGETPATHLIB=../../lib # .lib 輸出目錄
!ifdef KERNEL_BUILD
# === 核心模式編譯設定 ===
TARGETTYPE=EXPORT_DRIVER
!else
# === 使用者模式編譯設定 ===
TARGETTYPE=DYNLINK
USE_MSVCRT=1
DLLENTRY=_DllMainCRTStartup # DLL 進入點
INCLUDES=$(INCLUDES); $(DDK_INC_PATH)
INCLUDES=$(INCLUDES); $(SDK_INC_PATH)
TARGETLIBS=$(TARGETLIBS) $(SDK_LIB_PATH)\kernel32.lib
TARGETLIBS=$(TARGETLIBS) $(SDK_LIB_PATH)\gdi32.lib
TARGETLIBS=$(TARGETLIBS) $(SDK_LIB_PATH)\advapi32.lib
TARGETLIBS=$(TARGETLIBS) $(SDK_LIB_PATH)\ntdll.lib
!endif
# === 共用專案頭文件路徑 ===
INCLUDES=$(INCLUDES); ../../inc
INCLUDES=$(INCLUDES); ../../inc/SampleOne
INCLUDES=$(INCLUDES); ../../inc/SampleTwo
# === 共用原始碼檔案 ===
SOURCES=\
A.cpp\
B.cpp\
C.cpp\
D.cpp
; Sample.def
NAME "Sample.sys" ; 核心庫名稱;若編譯 DLL 請改為 "Sample.dll"
EXPORTS
DllInitialize PRIVATE ; 核心庫初始化/卸載進入點(私有)
DllUnload PRIVATE
AFunc ; 共用對外介面函數
BBFunc
核心庫需提供 DriverEntry/DriverUnload,內部委派給共用的 DllInitialize/DllUnload:
#include "ntddk.h"
extern "C" {
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pusRegPath);
void DriverUnload(PDRIVER_OBJECT pDriverObject);
NTSTATUS DllInitialize(PUNICODE_STRING RegistryPath);
NTSTATUS DllUnload();
}
void DriverUnload(PDRIVER_OBJECT pDriverObject) {
DllUnload();
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pusRegPath) {
DllInitialize(pusRegPath);
pDriverObject->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
// 共用初始化/清理邏輯
NTSTATUS DllInitialize(PUNICODE_STRING RegistryPath) {
DbgPrint("Sample: DllInitialize(%wZ)\n", RegistryPath);
return STATUS_SUCCESS;
}
NTSTATUS DllUnload() {
DbgPrint("Sample: DllUnload\n");
return STATUS_SUCCESS;
}
// 共用函數:明確指定呼叫慣例避免連結錯誤
// DDK 預設 _stdcall,VC 預設 _cdecl
ULONG _stdcall TestFun(void) {
return 100;
}
_stdcall。ntddk.h 等核心專用頭文件,需以條件編譯隔離。完整範例程式碼可參考:下載連結