☰
Windows-classic-samples 的 WDS PXE 过滤器 Provider(FilterProvider)样例深度解析:从 DLL 源码到注册与 .ini 配置的完整实战
2026/10/16 1:50:56 网站建设 项目流程
  • 示例工程

【免费下载链接】Windows-classic-samples

This repo contains samples that demonstrate the API used in Windows classic desktop applications.

项目地址:https://gitcode.com/gh_mirrors/wi/Windows-classic-samples
点击查看免费下载

本文围绕 Windows-classic-samples 仓库中的 FilterProvider 样例 展开,讲解如何在 Windows Deployment Services(WDS)的 PXE Server 上,用 C++ 编写一个"过滤器型" PXE Provider DLL:它在有序的 Provider 列表最前端充当"闸门",根据一个纯文本 .ini 文件中的策略决定哪些 PXE 客户端被放行给下游(如 WDSDCPXE/BINL)继续服务、哪些被直接拒绝。读完本文,你将掌握 PXE Provider 的注册表注册方式、PxeProviderInitialize/PxeProviderRecvRequest等核心回调的写法、配置文件格式以及完整的端到端部署流程。

WDS 与 PXE Provider 插件模型:过滤器 Provider 的定位

WDS 网络安装场景

Windows Deployment Services(WDS)用于通过网络完成 Windows 操作系统的批量部署:客户端机器可以基于网络启动(PXE Boot)进入 Windows PE 安装环境,从而无需管理员逐台访问每台机器、也无需使用 CD/DVD 介质逐台安装。WDS 服务器承担了引导映像分发、安装映像管理以及 PXE 网络引导等核心职责。

PXE Server 与 PXE Provider 双层结构

从实现上看,WDS 的 PXE Server 可以被拆分为两个部分:PXE Server与PXE Provider(插件):

  • PXE Server负责核心网络能力,包括监听网络、解析 DHCP/PXE 报文、维护与客户端的网络会话等;
  • PXE Provider是一组插件(plug-in),官方文档将其称为"PXE Providers"。PXE Server 对外暴露插件接口,允许开发者在复用同一套 PXE Server 核心网络代码的基础上,开发自定义的 PXE 解决方案。

标准 WDS 服务器自带WDSDCPXE提供程序(负责将请求交给 BINL 等上层服务处理)。插件模型的意义在于:你可以编写自己的 Provider DLL,与WDSDCPXE并存运行,甚至替换它。

过滤器 Provider:Provider 列表中的"闸门"

在注册表中,PXE Provider 按ProvidersOrder组成一个有序列表,WDSPXE 会把进来的 PXE 请求依次交给列表中的 Provider。

本样例实现的是一个filter provider(过滤器提供程序):它被放在列表最前面,位于WDSDCPXE(或任何其他 PXE Provider)之前,像一个"闸门":

  • 如果希望自己的 Provider总是拥有应答 PXE 请求的第一机会,就应把它排在列表首位;
  • 过滤器 Provider 自身不提供真正的引导/下载服务,它只裁决"这个客户端的请求是否允许流向下一个 Provider";
  • 与标准实现把数据存放在 Active Directory 中不同,本样例把过滤数据存放在一个纯文本文件里(见 cpp/README.txt),便于直接查看和修改。

样例文件清单与工程构成

样例位于 Samples/WindowsDeploymentServices/FilterProvider,cpp子目录下共 5 个文件,各自职责如下:

文件说明
FilterProv.cpp主 DLL 源码文件,包含全部必需的 DLL 入口点与辅助函数实现
FilterProv.h头文件,包含所需头文件并定义辅助数据结构与常量
FilterProv.sln解决方案文件,用 Visual Studio 打开后即可生成 DLL
FilterProv.vcxprojVisual Studio 项目文件,记录工具集版本、平台/配置与项目属性
FilterProv.def模块定义文件,声明 DLL 必须导出的函数,供 WDS PXE Server 加载

从 FilterProv.vcxproj 可以看到工程的关键属性:

  • 配置类型为DynamicLibrary(即产出 DLL);
  • 字符集为Unicode;
  • 四种构建组合:Debug/Release × Win32/x64,工具集为v110;
  • 链接器依赖wdspxe.lib(WDS PXE 提供程序 API 的导入库),这是编写 PXE Provider 必需的链接项;
  • 通过<ModuleDefinitionFile>filterprov.def</ModuleDefinitionFile>指定模块定义文件。

FilterProv.def 的内容极简但关键——它导出唯一的入口函数:

LIBRARY FilterProv EXPORTS PxeProviderInitialize

WDSPXE 在加载 Provider DLL 时正是通过查找并调用这个PxeProviderInitialize导出函数来完成初始化的。

核心源码解析:一个过滤器 Provider 的完整实现

DllMain:保存模块实例句柄

FilterProv.cpp 中的DllMain在DLL_PROCESS_ATTACH时把模块实例句柄保存到全局变量g_hInstance,供后续构造配置文件路径时使用(GetModuleFileName需要这个句柄)。

PxeProviderInitialize:初始化、回调注册与请求过滤属性

PxeProviderInitialize是 WDSPXE 调用的第一个函数(FilterProv.cpp),其工作分四步:

  1. 读取策略配置:调用InitializeConfiguration()构造配置文件路径并校验全局策略;
  2. 注册关闭回调:PxeRegisterCallback(hProvider, PXE_CALLBACK_SHUTDOWN, PxeProviderShutdown, NULL),当 WDSPXE 需要关闭 Provider 时回调PxeProviderShutdown;
  3. 注册请求处理回调:PxeRegisterCallback(hProvider, PXE_CALLBACK_RECV_REQUEST, PxeProviderRecvRequest, NULL),注册接收客户端请求的处理函数;
  4. 声明只处理 PXE 类型的请求:调用PxeProviderSetAttribute(hProvider, PXE_PROV_ATTR_FILTER, &uFilter, sizeof(uFilter)),其中uFilter = PXE_PROV_FILTER_PXE_ONLY。从源码注释看,该属性表示 Provider只接收"合法 DHCP 报文且包含 Option 60 'PXEClient'"的请求,从而避免处理无关的 DHCP 流量。

最后把 Provider 句柄存入全局g_hFilterProvider,供PxeTrace日志输出使用。

PxeProviderRecvRequest:逐请求决策

这是过滤器 Provider 的核心决策函数(FilterProv.cpp)。每次收到 PXE 请求时,WDSPXE 同步调用它,处理流程如下:

  1. 通过GetClientMacAddress()从 DHCP 报文中提取客户端 MAC 地址并格式化为字符串;
  2. 用GetPrivateProfileString()在配置文件的[Devices]段中查找该 MAC;
  3. 若命中,读取对应的设备级策略(Allow=1/Deny=0),并校验取值合法性(非法值一律按Deny处理);
  4. 若未命中,则使用全局策略g_uFilterPolicy(来自[Configuration]段的Policy键)进行裁决。

最终决策以PXE_BOOT_ACTION形式写入输出参数pAction,同时把各参数的"忽略"意图写得很清楚:hClientRequest、uPacketLen、pLocalAddress、pRemoteAddress、pContext在本实现中均未使用(UNREFERENCED_PARAMETER),全部决策只依赖报文中的 MAC 与配置文件。

GetClientMacAddress:从 DHCP 报文提取并格式化 MAC

GetClientMacAddress(FilterProv.cpp)把PXE_DHCP_MESSAGE中的硬件地址转换为定长字符串:

  • DHCP 报文中的HardwareAddress字段长度不定(由HardwareAddressLength给出),函数先用MoveMemory把它右对齐放入长度为PXE_DHCP_HWAADR_SIZE的缓冲区,左侧自动补零;
  • 随后逐字节将高 4 位、低 4 位转成十六进制字符(0-9、A-F,字母为大写)。

结合 FilterProv.h 中的定义MAX_MAC_ADDR_STRING_LEN = PXE_DHCP_HWAADR_SIZE * 2 + 1,以及 README 中"MAC 字符串必须为 32 个字符"的要求,可以推断PXE_DHCP_HWAADR_SIZE为 16(DHCPchaddr字段大小),16 字节展开为 32 个十六进制字符,外加一个终止符。这也正是配置文件[Devices]段键名必须是 32 字符(12 位 MAC 前补 20 个0)的原因——源码生成的字符串格式必须与配置文件键名精确一致。

InitializeConfiguration:配置文件路径构造与策略校验

InitializeConfiguration(FilterProv.cpp)负责:

  1. 用GetModuleFileName取得 DLL 的完整路径;
  2. 用StringCchCat追加后缀FILTER_CONFIG_FILE_SUFFIX(在 FilterProv.h 中定义为L".filter.ini"),从而得到配置文件filterprov.dll.filter.ini——配置文件永远与 DLL 同目录、以" DLL 名 + .filter.ini"命名;
  3. 用GetFileAttributes确认配置文件存在,不存在则报错;
  4. 读取[Configuration]段的Policy键,将其解析为整数并校验(只接受 0 或 1,其余值视为数据无效);
  5. 通过PxeTrace(g_hFilterProvider, PXE_TRACE_INFO, ...)输出日志,策略为Deny时记录"Filter Policy: Exclude",为Allow时记录"Filter Policy: Include"。

决策逻辑对照表

结合 README 的语义描述与源码映射,完整决策逻辑如下:

客户端状态配置取值源码返回动作(FilterProv.cpp)效果(README.txt 描述)
MAC 命中[Devices]1(Allow)PXE_BA_REJECTED放行:请求被转发给有序列表中的下一个 Provider
MAC 命中[Devices]0(Deny)PXE_BA_IGNORE拒绝:请求不被应答,也不转发给下一个 Provider
MAC 未命中Policy=1(Allow)PXE_BA_REJECTED放行:未知设备被允许继续流向下一个 Provider
MAC 未命中Policy=0(Deny)PXE_BA_IGNORE拒绝:未知设备被拒绝 PXE 服务,请求就此终止

需要说明的是:README.txt 站在管理员意图角度描述 Deny/Allow 的最终效果,而 FilterProv.cpp 将"允许"映射为返回PXE_BA_REJECTED、将"拒绝"映射为返回PXE_BA_IGNORE;PXE_BOOT_ACTION枚举值由 WDSPXE 在运行时解释,若需要精确的行为语义,请以 WDS PXE API 文档为准。

注册 Provider:让 WDSPXE 认识你的 DLL

根据 cpp/README.txt 的说明,注册一个 Provider 需要两个步骤:

第一步:创建代表你的 Provider 的注册表键

  • 在以下路径下创建一个新键(键名自定,例如FilterProvider):
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WDSServer\Providers\WDSPXE\Providers
  • 在该新键下创建一个值,指定 Provider DLL 的路径:
属性取值
名称(Name)ProviderDLL
类型(Type)REG_EXPAND_SZ
值(Value)自定义 Provider DLL 的完整路径与文件名

第二步:指定 Provider 在有序列表中的位置

  • 修改父键:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WDSServer\Providers\WDSPXE\Providers
  • 在该键下创建一个多字符串值:
属性取值
名称(Name)ProvidersOrder
类型(Type)REG_MULTI_SZ
值(Value)第一步中创建的键名(如FilterProvider)

WDSPXE 会按照ProvidersOrder中列出的顺序把收到的 PXE 请求依次交给注册的 Provider;想让自己的 Provider 永远拥有应答请求的第一机会,就把它排在列表最前面。以管理员身份在 WDS 服务器上执行的等效命令示例如下(依据 README 描述的注册表结构整理):

reg add "HKLM\SYSTEM\CurrentControlSet\Services\WDSServer\Providers\WDSPXE\Providers\FilterProvider" /v ProviderDLL /t REG_EXPAND_SZ /d "C:\WDS\filterprov.dll" /f reg add "HKLM\SYSTEM\CurrentControlSet\Services\WDSServer\Providers\WDSPXE\Providers" /v ProvidersOrder /t REG_MULTI_SZ /d "FilterProvider" /f

配置文件 filterprov.dll.filter.ini 详解

命名与位置规则

编译产物必须是 DLL 形式(例如filterprov.dll),配置文件必须命名为filterprov.dll.filter.ini且与 DLL 放在同一目录。命名规则由源码中的FILTER_CONFIG_FILE_SUFFIX(L".filter.ini")强制保证。

[Configuration] 段:Policy 键

该段只有一个键Policy,决定未知设备(不在[Devices]段中的设备)是否继续流向有序列表中的下一个 Provider:

取值语义效果
0Deny(拒绝)所有未知设备被样例 Provider 拒绝 PXE 服务:请求不被应答,也不转发给有序列表中的下一个 PXE Provider
1Allow(允许)所有未知设备被放行:请求被转发给有序列表中的下一个 PXE Provider

[Devices] 段:按 MAC 的设备级策略

该段列出所有已知设备:

  • 键名:设备的 MAC 地址字符串,长度必须为32 个字符(12 位十六进制 MAC 地址前补 20 个0);
  • 值:0 或 1,含义与全局Policy一致——0=Deny(拒绝请求且不转发给下一个 Provider)、1=Allow(把请求转发给有序列表中的下一个 Provider,由下游 Provider 决定是否提供服务)。

由于源码在转换 MAC 时使用大写A-F,配置文件中 MAC 的字母部分应使用大写以保证精确匹配。官方文档示例中的 MAC 位数略有出入,实际配置时应填写完整的 12 位十六进制 MAC,使键名严格保持 32 字符。

完整配置示例一:仅放行指定设备(白名单模式)

[Configuration] Policy=0 [Devices] 00000000000000000000FEFEDEBCBA=1 00000000000000000000123456789AB=1

Policy=0意味着默认拒绝所有未知设备;[Devices]中值为1的两台设备被放行。净效果是:只有列出的设备能到达下一个 Provider 并获得 PXE 服务。

完整配置示例二:仅拒绝指定设备(黑名单模式)

[Configuration] Policy=1 [Devices] 00000000000000000000FEFEDEBCBA=0 00000000000000000000123456789AB=0

Policy=1意味着默认放行所有未知设备;[Devices]中值为0的两台设备被拒绝。净效果是:除列出的设备外,其余设备都流向下一个 Provider,列出的设备被拒绝 PXE 服务。

端到端运行流程(Walkthrough)

使用该样例过滤器 Provider 的完整操作步骤如下:

  1. 把样例代码编译成 DLL(如filterprov.dll);
  2. 按上述格式创建 .ini 配置文件,命名为filterprov.dll.filter.ini并放在与 DLL 相同的目录;
  3. 将样例 Provider 注册为有序 Provider 列表中的第一个;
  4. 用 PXE 启动一台客户端机器。此时决策链路为:
    • a. PXE Server 把请求交给有序列表中的第一个 Provider(按第 3 步,即本样例 Provider);
    • b. 样例 Provider 在文件[Devices]段中查找引导中的设备:若命中,则遵循该设备指定的策略(Allow/Deny);
    • c. 若未命中,则读取[Configuration]段的Policy值并遵循之;
    • d. 任何没有被样例 Provider 过滤掉的请求,都会被传递给有序列表中的下一个已注册 Provider。

环境要求、构建与运行

系统要求

角色要求
客户端无受支持的系统(本样例面向服务器端开发)
服务器Windows Server 2012 R2

准备 WDS 服务器

除安装服务器角色外,完成本样例还需要:

  1. 在 Windows Server 2012 R2 上安装 Windows Deployment Services 服务器角色;
  2. 初始化 WDS 服务器;
  3. 向服务器添加引导映像(Boot.wim)。Boot.wim 是一份 Windows PE 映像,客户端机器可引导进入其中以安装操作系统,该文件位于 Windows 产品 DVD 上。

构建

  1. 启动 Visual Studio,选择File > Open > Project/Solution;
  2. 进入样例目录,双击打开FilterProv.sln;
  3. 按F7(Visual Studio 2013 为 F6)或使用Build > Build Solution构建样例,产出filterprov.dll(Debug/Release、Win32/x64 四种组合均可,工程已通过wdspxe.lib链接 WDS PXE API)。

运行与调试

README 中 F5(Debug > Start Debugging)与 Ctrl+F5(Debug > Start Without Debugging)是 Visual Studio 调试运行的一般流程;就本样例而言,产物是供 WDSPXE 加载的 DLL,并没有独立可执行入口,实际"运行"是将其注册到ProviderDLL指向的路径后,由 WDS PXE Server 进程加载,再通过 PXE 客户端触发请求来验证过滤效果。更详细的运行说明请参阅随样例附带的 cpp/README.txt。

小结

Windows-classic-samples 中的 FilterProvider 样例完整演示了一条自定义 WDS PXE Provider 的研发链路:从 FilterProv.def 导出PxeProviderInitialize,到 FilterProv.cpp 中实现初始化、回调注册、请求裁决与配置加载,再到注册表ProvidersOrder排序与.filter.ini文本配置。理解这个样例后,你可以在此基础上扩展更复杂的过滤逻辑(例如结合数据库、AD 查询或策略服务),在完全复用 WDS PXE Server 核心网络能力的前提下,构建属于自己企业的 PXE 准入与分发方案。

  • 示例工程

【免费下载链接】Windows-classic-samples

This repo contains samples that demonstrate the API used in Windows classic desktop applications.

项目地址:https://gitcode.com/gh_mirrors/wi/Windows-classic-samples
点击查看免费下载
上一篇:5个实用案例!设计师如何用Inpaint-web浏览器工具提升工作流效率
下一篇:Eventos Tech Brasil 国际化域名:为何选择.eventostechbrasil.com而非其他

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询