找回密码
 注册
搜索
系统gho:最纯净好用系统下载站投放广告、加入VIP会员,请联系 微信:wuyouceo
查看: 3598|回复: 23

[讨论] 一些 PECMD 封装函数和调用 WinApi 示例代码

  [复制链接]
发表于 2026-1-17 19:19:25 | 显示全部楼层 |阅读模式
本帖最后由 Bluebells 于 2026-8-31 20:53 编辑

一些实用的(?) PECMD 示例代码分享给大家参考参考
代码质量可能不太好, 一些术语描述可能有误, 因为本人没啥编程基础, 希望大家谅解

EFI 变量设置封装函数
简单的 Ini 文本文件读写封装函数 (2楼)
获取目标驱动器的磁盘号和分区号 (3楼)
判断当前安全启动状态 或使用 18 楼示例代码
获取系统版本号 (8楼)
获取计算机名, 当前登录用户名和 Windows 系统产品名称 (10楼)
脱机注册表封装函数
获取系统(启动)分区 (14楼)
获取系统当前启动方式 (15楼)
获取 Windows, System 和 Temp 目录 (16楼)
调用libwim-15.dll函数获取WIM/ESD映像文件信息
枚举 PE 文件中的证书名和 EFI 中的某些变量中的证书名
获取系统当前安全启动状态 (17楼)
一些路径处理函数的调用示例 (21楼)
判断计算机是否已加入工作组或域 (22楼)
查看 VHD 启动信息 (23楼)
 楼主| 发表于 2026-1-17 19:20:19 | 显示全部楼层
本帖最后由 Bluebells 于 2026-8-31 20:26 编辑
  1. ;IniRead 有五个参数, 第一个参数为目标 .ini 完整文件名, 第二个参数为节名, 第三个参数为键名, 第四个参数为默认值, 第五个参数为存放读取的值的变量
  2. ;当无法读取目标键的值时, 其返回值(第五个参数变量值)将为默认值(第四个参数定义的值)
  3. _SUB IniRead
  4.     TEAM ENVI &FileName=%~1| ENVI &SectionName=%~2| ENVI &KeyName=%~3| ENVI &DefValue=%~4
  5.     ENVI$# &ValueName=*65535 0
  6.     CALL $--qd --ret:&ret Kernel32.dll,GetPrivateProfileStringW,$%&SectionName%,$%&KeyName%,$%&DefValue%,*&ValueName,#65535,$%&FileName%
  7.     ENVI-ret %~5=%&ValueName%
  8. _END

  9. ;IniWrite 有四个参数, 第一个参数为目标 .ini 完整文件名, 第二个参数为节名, 第三个参数为键名, 第四个参数为值名, 第五个参数为返回值(若返回零则写入失败)
  10. ;其中第四个和第五个参数为可选, 当第四个参数留空时, 将移除目标键的值
  11. _SUB IniWrite
  12.     TEAM ENVI &FileName=%~1| ENVI &SectionName=%~2| ENVI &KeyName=%~3| ENVI &ValueName=%~4
  13.     CALL $--qd --ret:&ret Kernel32.dll,WritePrivateProfileStringW,$%&SectionName%,$%&KeyName%,$%&ValueName%,$%&FileName%
  14.     ENVI-ret %~5=%&ret%
  15. _END

  16. ;示例
  17. IniRead "D:\Example\Config.ini" "Settings" "Simple" "" &Value
  18. MESS %&Value%
  19. IniWrite "D:\Example\Config.ini" "Settings" "Simple" "Value" &RetVal
  20. MESS %&RetVal%
复制代码
PS: mdyblog 老大的 PECMD 的示例代码(readini.wcs)中有读取 Ini 文本文件的封装代码段, 不过挺复杂的; 将 IniWrite 封装函数内置到 PECMD 可执行文件里时好像需要将代码文件的文本编码转换为 ANSI 编码, 否则可能会导致 PECMD 在执行该封装函数时卡死


回复

使用道具 举报

 楼主| 发表于 2026-1-17 19:23:43 | 显示全部楼层
本帖最后由 Bluebells 于 2026-8-31 20:34 编辑
  1. ;只有一个输入参数和两个输出参数
  2. ;输入参数为路径, 支持驱动器号(盘符), 路径命名空间(\\?\HarddiskVolumeX, \\?\HarddiskX\PartitionX 等)
  3. ;第一个输出参数为设备号, 第二个输出参数为分区号
  4. _SUB GetDevicePartNum
  5.     TEAM ENVI &drv=%~1| ENVI-ret %~2=| ENVI-ret %~3=
  6.     TEAM ENVI &prefix=\\?\| ENVI &gr=
  7.     TEAM LSTR aStr1=4,%&drv%| LSTR aStr2=7,%&drv%
  8.     FIND $%aStr1%=\\?\,ENVI &prefix=
  9.     FIND $%aStr2%=\Device,ENVI &gr=GLOBALROOT
  10.    
  11.     TEAM ENVI &FILE_ANY_ACCESS=0x00000000| ENVI &OPEN_EXISTING=3
  12.     TEAM ENVI &IOCTL_STORAGE_GET_DEVICE_NUMBER=0x2D1080| ENVI$# &udtQuery=*4 0 *4 0 *4 0| ENVI$# &cbBytesReturned=*8 0
  13.     CALL $--qd --ret:&hFile Kernel32.dll,CreateFileW,$%&prefix%%&gr%%&drv%,#0,#%&FILE_ANY_ACCESS%,#0,#%&OPEN_EXISTING%,#0,#0
  14.     IFEX #%&hFile%=-1, EXIT
  15.     CALL $--qd --ret:&ret Kernel32.dll,DeviceIoControl,#%&hFile%,#%&IOCTL_STORAGE_GET_DEVICE_NUMBER%,#0,#0,*&udtQuery,#12,*&cbBytesReturned,#0 //待处理
  16.     CALL $--qd --ret:&ret Kernel32.dll,CloseHandle,#%&hFile%
  17.     TEAM ENVI?int &udtQuery=&DeviceNum:4| ENVI?int &udtQuery=&PartNum:8
  18.     ENVI-ret %~2=%&DeviceNum%
  19.     ENVI-ret %~3=%&PartNum%
  20. _END

  21. ;示例
  22. GetDevicePartNum C: &1 &2
  23. GetDevicePartNum \\?\HarddiskVolume4 &3 &4
  24. MESS 设备(磁盘)号: %1%\n分区号: %2%\n\n设备(磁盘)号: %3%\n分区号: %4%
复制代码



回复

使用道具 举报

发表于 2026-1-17 19:30:51 | 显示全部楼层
回复

使用道具 举报

发表于 2026-1-17 19:32:19 | 显示全部楼层
学习一下
回复

使用道具 举报

发表于 2026-1-17 19:47:52 来自手机 | 显示全部楼层
强贴留名
回复

使用道具 举报

发表于 2026-1-17 19:54:47 | 显示全部楼层
了解一下。
回复

使用道具 举报

 楼主| 发表于 2026-1-17 20:09:23 | 显示全部楼层
本帖最后由 Bluebells 于 2026-8-31 20:34 编辑
  1. ;此封装函数有三个参数, 第一个参数用于存放返回的操作系统主要版本号, 第二个参数用于存放返回的操作系统次要版本号, 第三个参数用于存放返回的操作系统内部版本号
  2. _SUB RtlGetVersion
  3.     ENVI$# &VersionInformation=*4 0 *4 0 *4 0 *4 0 *4 0 *256 0
  4.     ENVI-long &VersionInformation=276:0
  5.     CALL $--qd --ret:&ret ntdll.dll,RtlGetVersion,*&VersionInformation
  6.     IFEX #%&ret%<>0,EXIT
  7.     TEAM ENVI?int &VersionInformation=&MajorVersion:4| ENVI?int &VersionInformation=&MinorVersion:8| ENVI?int &VersionInformation=&BuildNumber:12
  8.     ENVI-ret %~1=%MajorVersion%
  9.     ENVI-ret %~2=%MinorVersion%
  10.     ENVI-ret %~3=%BuildNumber%
  11. _END

  12. ;参数同上
  13. _SUB GetNTVersion
  14.     TEAM ENVI-long &pMajorVersion=0| ENVI-long &pMinorVersion=0| ENVI-long &pBuildNumber=0
  15.     CALL $--qd --ret:&ret ntdll.dll,RtlGetNtVersionNumbers,*&pMajorVersion,*&pMinorVersion,*&pBuildNumber
  16.     IFEX #%&ret%=0,EXIT
  17.     TEAM ENVI?int &pMajorVersion=&MajorVersion| ENVI?int &pMinorVersion=&MinorVersion| ENVI?int &pBuildNumber=&BuildNumber
  18.     CALC #&BuildNumber = %&BuildNumber% & 0xFFFF
  19.     ENVI-ret %~1=%MajorVersion%
  20.     ENVI-ret %~2=%MinorVersion%
  21.     ENVI-ret %~3=%BuildNumber%
  22. _END

  23. ;示例
  24. RtlGetVersion &MajorVersion &MinorVersion &BuildNumber
  25. MESS %&MajorVersion%\n%&MinorVersion%\n%&BuildNumber%
  26. GetNTVersion &MajorVersion &MinorVersion &BuildNumber
  27. MESS %&MajorVersion%\n%&MinorVersion%\n%&BuildNumber%
复制代码
PS: RtlGetNtVersionNumbers 为非公开函数, 且支持系统版本范围为 NT5.1+; 感谢 wintoflash 老大解释如何获取正确的 BuildNumber 值


回复

使用道具 举报

发表于 2026-1-17 20:19:24 | 显示全部楼层
技术贴 支持
回复

使用道具 举报

 楼主| 发表于 2026-1-17 20:27:20 | 显示全部楼层
本帖最后由 Bluebells 于 2026-8-31 20:33 编辑
  1. ;获取计算机名称, 仅一个参数, 用于存放返回的计算机名称
  2. _SUB GetComputerName
  3.     ENVI-int &pnSize=0
  4.     CALL $--qd --ret:&ret Kernel32.dll,GetComputerNameW,#0,*&pnSize
  5.     ENVI?int &pnSize=&nSize
  6.     IFEX #%&nSize%=0,EXIT _SUB
  7.     ENVI$ &lpBuffer=*%&nSize% 0
  8.     CALL $--qd --ret:&&ret Kernel32.dll,GetComputerNameW,*&lpBuffer,*&pnSize
  9.     IFEX #%&ret%=0,EXIT _SUB
  10.     ENVI-ret %~1=%&lpBuffer%
  11. _END

  12. ;获取系统当前登录用户名, 仅一个参数, 用于存放返回的当前登录用户名
  13. _SUB GetUserName
  14.     ENVI-int &pcbBuffer=0
  15.     CALL $--qd --ret:&ret Advapi32.dll,GetUserNameW,#0,*&pcbBuffer
  16.     ENVI?int &pcbBuffer=&cbBuffer
  17.     IFEX #%&cbBuffer%=0,EXIT _SUB
  18.     ENVI$ &lpBuffer=*%&cbBuffer% 0
  19.     CALL $--qd --ret:&ret Advapi32.dll,GetUserNameW,*&lpBuffer,*&pcbBuffer
  20.     IFEX #%&ret%=0,EXIT _SUB
  21.     ENVI-ret %~1=%&lpBuffer%
  22. _END

  23. ;获取 Windows 系统产品名称, 仅一个参数, 用于存放返回的产品名称
  24. _SUB GetProductName
  25.     CALL $--qd --ret:&ret winbrand.dll,BrandingFormatString,$%WINDOWS_LONG%
  26.     IFEX #%&ret%>0,TEAM ENVI-mkfixdummy &ProductName=%&ret%| CALL $--qd Kernel32.dll,GlobalFree,#%&ret%
  27.     ENVI-ret %~1=%&ProductName%
  28. _END

  29. ;调用示例
  30. GetComputerName &ComputerName
  31. GetUserName &UserName
  32. GetProductName &ProductName
  33. MESS 计算机名称: %&ComputerName%\n\n用户名: %&UserName%\n\n产品名称: %&ProductName%
复制代码

回复

使用道具 举报

发表于 2026-1-17 21:05:17 | 显示全部楼层
感谢分享!
回复

使用道具 举报

发表于 2026-1-17 21:22:06 | 显示全部楼层
谢谢分享
回复

使用道具 举报

发表于 2026-1-18 00:26:16 | 显示全部楼层
学习一下。
回复

使用道具 举报

 楼主| 发表于 2026-1-18 19:51:44 | 显示全部楼层
本帖最后由 Bluebells 于 2026-5-3 14:13 编辑

获取系统(启动)分区
  1. _SUB GetBootDevice
  2.     IFEX #%&bX64%=3, ENVI &PtrSz=8! ENVI &PtrSz=4
  3.     CALC #&Sz=%&PtrSz% * 2
  4.     CALC #&SzA=%&Sz% + 8192
  5.     ENVI$# &retName=*%&SzA% 0 *32 0
  6.     ENVI-mkdummy &Nm=&retName@%&Sz%;8192
  7.     CALL $--qd --ret:&ret Ntdll.dll,NtQuerySystemInformation,#98,*&retName,#%&SzA%,#0
  8.     IFEX #%&ret%=0,ENVI-ret %~1=%&Nm%
  9. _END

  10. ;示例
  11. GetBootDevice &BootDevice
  12. MESS %&BootDevice%
复制代码
PS: 此代码摘自 mdyblog 老大的 PECMD 的示例代码, 且返回信息为 dos 设备路径


回复

使用道具 举报

 楼主| 发表于 2026-1-18 19:56:01 | 显示全部楼层
本帖最后由 Bluebells 于 2026-5-3 14:13 编辑

获取系统当前启动方式
  1. _SUB GetSystemBootEnvironmentInformation
  2.     ENVI &SystemBootEnvironmentInformation=90
  3.     ENVI$# &pBuffer=*16 0 *4 0 *8 0
  4.     CALL $--qd --ret:&ret Ntdll.dll,NtQuerySystemInformation,#%&SystemBootEnvironmentInformation%,*&pBuffer,#32,#0
  5.     IFEX #%&ret%=0,
  6.     {*
  7.         ENVI?int pBuffer=&&FirmwareType:16
  8.         IFEX #%&FirmwareType%=0,ENVI-ret %~1=Unknown
  9.         IFEX #%&FirmwareType%=1,ENVI-ret %~1=BIOS
  10.         IFEX #%&FirmwareType%=2,ENVI-ret %~1=UEFI
  11.         IFEX #%&FirmwareType%=3,ENVI-ret %~1=Unknown
  12.     }! ENVI-ret %~1=Unknown
  13. _END

  14. ;示例
  15. GetSystemBootEnvironmentInformation &BootEnvironment
  16. MESS %&BootEnvironment%
复制代码
PS: 一些人根据磁盘的分区样式去判断启动方式是非常不正确的
回复

使用道具 举报

 楼主| 发表于 2026-1-18 20:09:55 | 显示全部楼层
本帖最后由 Bluebells 于 2026-8-31 20:41 编辑

获取 Windows, System 和 Temp 目录
  1. //获取 Windows 目录路径
  2. CALL $--qd --ret:&ret Kernel32.dll,GetWindowsDirectoryW,#0,#0
  3. ENVI$ &lpBuffer=*%&ret% 0   //根据上一个函数调用的返回值获取 &&lpBuffer 指针的大小以节省内存
  4. CALL $--qd --ret:&ret Kernel32.dll,GetWindowsDirectoryW,*&lpBuffer,#%&ret%
  5. MESS %&lpBuffer%
  6. //另一获取当前系统根目录路径(需 Win10RS2+)
  7. CALL $--qd --ret:&ret ntdll.dll,RtlGetNtSystemRoot
  8. IFEX #%&ret%>0,
  9. {
  10.     ENVI-mkfixdummy &SR=%&ret%
  11.     MESS %&SR%
  12. }

  13. //获取 System 目录路径
  14. CALL $--qd --ret:&ret Kernel32.dll,GetSystemDirectoryW,#0,#0
  15. ENVI$ &lpBuffer=*%&ret% 0   //根据上一个函数调用的返回值获取 &&lpBuffer 指针的大小以节省内存
  16. CALL $--qd --ret:&ret Kernel32.dll,GetSystemDirectoryW,*&lpBuffer,#%&ret%
  17. MESS %&lpBuffer%

  18. //获取临时目录路径
  19. CALL $--qd --ret:&ret Kernel32.dll,GetTempPathW,#0,#0
  20. ENVI$ &lpBuffer=*%&ret% 0   //根据上一个函数调用的返回值获取 &&lpBuffer 指针的大小以节省内存
  21. CALL $--qd --ret:&ret Kernel32.dll,GetTempPathW,#%&ret%,*&lpBuffer
  22. MESS %&lpBuffer%
复制代码
PS: 也许有人说, 为啥不直接用环境变量, 正所谓萝卜青菜,各有所爱

回复

使用道具 举报

 楼主| 发表于 2026-1-18 20:43:01 | 显示全部楼层
本帖最后由 Bluebells 于 2026-5-3 14:16 编辑

获取当前安全启动状态
  1. ;此方法仅支持NT6.2及以上版本, 此函数还能查询目标机器是否支持安全启动功能
  2. _SUB GetSystemSecureBootInformation
  3.     ENVI$ &SSBI=*1 0 *1 0
  4.     CALL $--qd --ret:&ret ntdll.dll,NtQuerySystemInformation,#145,*&SSBI,#2,#0
  5.     IFEX #%&ret%<>0,TEAM ENVI-ret %~1=Unknown| EXIT _SUB
  6.     ENVI?char &SSBI=&SecureBootEnabled
  7.     ;ENVI?char &SSBI=&SecureBootCapable:1
  8.     IFEX #%&SecureBootEnabled%=0,ENVI-ret %~1=Disabled! ENVI-ret %~1=Enabled
  9. _END

  10. ;示例
  11. GetSystemSecureBootInformation &SecureBoot
  12. MESS %&SecureBoot%
复制代码
PS: 在 64 位操作系统下必须使用 64 位版本的 PECMD 才能正确调用此函数



回复

使用道具 举报

发表于 2026-1-19 08:21:53 | 显示全部楼层
等热心人来
回复

使用道具 举报

发表于 2026-1-19 13:17:36 来自手机 | 显示全部楼层
感谢分享
回复

使用道具 举报

发表于 2026-1-21 13:30:07 | 显示全部楼层
学习一下
回复

使用道具 举报

 楼主| 发表于 2026-8-31 20:45:28 | 显示全部楼层
一些路径处理函数的调用示例
  1. //去除路径末尾的反斜杠符号
  2. ENVI &pszPath=C:\Windows\System32\Config\
  3. CALL $--qd --ret:&ret Shlwapi.dll,PathRemoveBackslashW,*&pszPath
  4. MESS %&pszPath%

  5. //返回目标路径后面的第一个反斜杠部分
  6. ENVI &LPCWSTR=C:\Windows\System32\cmd.exe
  7. CALL $--qd --ret:&ret Shlwapi.dll,PathFindNextComponentW,*&LPCWSTR
  8. ENVI-mkdummy &pszPath=%&ret%
  9. MESS %&pszPath%

  10. //当目标路径存在空格时将为该路径添加引导
  11. ENVI$ &lpsz=*260 0
  12. ENVI &String=G:\Ys Memories of Celceta\puzzle\pz1prts1.itp
  13. ENVI-copy &lpsz=&String;0
  14. CALL $--qd --ret:&ret Shlwapi.dll,PathQuoteSpacesW,*&lpsz
  15. IFEX #%&ret%=1,MESS %&lpsz%
复制代码
PS: 感谢 527104427 的指导
回复

使用道具 举报

 楼主| 发表于 2026-8-31 20:46:45 | 显示全部楼层
判断计算机是否已加入工作组或域示例
  1. TEAM ENVI-ptr &lpNameBuffer=0| ENVI-int &BufferType=0
  2. CALL $--qd --ret:&ret Netapi32.dll,NetGetJoinInformation,#0,*&lpNameBuffer,*&BufferType
  3. IFEX #%&ret%=0,
  4. {
  5.     TEAM ENVI?ptr &lpNameBuffer=&pNameBuffer| ENVI?int &BufferType=&Join_Status
  6.     ENVI-mkfixdummy &NameBuffer=%&pNameBuffer%
  7.     IFEX $%&Join_Status%=0,MESS 加入状态未知。@JOIN_STATUS#OK
  8.     IFEX $%&Join_Status%=1,MESS 计算机未加入任何域或工作组。@JOIN_STATUS#OK
  9.     IFEX $%&Join_Status%=2,MESS 计算机加入了一个工作组。\n%&NameBuffer%@JOIN_STATUS#OK
  10.     IFEX $%&Join_Status%=3,MESS 计算机加入了一个域。@JOIN_STATUS#OK
  11.     CALL $--qd --ret:&ret Netapi32.dll,NetApiBufferFree,#%&pNameBuffer%
  12. }
复制代码

回复

使用道具 举报

 楼主| 发表于 2026-8-31 20:48:43 | 显示全部楼层
查看 VHD 启动信息
  1. _SUB GetVhdBootInformation
  2.     TEAM ENVI$ &NL=0d 0a| ENVI$# &Buffer=*265 0
  3.     CALL $--qd --ret:&ret Ntdll.dll,NtQuerySystemInformation,#112,*&Buffer,#265,#0
  4.     IFEX #%&ret%=0,
  5.     {
  6.         ENVI?char &Buffer=&OsDiskIsVhd
  7.         IFEX #%&OsDiskIsVhd%=0,MESS 当前系统非 VHD 启动!
  8.         {
  9.             ENVI?int &Buffer=&Offset:4
  10.             ENVI-mkfixdummy &Root=&Buffer@8
  11.             ENVI-mkfixdummy &ParentVolume=&Buffer@%&Offset%
  12.             MESS 当前系统以 VHD 启动%&NL%VHD 映像根路径: %&Root%%&NL%VHD 父卷文件路径: %&ParentVolume%
  13.         }
  14.     }
  15. _END

  16. CALL GetVhdBootInformation
复制代码
回复

使用道具 举报

发表于 2026-8-31 20:51:00 来自手机 | 显示全部楼层
感谢分享
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|Archiver|捐助支持|无忧启动 ( 闽ICP备05002490号-1|闽公网安备35020302032614号 )

GMT+8, 2026-9-11 17:05

Powered by Discuz! X5.0

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表