以下代码示例演示如何:
C#
Windows PowerShell
C#
从项目创建提交包
此示例演示如何从项目创建提交包。
//-----------------------------------------------------------------------
// <copyright file="CreateAPackage.cs" company="Microsoft">
// Copyright © Microsoft Corporation. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Samples
{
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Collections.Generic;
using Microsoft.Windows.Kits.Hardware.ObjectModel;
using Microsoft.Windows.Kits.Hardware.ObjectModel.DBConnection;
using Microsoft.Windows.Kits.Hardware.ObjectModel.Submission;
using System.Collections.Specialized;
using System.IO;
public class CreateAPackage
{
public static void Main(string[] args)
{
Console.WriteLine("Usage: CreateAPackage.exe [ControllerMachineName] [ProjectName] [PathToDrivers] [SaveFileName]");
if (args.Length != 4)
{
Console.WriteLine("Controller Name, Project Name, Path-to-drivers and SaveFile all need to be supplied as an argument.");
return;
}
// first, connect to the controller
string controllerName = args[0];
Console.WriteLine("Connecting to controller : {0}", controllerName);
ProjectManager manager = new DatabaseProjectManager(controllerName);
// next, load the project
string projectName = args[1];
Console.WriteLine("Opening project : {0}", projectName);
Project project;
try
{
project = manager.GetProject(projectName);
}
catch (ProjectManagerException e)
{
Console.WriteLine("could not open project {0} \nError : {1}",
projectName,
e.Message);
return;
}
string driverPath = args[2];
Console.WriteLine("adding drivers found at {0}", driverPath);
string saveFileName = args[3];
Console.WriteLine("saving package to {0}", saveFileName);
// the steps to create a package are:
// 1) create a LogoPackageWriter
// 2) add drivers
// 3) [optional] add supplemental content
// 4) save to disk
PackageWriter packageWriter = new PackageWriter(project);
// packaging can be somewhat slow because it may have a lot of files to read, so compress and write
// the packaging information does have an alerting mechanism
// this will also be alerted for events when processing drivers
packageWriter.SetProgressActionHandler(SubmissionCreationProgressHandler);
// the AddDriver method has this definition.
// public bool AddDriver(
// string pathToDriver,
// string pathToSymbols,
// ReadOnlyCollection<Target> targets,
// ReadOnlyCollection<string> locales,
// out StringCollection errorMessages,
// out StringCollection warningMessages)
// the path to symbols are optional, and can be null
// if there is at least one unreferenced file, that will be noted in warningMessages, along with a path to the unreferenced file log
// the unreferenced file log is a file "{Drive letter}\HLK\JobsWorkingDir\UnreferencedFileLogs\UnreferencedFiles_{timestamp}.txt" generated during every run of AddDriver
//
// there is also a second overloaded AddDriver method with output args for unreferenced file info
// public bool AddDriver(
// string pathToDriver,
// string pathToSymbols,
// ReadOnlyCollection<Target> targets,
// ReadOnlyCollection<string> locales,
// out StringCollection errorMessages,
// out StringCollection warningMessages,
// out bool unreferencedFilePresent,
// out string unreferencedFileLogPath)
// like previous method overload, unreferenced file info will also get added to warningMessages (if there is at least 1 unreferenced file)
// each driver package can be associated with one or more targets, and can be from targets in different product instances
// the possible locales can be retrieved from LogoManager.GetLocaleList()
// when adding a driver, the driver package is validated that it will be signalable, and additional checks will be performed
// this means that if this task fails, errorMessages and warningMessages will be filled out to provide information about any
// problems that are encountered
// for simplicity, add one driver package as received from the command line,
// and associate that with all of the targets in the project
List<Target> targetList = new List<Target>();
foreach (ProductInstance pi in project.GetProductInstances())
{
targetList.AddRange(pi.GetTargets());
}
// also for simplicity, use the first 3 locales returned by GetLocaleList
List<string> localeList = new List<string>();
localeList.Add(ProjectManager.GetLocaleList()[0]);
localeList.Add(ProjectManager.GetLocaleList()[1]);
localeList.Add(ProjectManager.GetLocaleList()[2]);
StringCollection errorMessages;
StringCollection warningMessages;
bool unreferencedFilePresent;
string unreferencedFileLogPath;
// call this method
bool success = packageWriter.AddDriver(driverPath, null, targetList.AsReadOnly(), localeList.AsReadOnly(), out errorMessages, out warningMessages, out unreferencedFilePresent, out unreferencedFileLogPath);
if (!success)
{
Console.WriteLine("Add driver failed to add this driver found at : {0}", driverPath);
foreach (string message in errorMessages)
{
Console.WriteLine("Error: {0}", message);
}
}
// warnings might not cause the method to fail, but may still be present
if (warningMessages.Count != 0)
{
Console.WriteLine("Add driver found warnings in the package found at : {0}", driverPath);
foreach (string message in warningMessages)
{
Console.WriteLine("Warning: {0}", message);
// the recommended way of getting the unreferenced file log path is to use the AddDriver overload (the one used above) that includes this value in output args.
// nevertheless, no matter which AddDriver overload is used, it is possible to get the unreferenced file log path by parsing the warning messages:
//
// if(message.StartsWith("There was at least one file in the driver package that was not referenced by an .inf"))
// {
// int lastSpace = message.LastIndexOf(' ');
// Console.WriteLine("Unreferenced file log path: {0}", message.Substring(lastSpace+1));
// }
}
}
Console.WriteLine("Unreferenced file present: {0}", unreferencedFilePresent); //false if all files are referenced
Console.WriteLine("Unreferenced file log path: {0}", unreferencedFileLogPath);//the log is still produced even if all files are referenced
if(unreferencedFilePresent) {
try
{
// Names of unreferenced files are available in log
using (var reader = new StreamReader(unreferencedFileLogPath))
{
string line;
while ((line = reader.ReadLine()) != null && !line.StartsWith("Unreferenced Files:"));// skip to unreferenced file section
while ((line = reader.ReadLine()) != null && line.Length>0) {
Console.WriteLine("Unreferenced file: {0}", line.Trim());
}
}
}
catch (Exception ex)
{
Console.WriteLine($"Error parsing log {unreferencedFileLogPath}. Details: {ex.Message}");
}
}
// and now call the save as
// this save as does the bulk of the work
// Note: it is possible to save a package even with unreferenced files
packageWriter.Save(saveFileName);
// now that we're done with writing the package, dispose it to make sure the file handle is closed
packageWriter.Dispose();
}
public static void SubmissionCreationProgressHandler(PackageProgressInfo info)
{
Console.WriteLine("Package progress {0} of {1} : {2}", info.Current, info.Maximum, info.Message);
}
}
}
未引用的文件日志(“{驱动器号}\HLK\JobsWorkingDir\UnreferencedFileLogs\UnreferencedFiles_{timestamp}.txt”)包含以下信息:
项目名称
日志创建时间戳
未引用的文件列表
- 如果引用了所有附加文件,以下文本将包含在“未引用的文件”部分:“(无,感谢你引用所有附加文件!)
.inf 引用的文件列表
日志格式示例:
Project Name: Project1
Log Creation: 2025-12-23 15:33:36
Please only include files that are referenced by an .inf in the driver package.
Refer to HLK's Microsoft Learn pages online for more information.
Unreferenced Files:
driverB.sys
driverB.txt
Files expected from .inf:
driverA.cat
driverA.inf
drvSubdirectory\driverA.sys
使用存储在 pfx 文件中的数字签名对包进行签名
此示例演示如何使用存储在 pfx 文件中的数字签名对包进行签名。
namespace HLK
{
using System;
using System.Security.Cryptography.X509Certificates;
using Microsoft.Windows.Kits.Hardware.ObjectModel.Submission;
class Program
{
static void Main(string[] args)
{
string packageFile = args[0];
string pfxFile = args[1];
Console.WriteLine("Enter the password for the pfx file:");
string password = Console.ReadLine();
X509Certificate certificate = new X509Certificate(pfxFile, password);
PackageManager.Sign(packageFile, certificate);
}
}
}
PowerShell
从项目创建提交包
此示例演示如何从项目创建提交包。
$ObjectModel = [Reflection.Assembly]::LoadFrom($env:WTTSTDIO + "microsoft.windows.Kits.Hardware.objectmodel.dll")
$DbConnection = [Reflection.Assembly]::LoadFrom($env:WTTSTDIO + "microsoft.windows.Kits.Hardware.objectmodel.dbconnection.dll")
$Submission = [Reflection.Assembly]::LoadFrom($env:WTTSTDIO + "microsoft.windows.Kits.Hardware.objectmodel.submission.dll")
$Submission = [Reflection.Assembly]::LoadFrom($env:WTTSTDIO + "microsoft.windows.Kits.Hardware.objectmodel.submission.package.dll")
write-Host "Usage: %SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe -file CreateAPackage.ps1 [ControllerMachineName] [ProjectName] [PathToDrivers] [SaveFileName]"
if ($args.count -ne 4)
{
write-host "error: need to provide the controller name, name of the project to package, path to drivers, and save file location "
return
}
$ControllerName = $args[0]
$projectName = $args[1];
$driverPath = $args[2];
$saveFileName = $args[3];
# we need to connect to the Server
if ($ControllerName -eq $null -OR $ControllerName -eq "")
{
write-host "Need to supply the controller Name as a parameter to this script"
return
}
if ($projectName -eq $null -OR $projectName -eq "")
{
write-host "Need to supply the Project Name as a parameter to this script"
return
}
Write-host "connecting to controller : $ControllerName"
$Manager = new-object -typename Microsoft.Windows.Kits.Hardware.ObjectModel.DBConnection.DatabaseProjectManager -Args $ControllerName, HLKJobs
# next load the project
write-host "Opening project :$projectName"
$project = $Manager.GetProject($projectName)
if ($project -eq $null)
{
write-host "unable to load project `n" $error.ToString()
}
write-host "adding drivers found at $driverPath"
write-host "saving package to $saveFileName"
# the steps needed to create a package are:
# 1) create a LogoPackageWriter
# 2) add drivers
# 3) [optional] add supplemental content
# 4) save to disk
$packageWriter = new-object -typename Microsoft.Windows.Kits.Hardware.ObjectModel.Submission.PackageWriter -Args $project
# the AddDriver method has this definition.
# public bool AddDriver(
# string pathToDriver,
# string pathToSymbols,
# ReadOnlyCollection<Target> targets,
# ReadOnlyCollection<string> locales,
# out StringCollection errorMessages,
# out StringCollection warningMessages)
# the path to symbols are optional, and can be null
# if there is at least one unreferenced file, that will be noted in warningMessages, along with a path to the unreferenced file log
# the unreferenced file log is a file "{Drive letter}\HLK\JobsWorkingDir\UnreferencedFileLogs\UnreferencedFiles_{timestamp}.txt" generated during every run of AddDriver
#
# there is also a second overloaded AddDriver method with output args for unreferenced file info
# public bool AddDriver(
# string pathToDriver,
# string pathToSymbols,
# ReadOnlyCollection<Target> targets,
# ReadOnlyCollection<string> locales,
# out StringCollection errorMessages,
# out StringCollection warningMessages,
# out bool unreferencedFilePresent,
# out string unreferencedFileLogPath)
# like previous method overload, unreferenced file info will also get added to warningMessages (if there is at least 1 unreferenced file)
# each driver package can be associated with one or more targets, and can be from targets in different product instances.
# the possible locales can be retrieved from LogoManager.GetLocaleList()
# when adding a driver, the driver package is validated that it will be signal-able, and additional checks will be performed
# this means that if this task fails, the errorMessages and warningMessages will be filled out to provide information about any
# problems encountered
# for simplicity, we are going to add one driver package that we received from the command line,
# and associate that with all of the targets in the project
$targetList = New-Object "System.Collections.Generic.List``1[Microsoft.Windows.Kits.Hardware.ObjectModel.Target]"
$Project.GetProductInstances() | foreach {
$targetlist.AddRange($_.GetTargets())
}
# also for simplicity, we are going to use the first 3 locales returned by the GetLocaleList.
$localeList = New-Object "System.Collections.Generic.List``1[System.string]"
$localeList.Add([Microsoft.Windows.Kits.Hardware.ObjectModel.ProjectManager]::GetLocaleList()[0])
$localeList.Add([Microsoft.Windows.Kits.Hardware.ObjectModel.ProjectManager]::GetLocaleList()[1])
$localeList.Add([Microsoft.Windows.Kits.Hardware.ObjectModel.ProjectManager]::GetLocaleList()[2])
$errorMessages = New-Object "System.Collections.Specialized.StringCollection"
$warningMessages = New-Object "System.Collections.Specialized.StringCollection"
$unreferencedFileLogPath = ""
$unreferencedFilePresent = New-Object "System.Boolean"
# go ahead and call this API
if ($packageWriter.AddDriver($driverPath, [System.Management.Automation.Language.NullString]::Value, $targetList.AsReadOnly(), $localeList.AsReadOnly(), [ref] $errorMessages, [ref] $warningMessages, [ref] $unreferencedFilePresent, [ref] $unreferencedFileLogPath) -eq $false)
{
Write-Host "Add driver failed to add this driver found at : $driverPath"
foreach ($msg in $errorMessages)
{
Write-Host "Error: $msg"
}
}
# warnings might not cause the method to fail, but may still be present
if ($warningMessages.Count -ne 0)
{
Write-Host "Add driver found warnings in the package found at : $driverPath"
foreach ($msg in $warningMessages)
{
Write-Host "Warning: $msg"
# the recommended way of getting the unreferenced file log path is to use the AddDriver overload (the one used above) that includes this value in output args.
# nevertheless, no matter which AddDriver overload is used, it is possible to get the unreferenced file log path by parsing the warning messages:
#
# if ($msg -like 'There was at least one file in the driver package that was not referenced by an .inf*') {
# $lastSpace = $msg.LastIndexOf(' ')
# $unreferencedFileLogPath = $msg.Substring($lastSpace + 1)
# Write-Host "Unreferenced file log path: $unreferencedFileLogPath"
# }
}
}
Write-Host "Unreferenced file present: $unreferencedFilePresent" # false if all files are referenced
Write-Host "Unreferenced file log path: $unreferencedFileLogPath" # the log is still produced even if all files are referenced
if ($unreferencedFilePresent) {
try {
# Names of unreferenced files are available in log
$lines = Get-Content -Path $unreferencedFileLogPath -ErrorAction Stop
# find the header line index
$startIndex = -1
for ($i = 0; $i -lt $lines.Count; $i++) {
if ($lines[$i] -match '^Unreferenced Files:') { $startIndex = $i; break }
}
if ($startIndex -ge 0) {
for ($j = $startIndex + 1; $j -lt $lines.Count; $j++) {
if ([string]::IsNullOrWhiteSpace($lines[$j])) { break }
Write-Host "Unreferenced file: $($lines[$j].Trim())"
}
}
}
catch {
Write-Host "Error parsing log $unreferencedFileLogPath. Details: $($_.Exception.Message)"
}
}
# Since packaging can be somewhat slow, as it may have a lot of files to read, compress and write
# the packaging information does have an alerting mechanism
# This is not implemented in the PowerShell example, but it is implemented in the C# version
# packageWriter.SetProgressActionHandler(SubmissionCreationProgressHandler);
# and now call the save as
# this save as does the bulk of the work
# Note: it is possible to save a package even with unreferenced files
$packageWriter.Save($saveFileName);
# now that we're done with writing the package, dispose it to make sure the file handle is closed
$packageWriter.Dispose();
使用存储在 pfx 文件中的数字签名对包进行签名
此示例演示如何使用存储在 pfx 文件中的数字签名对包进行签名。
# Load DLLs.
$ObjectModel = [Reflection.Assembly]::LoadFrom($env:WTTSTDIO + "Microsoft.Windows.Kits.Hardware.objectmodel.dll")
$ObjectModel = [Reflection.Assembly]::LoadFrom($env:WTTSTDIO + "Microsoft.Windows.Kits.Hardware.objectmodel.submission.dll")
Write-Host "Usage: %SystemRoot%\System32\WindowsPowershell\v1.0\powershell.exe -file sign_package.ps1 <<Full Path To Package>> <<Full Path to PFX>>"
# The package to sign.
$packageFile = $args[0]
# The pfx file to use.
$pfxfile = $args[1]
# The password for the pfx file.
$pfxpassword = Read-host "Please enter the password for the pfx file" -AsSecureString
# Load the certificate.
$cert = new-object -typename System.Security.Cryptography.X509Certificates.X509Certificate($pfxfile, $pfxpassword)
# Sign the package.
[Microsoft.Windows.Kits.Hardware.ObjectModel.Submission.PackageManager]::Sign($packageFile, $cert)
注释
使用 PowerShell 对某些(大型)包进行签名时,可能会看到异常,例如“无法确定域的标识”。 发生此异常时,请使用托管 API(请参阅 https://msdn.microsoft.com/library/windows/hardware/jj123504.aspx#BKMK_CS_SignPackage)作为解决方法。
确定哪些操作系统的驱动程序通过签名检查
该示例演示如何确定驱动程序在什么操作系统上能通过签名验证。
# Load DLLs.
$ObjectModel = [Reflection.Assembly]::LoadFrom($env:WTTSTDIO + "Microsoft.Windows.Kits.Hardware.objectmodel.dll")
$ObjectModel = [Reflection.Assembly]::LoadFrom($env:WTTSTDIO + "Microsoft.Windows.Kits.Hardware.objectmodel.submission.dll")
Write-Host "Usage: %SystemRoot%\System32\WindowsPowershell\v1.0\powershell.exe -file check_downlevel_os.ps1 <<Full Path To Package>>"
# The package file.
$packageFile = $args[0]
# Create a package manager.
$Manager = new-object -typename Microsoft.Windows.Kits.Hardware.ObjectModel.Submission.PackageManager -Args $packageFile
$Manager.GetProjectNames() | foreach {
Write-Host Examining Project $_
$Project = $Manager.GetProject($_)
$Project.GetProductInstances() | foreach {
$_.GetTargetFamilies() | foreach {
$_.GetTargets() | foreach {
Write-Host Examining Target $_.Name
$_.GetDrivers() | foreach {
Write-Host Driver is signable for the following platforms
Write-Host $_.SignabilityResults
}
}
}
}
}
# Clean up.
$Manager.Dispose()
验证数字证书
此示例演示如何验证数字证书。
# Load DLLs.
$ObjectModel = [Reflection.Assembly]::LoadFrom($env:WTTSTDIO + "Microsoft.Windows.Kits.Hardware.objectmodel.dll")
$ObjectModel = [Reflection.Assembly]::LoadFrom($env:WTTSTDIO + "Microsoft.Windows.Kits.Hardware.objectmodel.submission.dll")
Write-Host "Usage: %SystemRoot%\System32\WindowsPowershell\v1.0\powershell.exe -file verify_certificate.ps1 <<Full Path To Package>>"
# The package file.
$packageFile = $args[0]
# Create a package manager.
$Manager = new-object -typename Microsoft.Windows.Kits.Hardware.ObjectModel.Submission.PackageManager -Args $packageFile
# Write out the certificate object.
Write-host $Manager.Certificate
# Clean Up.
$Manager.Dispose()