无忧启动论坛

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

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

  [复制链接]
跳转到指定楼层
1#
发表于 昨天 19:19 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 Bluebells 于 2026-1-18 20:43 编辑

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

EFI 变量设置封装函数
简单的 Ini 文本文件读写封装函数 (2楼)
获取目标驱动器的磁盘号和分区号 (3楼)
判断当前安全启动状态 或使用 18 楼示例代码
获取系统版本号 (8楼)
获取计算机名和当前登录用户名 (10楼)
脱机注册表封装函数
获取系统(启动)分区 (15楼)
获取系统当前启动方式 (16楼)
获取 Windows, System 和 Temp 目录 (17楼)

2#
 楼主| 发表于 昨天 19:20 | 只看该作者
  1.     ;IniRead 有五个参数, 第一个参数为目标 .ini 完整文件名, 第二个参数为节名, 第三个参数为键名, 第四个参数为默认值, 第五个参数为存放读取的值的变量
  2.     ;当无法读取目标键的值时, 其返回值(第五个参数变量值)将为默认值(第四个参数定义的值)
  3.     _SUB IniRead
  4.     TEAM ENVI &FileName=%~1| ENVI &SectionName=%~2| ENVI &KeyName=%~3| ENVI &DefValue=%~4
  5.     FIND %&FileName%=,EXIT _SUB
  6.     FIND %&SectionName%=,EXIT _SUB
  7.     FIND %&KeyName%=,EXIT _SUB
  8.     FIND %&DefValue%=,EXIT _SUB
  9.     CALL $--qd --ret:&ret Kernel32.dll,GetPrivateProfileStringW,$%&SectionName%,$%&KeyName%,$%&DefValue%,0,0,$%&FileName%
  10.     TEAM CALC #&CC = %&ret% + 1| CALC #&nSize = %&CC% * 2
  11.     SET$ &ValueName=*%&nSize% 0
  12.     CALL $--qd --ret:&ret Kernel32.dll,GetPrivateProfileStringW,$%&SectionName%,$%&KeyName%,$%&DefValue%,*&ValueName,#%&CC%,$%&FileName%
  13.     ENVI-ret %~5=%&ValueName%
  14.     _END

  15.     ;IniWrite 有四个参数, 第一个参数为目标 .ini 完整文件名, 第二个参数为节名, 第三个参数为键名, 第四个参数为值名, 第五个参数为返回值(若返回零则写入失败)
  16.     ;其中第四个和第五个参数为可选, 当第四个参数留空时, 将移除目标键的值
  17.     _SUB IniWrite
  18.     TEAM ENVI &FileName=%~1| ENVI &SectionName=%~2| ENVI &KeyName=%~3| ENVI &ValueName=%~4
  19.     FIND %&FileName%=,EXIT _SUB
  20.     FIND %&SectionName%=,EXIT _SUB
  21.     FIND %&KeyName%=,EXIT _SUB
  22.     CALL $--qd --ret:&ret Kernel32.dll,WritePrivateProfileStringW,$%&SectionName%,$%&KeyName%,$%&ValueName%,$%&FileName%
  23.     ENVI-ret %~5=%&ret%
  24.     _END
复制代码
PS: mdyblog 老大的 PECMD 的示例代码(readini.wcs)中有读取 Ini 文本文件的封装代码段, 不过挺复杂的


回复

使用道具 举报

3#
 楼主| 发表于 昨天 19:23 | 只看该作者
  1. ;只有一个输入参数和两个输出参数
  2. ;输入参数为路径, 支持驱动器号(盘符), 路径命名空间(\\?\HarddiskVolumeX, \\?\HarddiskX\PartitionX 等)
  3. ;第一个输出参数为设备号, 第二个输出参数为分区号
  4. _SUB GetDevicePartNum
  5.     SET &drv=%~1
  6.     ENVI-ret %~2=
  7.     ENVI-ret %~3=
  8.     SET &prefix=\\?\
  9.     SET &gr=
  10.     LSTR aStr1=4,%drv%
  11.     LSTR aStr2=7,%drv%
  12.     FIND $%aStr1%=\\?\,SET &prefix=
  13.     FIND $%aStr2%=\Device,SET &gr=GLOBALROOT
  14.    
  15.     SET &FILE_ANY_ACCESS=0x00000000
  16.     SET &OPEN_EXISTING=3
  17.     SET &IOCTL_STORAGE_GET_DEVICE_NUMBER=0x2D1080
  18.     SET$# &udtQuery=*4 0 *4 0 *4 0
  19.     ENVI$# &&cbBytesReturned=*8 0
  20.     CALL $--qd --ret:&&h Kernel32.dll,CreateFileW,$%prefix%%gr%%drv%,#0,#%FILE_ANY_ACCESS%,#0,#%OPEN_EXISTING%,#0,#0
  21.     IFEX #%h%=-1, EXIT
  22.     CALL $--qd --ret:&&ret Kernel32.dll,DeviceIoControl,#%h%,#%IOCTL_STORAGE_GET_DEVICE_NUMBER%,#0,#0,*&udtQuery,#12,*&cbBytesReturned,#0 //待处理
  23.     CALL $--qd --ret:&&ret Kernel32.dll,CloseHandle,#%h%
  24.     ENVI?int &udtQuery=&&DeviceNum:4
  25.     ENVI?int &udtQuery=&&PartNum:8
  26.     ENVI-ret %~2=%&DeviceNum%
  27.     ENVI-ret %~3=%&PartNum%
  28. _END

  29. GetDevicePartNum C: &1 &2
  30. GetDevicePartNum \\?\HarddiskVolume2 &3 &4
  31. MESS 设备(磁盘)号: %1%\n分区号: %2%\n\n设备(磁盘)号: %3%\n分区号: %4%
复制代码



回复

使用道具 举报

4#
发表于 昨天 19:30 | 只看该作者
了解
回复

使用道具 举报

5#
发表于 昨天 19:32 | 只看该作者
学习一下
回复

使用道具 举报

6#
发表于 昨天 19:47 来自手机 | 只看该作者
强贴留名
回复

使用道具 举报

7#
发表于 昨天 19:54 | 只看该作者
了解一下。
回复

使用道具 举报

8#
 楼主| 发表于 昨天 20:09 | 只看该作者
  1. ;此封装函数有三个参数, 第一个参数用于存放返回的操作系统主要版本号, 第二个参数用于存放返回的操作系统次要版本号, 第三个参数用于存放返回的操作系统内部版本号
  2. _SUB RtlGetVersion
  3.     SET$# &VersionInformation=*4 0 *4 0 *4 0 *4 0 *4 0 *256 0
  4.     SET-long &VersionInformation=276
  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 SET-long &pMajorVersion=| SET-long &pMinorVersion=| SET-long &pBuildNumber=
  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
复制代码
PS: RtlGetNtVersionNumbers 为非公开函数, 且支持系统版本范围为 NT5.1+; 感谢 wintoflash 老大帮忙处理获取正确的 BuildNumber 值


回复

使用道具 举报

9#
发表于 昨天 20:19 | 只看该作者
技术贴 支持
回复

使用道具 举报

10#
 楼主| 发表于 昨天 20:27 | 只看该作者
  1. ;获取计算机名称, 仅一个参数, 用于存放返回的计算机名称
  2. _SUB GetComputerName
  3.     SET$# &nSize=*4 0
  4.     CALL $--qd --ret:&ret Kernel32.dll,GetComputerNameW,*,*&nSize
  5.     ENVI?int &nSize=&BufferSize
  6.     IFEX $%&BufferSize%=0,EXIT _SUB
  7.     SET$ &lpBuffer=*%&BufferSize% 0
  8.     CALL $--qd --ret:&&ret Kernel32.dll,GetComputerNameW,*&lpBuffer,*&nSize
  9.     IFEX $%&ret%=0,EXIT _SUB
  10.     ENVI-ret %~1=%&lpBuffer%
  11. _END

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

  24. ;调用示例
  25. CALL GetComputerName &ComputerName
  26. CALL GetUserName &UserName
  27. MESS 计算机名称: %&ComputerName%\n\n用户名: %&UserName%
复制代码

回复

使用道具 举报

11#
发表于 昨天 21:05 | 只看该作者
感谢分享!
回复

使用道具 举报

12#
发表于 昨天 21:22 | 只看该作者
谢谢分享
回复

使用道具 举报

13#
发表于 昨天 22:48 | 只看该作者
支持原创
回复

使用道具 举报

14#
发表于 21 小时前 | 只看该作者
学习一下。
回复

使用道具 举报

15#
 楼主| 发表于 2 小时前 | 只看该作者
获取系统(启动)分区
  1. _SUB GetBootDevice
  2.     IFEX #%&bX64%=3, Set &PtrSz=8! SET &PtrSz=4
  3.     CALC #&&Sz=%PtrSz% * 2
  4.     CALC #&&SzA=%Sz% + 8192
  5.     SET$# &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. CALL GetBootDevice &BootDevice
  12. MESS %&BootDevice%
复制代码
PS: 此代码摘自 mdyblog 老大的 PECMD 的示例代码, 且返回信息为 dos 设备路径


回复

使用道具 举报

16#
 楼主| 发表于 2 小时前 | 只看该作者
本帖最后由 Bluebells 于 2026-1-18 19:58 编辑

获取系统当前启动方式
  1. _SUB GetSystemBootEnvironmentInformation
  2.     SET &SystemBootEnvironmentInformation=90
  3.     SET$# &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.         SET?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. CALL GetSystemBootEnvironmentInformation &BootEnvironment
  16. MESS %&BootEnvironment%
复制代码
PS: 一些人根据磁盘的分区样式去判断启动方式是非常不正确的
回复

使用道具 举报

17#
 楼主| 发表于 1 小时前 | 只看该作者
获取 Windows, System 和 Temp 目录
  1. _SUB GetWindowsDirectory
  2.     CALL $--qd --ret:&ret Kernel32.dll,GetWindowsDirectoryW,*&lpBuffer,#0
  3.     SET$ &&lpBuffer=*%&ret% 0
  4.     CALL $--qd --ret:&ret Kernel32.dll,GetWindowsDirectoryW,*&lpBuffer,#%&ret%
  5.     IFEX #%&ret%=0,EXIT
  6.     ENVI-ret %~1=%&lpBuffer%
  7. _END

  8. _SUB GetSystemDirectory
  9.     CALL $--qd --ret:&ret Kernel32.dll,GetSystemDirectoryW,*&lpBuffer,#0
  10.     SET$ &&lpBuffer=*%&ret% 0
  11.     CALL $--qd --ret:&&ret Kernel32.dll,GetSystemDirectoryW,*&lpBuffer,#%&ret%
  12.     IFEX #%&ret%=0,EXIT
  13.     ENVI-ret %~1=%&lpBuffer%
  14. _END

  15. _SUB GetTempPath
  16.     CALL $--qd --ret:&ret Kernel32.dll,GetTempPathW,#0,*&lpBuffer
  17.     SET$ &&lpBuffer=*%&ret% 0
  18.     CALL $--qd --ret:&ret Kernel32.dll,GetTempPathW,#%&ret%,*&lpBuffer
  19.     IFEX #%&ret%=0,EXIT
  20.     ENVI-ret %~1=%&lpBuffer%
  21. _END

  22. CALL GetWindowsDirectory &WinPath
  23. CALL GetSystemDirectory &SystemPath
  24. CALL GetTempPath &TempPath
  25. MESS %&WinPath%\n%&SystemPath%\n%&TempPath%
复制代码
PS: 也许有人说, 为啥不直接用环境变量, 正所谓萝卜青菜,各有所爱



回复

使用道具 举报

18#
 楼主| 发表于 1 小时前 | 只看该作者
获取当前安全启动状态
  1. ;此方法仅支持NT6.2及以上版本, 此函数还能查询目标机器是否支持安全启动功能
  2. _SUB GetSystemSecureBootInformation
  3.     SET$ &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.     SET?char &SSBI=&SecureBootEnabled
  7.     ;SET?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 才能正确调用此函数



回复

使用道具 举报

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

本版积分规则

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

闽公网安备 35020302032614号

GMT+8, 2026-1-18 21:59

Powered by Discuz! X3.3

© 2001-2017 Comsenz Inc.

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