通过


创建包

以下代码示例演示如何:

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()