1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Windows 11をクリーンインストールしたらドライバが不足したときに同型機からPnPUtilで抽出・復元する実践手順

1
Posted at

検証日:2026年8月22日
検証環境:Windows 11 Pro x64、Windows PowerShell 5.1
実機例:AMD Ryzen 7 5700U搭載のAskHandミニPC
想定読者:メーカー配布ドライバーが見つからないPCを再セットアップする管理者

はじめに

小規模メーカーやホワイトボックス系のミニPCでは、Microsoft公式イメージからWindows 11をクリーンインストールすると、次のようなドライバーが不足することがあります。

  • 有線LAN
  • Wi-Fi
  • Bluetooth
  • AMD/Intelグラフィックス
  • オーディオ
  • USBコントローラー
  • PCIデバイス
  • ACPIデバイス

メーカーが機種別ドライバーセットを公開していない場合、Web上から「似た型番」のドライバーを探す方法は安全とはいえません。同じ筐体名でも、製造ロットによって無線LANや有線LANのチップが異なる場合があるためです。

今回、次の条件がそろっていました。

  • Microsoft公式USBからWindows 11 Proを新規インストールした対象機がある
  • 対象機には複数のドライバーが不足している
  • Windows 11 Proが正常動作している同型機が別にある
  • メーカー配布の完全なドライバーセットは確認できない

そこで、正常動作している同型機のDriver Storeから、現在使用されている署名済みOEMドライバーパッケージを抽出し、新規インストール機へ移植しました。

Windows標準の PnPUtil.exe だけで実行できます。

この記事では、単純な全件コピーではなく、次も考慮します。

  • BIOS/UEFI/ECファームウェアを移植対象から除外する
  • 移植元と移植先のハードウェアを記録する
  • 抽出ファイルのSHA-256を保存する
  • 導入前後の問題デバイスを記録する
  • Windows PowerShell 5.1のUTF-8 BOM問題を回避する
  • 問題発生時に特定のドライバーだけを戻せるようにする

結論

全体の流れは次のとおりです。

正常動作している同型機
        |
        | 1. ハードウェア情報を取得
        | 2. 現在使用中のOEM INFを特定
        | 3. Firmware/UEFIを除外
        | 4. PnPUtilでドライバーを抽出
        v
USBメモリ
        |
        | 5. SHA-256で整合性確認
        v
Windows 11新規インストール機
        |
        | 6. PnPUtilでDriver Storeへ追加
        | 7. 一致するデバイスへインストール
        | 8. 再起動
        | 9. Windows Update
        v
問題デバイスがないことを確認

使用する中心コマンドは次の2つです。

pnputil.exe /export-driver <oem#.inf> <出力先>
pnputil.exe /add-driver "<ドライバーディレクトリ>\*.inf" /subdirs /install

ただし、pnputil.exe /export-driver * で全件抽出したものを無条件にインストールすると、Firmwareクラスのパッケージまで含まれる可能性があります。

この記事では、FirmwareクラスとUEFIデバイスに関連するパッケージを除外してから抽出します。

作業範囲と前提条件

項目 内容
実行者 Windowsのローカル管理者
移植元 Windows 11が正常動作している同型機。この記事では SOURCE-PC と表記
移植先 Windows 11を新規インストール済みでドライバーが不足している機体。この記事では TARGET-PC と表記
実行環境 Windows 11 Pro x64
使用シェル Windows PowerShell 5.1
PowerShell実体 C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
USBボリュームラベル DRIVERTRANSFER
USB保存先 <USBドライブ>:\Windows-Driver-Transfer
移植先のローカル保存先 C:\Drivers\Windows-Driver-Transfer\Current
変更対象 TARGET-PC のWindows Driver Storeおよびデバイスドライバー
変更禁止対象 BIOS、UEFI、EC、SSDファームウェア、パーティション、Windowsライセンス
対象branch 該当なし
対象PR 該当なし
実行時点 Windows新規インストール後、業務データや主要アプリを導入する前
期待結果 デバイスマネージャーの不明なデバイスが解消し、ネットワーク、映像、音声、Bluetoothなどが動作する

この方法で移行するもの、移行しないもの

移行するもの

Windows Driver Storeに登録されている、現在のデバイスに関連付けられた第三者製ドライバーパッケージです。

代表例は次のとおりです。

  • AMD/Intel/NVIDIAグラフィックスドライバー
  • Realtek/Intel/MediaTekなどのネットワークドライバー
  • Bluetoothドライバー
  • オーディオドライバー
  • チップセット関連ドライバー
  • USB、GPIO、I2C、ACPI関連ドライバー

移行しないもの

次は対象外です。

  • Windows本体
  • インストール済みアプリケーション
  • ユーザーデータ
  • Windowsライセンス
  • BIOS設定
  • BIOS/UEFI更新
  • ECファームウェア
  • SSDファームウェア
  • Microsoft標準のin-boxドライバー

PnPUtil が列挙するのは、原則としてWindowsに標準搭載されていない第三者製ドライバーパッケージです。Windows標準ドライバーは、Microsoft公式Windows 11イメージ側に含まれています。

なぜメーカー名や型番ではなくハードウェアIDを見るのか

同じ販売型番でも、次の部品がロットによって異なる場合があります。

  • Wi-Fiモジュール
  • Bluetoothモジュール
  • 有線LANコントローラー
  • オーディオCodec
  • USBコントローラー
  • NVMe SSD

そのため、次の順で一致を確認します。

  1. PCのメーカー名とモデル名
  2. ベースボードの製品名とリビジョン
  3. デバイスのハードウェアID
  4. 使用中のINFファイル
  5. ドライバープロバイダーとバージョン

販売ページの製品名だけを根拠に、別メーカーのドライバーやBIOSを導入してはいけません。

手順1:移植元と移植先のハードウェアを確認する

SOURCE-PCTARGET-PC の両方で、Windows PowerShellを管理者として起動します。

実行ファイルは次です。

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe

次を実行します。

Get-CimInstance Win32_ComputerSystem |
    Select-Object Manufacturer, Model, SystemSKUNumber, Name |
    Format-List

Get-CimInstance Win32_BaseBoard |
    Select-Object Manufacturer, Product, Version, SerialNumber |
    Format-List

Get-CimInstance Win32_BIOS |
    Select-Object SMBIOSBIOSVersion, Version, ReleaseDate |
    Format-List

ManufacturerModel が空欄、または汎用文字列になっているミニPCもあります。その場合は、Win32_BaseBoardProduct と、後述するハードウェアIDを重視します。

TARGET-PC では、問題デバイスも確認します。

pnputil.exe /enum-devices /problem /deviceids

または、デバイスマネージャーを開きます。

devmgmt.msc

手順2:USBメモリを準備する

8GB以上のUSBメモリを使用します。

USBメモリ内の既存データは、あらかじめ別の場所へ退避してください。

USBメモリのボリュームラベルを次に設定します。

DRIVERTRANSFER

エクスプローラーから設定しても構いません。

PowerShellから設定する場合、USBメモリが E: であることを確認したうえで実行します。

Set-Volume -DriveLetter E -NewFileSystemLabel 'DRIVERTRANSFER'

ドライブ文字だけで対象を決めず、必ず次で確認します。

Get-Volume |
    Select-Object DriveLetter, FileSystemLabel, FileSystem, HealthStatus |
    Format-Table -AutoSize

期待する状態は、DRIVERTRANSFER というラベルのドライブが1件だけ存在することです。

手順3:Windows PowerShellのバージョンを確認する

SOURCE-PC で実行します。

$PSVersionTable |
    Select-Object PSVersion, PSEdition |
    Format-List

Windows 11に標準搭載されているWindows PowerShellでは、通常、次のように表示されます。

PSVersion : 5.1.xxxxx.xxxx
PSEdition : Desktop

この記事のスクリプトはWindows PowerShell 5.1で動作するようにしています。

また、コードブロック内のコメントと文字列をASCII文字だけにしています。これは後述するUTF-8 BOM問題を避けるためです。

手順4:正常動作機からドライバーを抽出する

4.1 スクリプトファイルを作成する

SOURCE-PC の管理者PowerShellで実行します。

New-Item -ItemType Directory -Force -Path 'C:\Temp' | Out-Null
notepad.exe 'C:\Temp\Export-DriverPackages.ps1'

次のスクリプトを貼り付けて保存します。

スクリプト内の文字列はASCIIだけで構成しているため、UTF-8 BOMなしでも文字化けしません。ただし、Windows PowerShell 5.1で継続的に管理する場合は、UTF-8 BOM付きで保存することを推奨します。

# C:\Temp\Export-DriverPackages.ps1
#requires -RunAsAdministrator

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

$UsbLabel = 'DRIVERTRANSFER'
$FirmwareClassGuid = '{F2E7DD72-6468-4E36-B6F1-6488F42C1B52}'
$PnpUtil = Join-Path $env:SystemRoot 'System32\pnputil.exe'

$volumes = @(
    Get-Volume -FileSystemLabel $UsbLabel -ErrorAction Stop |
        Where-Object { $_.DriveLetter }
)

if ($volumes.Count -ne 1) {
    throw "USB volume '$UsbLabel' was not found uniquely. Count=$($volumes.Count)"
}

$usbDrive = '{0}:' -f $volumes[0].DriveLetter
$root = Join-Path $usbDrive 'Windows-Driver-Transfer'
$driverRoot = Join-Path $root 'Drivers'
$inventoryRoot = Join-Path $root 'Inventory'
$logRoot = Join-Path $root 'Logs'

if (Test-Path -LiteralPath $root) {
    $archive = "${root}.previous.$(Get-Date -Format 'yyyyMMdd-HHmmss')"
    Move-Item -LiteralPath $root -Destination $archive
}

New-Item -ItemType Directory -Force -Path (
    $driverRoot,
    $inventoryRoot,
    $logRoot
) | Out-Null

Get-CimInstance Win32_ComputerSystem |
    Select-Object Manufacturer, Model, SystemSKUNumber, Name |
    Format-List |
    Out-File (
        Join-Path $inventoryRoot 'ComputerSystem.txt'
    ) -Encoding utf8 -Width 4096

Get-CimInstance Win32_BaseBoard |
    Select-Object Manufacturer, Product, Version, SerialNumber |
    Format-List |
    Out-File (
        Join-Path $inventoryRoot 'BaseBoard.txt'
    ) -Encoding utf8 -Width 4096

Get-CimInstance Win32_BIOS |
    Select-Object SMBIOSBIOSVersion, Version, ReleaseDate, SerialNumber |
    Format-List |
    Out-File (
        Join-Path $inventoryRoot 'BIOS.txt'
    ) -Encoding utf8 -Width 4096

Get-CimInstance Win32_OperatingSystem |
    Select-Object Caption, Version, BuildNumber, OSArchitecture, InstallDate |
    Format-List |
    Out-File (
        Join-Path $inventoryRoot 'OperatingSystem.txt'
    ) -Encoding utf8 -Width 4096

$signedDrivers = @(
    Get-CimInstance Win32_PnPSignedDriver
)

$signedDrivers |
    Select-Object (
        'DeviceName',
        'FriendlyName',
        'DeviceClass',
        'ClassGuid',
        'DeviceID',
        'HardWareID',
        'Manufacturer',
        'DriverProviderName',
        'DriverVersion',
        'DriverDate',
        'InfName',
        'IsSigned',
        'Signer',
        'Status'
    ) |
    Sort-Object DeviceClass, DeviceName |
    Export-Csv (
        Join-Path $inventoryRoot 'PnPSignedDrivers.csv'
    ) -NoTypeInformation -Encoding utf8

& $PnpUtil /enum-devices /connected /deviceids 2>&1 |
    Out-File (
        Join-Path $inventoryRoot 'ConnectedDevices.txt'
    ) -Encoding utf8 -Width 4096

& $PnpUtil /enum-drivers /files 2>&1 |
    Out-File (
        Join-Path $inventoryRoot 'DriverStore.txt'
    ) -Encoding utf8 -Width 4096

$groups = @(
    $signedDrivers |
        Where-Object {
            $_.InfName -match '^oem\d+\.inf$'
        } |
        Group-Object InfName |
        Sort-Object Name
)

if ($groups.Count -eq 0) {
    throw 'No active OEM INF package was found.'
}

$manifest = @(
    foreach ($group in $groups) {
        $members = @($group.Group)

        $isFirmware = @(
            $members |
                Where-Object {
                    $_.DeviceClass -ieq 'FIRMWARE' -or
                    $_.ClassGuid -ieq $FirmwareClassGuid -or
                    $_.DeviceID -like 'UEFI\*'
                }
        ).Count -gt 0

        $isUnsigned = @(
            $members |
                Where-Object {
                    $_.IsSigned -eq $false
                }
        ).Count -gt 0

        $exportAllowed = -not $isFirmware -and -not $isUnsigned
        $skipReason = ''

        if ($isFirmware) {
            $skipReason = 'Firmware or UEFI package'
        }
        elseif ($isUnsigned) {
            $skipReason = 'Unsigned package'
        }

        [pscustomobject]@{
            InfName = $group.Name
            ExportAllowed = $exportAllowed
            SkipReason = $skipReason
            DeviceClasses = (
                $members |
                    ForEach-Object { $_.DeviceClass } |
                    Where-Object { $_ } |
                    Sort-Object -Unique
            ) -join ';'
            DeviceNames = (
                $members |
                    ForEach-Object { $_.DeviceName } |
                    Where-Object { $_ } |
                    Sort-Object -Unique
            ) -join ';'
            Providers = (
                $members |
                    ForEach-Object { $_.DriverProviderName } |
                    Where-Object { $_ } |
                    Sort-Object -Unique
            ) -join ';'
            DriverVersions = (
                $members |
                    ForEach-Object { $_.DriverVersion } |
                    Where-Object { $_ } |
                    Sort-Object -Unique
            ) -join ';'
        }
    }
)

$manifest |
    Export-Csv (
        Join-Path $inventoryRoot 'PackageManifest.csv'
    ) -NoTypeInformation -Encoding utf8

$exportLog = Join-Path $logRoot 'DriverExport.log'
Set-Content -LiteralPath $exportLog -Value '' -Encoding utf8

$exportFailures = @()

foreach (
    $package in @(
        $manifest |
            Where-Object { $_.ExportAllowed }
    )
) {
    $infName = [string]$package.InfName

    "===== Export: $infName =====" |
        Out-File $exportLog -Append -Encoding utf8

    $output = & $PnpUtil /export-driver $infName $driverRoot 2>&1
    $exitCode = $LASTEXITCODE

    $output |
        Out-File $exportLog -Append -Encoding utf8 -Width 4096

    "ExitCode=$exitCode" |
        Out-File $exportLog -Append -Encoding utf8

    if ($exitCode -ne 0) {
        $exportFailures += $infName
    }
}

$infFiles = @(
    Get-ChildItem -LiteralPath $driverRoot -Recurse -File -Filter '*.inf'
)

if ($infFiles.Count -eq 0) {
    throw "No INF file was exported. See $exportLog"
}

$hashManifest = @(
    foreach (
        $file in Get-ChildItem -LiteralPath $driverRoot -Recurse -File
    ) {
        $relativePath = $file.FullName.Substring($root.Length + 1)

        [pscustomobject]@{
            RelativePath = $relativePath
            Length = $file.Length
            SHA256 = (
                Get-FileHash -LiteralPath $file.FullName -Algorithm SHA256
            ).Hash
        }
    }
)

$hashManifest |
    Export-Csv (
        Join-Path $inventoryRoot 'SHA256.csv'
    ) -NoTypeInformation -Encoding utf8

if ($exportFailures.Count -gt 0) {
    $failurePath = Join-Path $logRoot 'ExportFailures.txt'

    $exportFailures |
        Set-Content -LiteralPath $failurePath -Encoding utf8

    throw "Some packages failed to export. See $failurePath"
}

$readyContent = @(
    "SourceComputer=$env:COMPUTERNAME"
    "ExportDate=$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
    "ExportedInfCount=$($infFiles.Count)"
    "DriverRoot=$driverRoot"
) -join [System.Environment]::NewLine

Set-Content -LiteralPath (
    Join-Path $root 'READY.txt'
) -Value $readyContent -Encoding utf8

Write-Host ''
Write-Host 'Driver export completed.'
Write-Host "Output: $root"
Write-Host "INF count: $($infFiles.Count)"

4.2 構文検査を行う

スクリプトを実行する前に、PowerShellのパーサーで検査します。

$Path = 'C:\Temp\Export-DriverPackages.ps1'
$Tokens = $null
$ParseErrors = $null

$null = [System.Management.Automation.Language.Parser]::ParseFile(
    $Path,
    [ref]$Tokens,
    [ref]$ParseErrors
)

if (@($ParseErrors).Count -eq 0) {
    Write-Host 'Parser check: OK'
}
else {
    $ParseErrors |
        ForEach-Object {
            '{0}:{1} {2}' -f (
                $_.Extent.StartLineNumber,
                $_.Extent.StartColumnNumber,
                $_.Message
            )
        }
}

期待結果は次です。

Parser check: OK

4.3 抽出スクリプトを実行する

& 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe' `
    -NoProfile `
    -ExecutionPolicy Bypass `
    -File 'C:\Temp\Export-DriverPackages.ps1'

期待結果は次です。

Driver export completed.
Output: E:\Windows-Driver-Transfer
INF count: 20

INF数はPCによって異なります。

USBメモリには次の構成が作成されます。

<USBドライブ>:\Windows-Driver-Transfer
├─ Drivers
├─ Inventory
│  ├─ BaseBoard.txt
│  ├─ BIOS.txt
│  ├─ ComputerSystem.txt
│  ├─ ConnectedDevices.txt
│  ├─ DriverStore.txt
│  ├─ OperatingSystem.txt
│  ├─ PackageManifest.csv
│  ├─ PnPSignedDrivers.csv
│  └─ SHA256.csv
├─ Logs
│  └─ DriverExport.log
└─ READY.txt

READY.txt が存在することを確認します。

$volume = Get-Volume -FileSystemLabel 'DRIVERTRANSFER'
$root = '{0}:\Windows-Driver-Transfer' -f $volume.DriveLetter

Get-Content -LiteralPath (
    Join-Path $root 'READY.txt'
)

手順5:新規インストール機へドライバーを導入する

5.1 導入前にネットワークを切断する

TARGET-PC で、導入作業中にWindows Updateが別のドライバーを同時適用しないようにします。

  • Ethernetケーブルを抜く
  • Wi-Fiが動作している場合はWi-Fiをオフにする

USBメモリ DRIVERTRANSFER を接続します。

5.2 インストールスクリプトを作成する

管理者PowerShellで実行します。

New-Item -ItemType Directory -Force -Path 'C:\Temp' | Out-Null
notepad.exe 'C:\Temp\Import-DriverPackages.ps1'

次を保存します。

# C:\Temp\Import-DriverPackages.ps1
#requires -RunAsAdministrator

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

$UsbLabel = 'DRIVERTRANSFER'
$PnpUtil = Join-Path $env:SystemRoot 'System32\pnputil.exe'

$volumes = @(
    Get-Volume -FileSystemLabel $UsbLabel -ErrorAction Stop |
        Where-Object { $_.DriveLetter }
)

if ($volumes.Count -ne 1) {
    throw "USB volume '$UsbLabel' was not found uniquely. Count=$($volumes.Count)"
}

$usbDrive = '{0}:' -f $volumes[0].DriveLetter
$sourceRoot = Join-Path $usbDrive 'Windows-Driver-Transfer'
$readyPath = Join-Path $sourceRoot 'READY.txt'

if (-not (Test-Path -LiteralPath $readyPath -PathType Leaf)) {
    throw "READY file was not found: $readyPath"
}

$localBase = 'C:\Drivers\Windows-Driver-Transfer'
$localCurrent = Join-Path $localBase 'Current'

New-Item -ItemType Directory -Force -Path $localBase | Out-Null

if (Test-Path -LiteralPath $localCurrent) {
    $previous = Join-Path (
        $localBase
    ) (
        "Previous-$(Get-Date -Format 'yyyyMMdd-HHmmss')"
    )

    Move-Item -LiteralPath $localCurrent -Destination $previous
}

New-Item -ItemType Directory -Force -Path $localCurrent | Out-Null

Copy-Item -Path (
    Join-Path $sourceRoot '*'
) -Destination $localCurrent -Recurse -Force

$localDriverRoot = Join-Path $localCurrent 'Drivers'
$localInventoryRoot = Join-Path $localCurrent 'Inventory'
$localLogRoot = Join-Path $localCurrent 'Target-Logs'

New-Item -ItemType Directory -Force -Path $localLogRoot | Out-Null

$hashPath = Join-Path $localInventoryRoot 'SHA256.csv'

if (-not (Test-Path -LiteralPath $hashPath -PathType Leaf)) {
    throw "Hash manifest was not found: $hashPath"
}

$hashEntries = @(Import-Csv -LiteralPath $hashPath)
$hashErrors = @()

foreach ($entry in $hashEntries) {
    $localFile = Join-Path $localCurrent $entry.RelativePath

    if (-not (Test-Path -LiteralPath $localFile -PathType Leaf)) {
        $hashErrors += "Missing: $localFile"
        continue
    }

    $actualHash = (
        Get-FileHash -LiteralPath $localFile -Algorithm SHA256
    ).Hash

    if ($actualHash -ne $entry.SHA256) {
        $hashErrors += "HashMismatch: $localFile"
    }
}

if ($hashErrors.Count -gt 0) {
    $hashErrorPath = Join-Path $localLogRoot 'HashErrors.txt'

    $hashErrors |
        Set-Content -LiteralPath $hashErrorPath -Encoding utf8

    throw "Hash verification failed. See $hashErrorPath"
}

& $PnpUtil /enum-devices /problem /deviceids 2>&1 |
    Out-File (
        Join-Path $localLogRoot 'Problems-BeforeImport.txt'
    ) -Encoding utf8 -Width 4096

& $PnpUtil /enum-drivers /files 2>&1 |
    Out-File (
        Join-Path $localLogRoot 'DriverStore-BeforeImport.txt'
    ) -Encoding utf8 -Width 4096

$importLog = Join-Path $localLogRoot 'DriverImport.log'

$importOutput = & $PnpUtil (
    '/add-driver'
) (
    "$localDriverRoot\*.inf"
) (
    '/subdirs'
) (
    '/install'
) 2>&1

$importExitCode = $LASTEXITCODE

$importOutput |
    Out-File $importLog -Encoding utf8 -Width 4096

"ExitCode=$importExitCode" |
    Out-File $importLog -Append -Encoding utf8

& $PnpUtil /scan-devices 2>&1 |
    Out-File (
        Join-Path $localLogRoot 'ScanDevices.log'
    ) -Encoding utf8 -Width 4096

& $PnpUtil /enum-devices /problem /deviceids 2>&1 |
    Out-File (
        Join-Path $localLogRoot 'Problems-AfterImport-BeforeReboot.txt'
    ) -Encoding utf8 -Width 4096

if ($importExitCode -ne 0) {
    Write-Warning "PnPUtil returned ExitCode=$importExitCode"
    Write-Warning "Check: $importLog"
}
else {
    Write-Host 'Driver import completed.'
}

Write-Host "Local archive: $localCurrent"
Write-Host "Import log: $importLog"
Write-Host 'Restart Windows after checking the log.'

5.3 構文検査を行う

$Path = 'C:\Temp\Import-DriverPackages.ps1'
$Tokens = $null
$ParseErrors = $null

$null = [System.Management.Automation.Language.Parser]::ParseFile(
    $Path,
    [ref]$Tokens,
    [ref]$ParseErrors
)

if (@($ParseErrors).Count -eq 0) {
    Write-Host 'Parser check: OK'
}
else {
    $ParseErrors |
        ForEach-Object {
            '{0}:{1} {2}' -f (
                $_.Extent.StartLineNumber,
                $_.Extent.StartColumnNumber,
                $_.Message
            )
        }
}

5.4 インストールスクリプトを実行する

& 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe' `
    -NoProfile `
    -ExecutionPolicy Bypass `
    -File 'C:\Temp\Import-DriverPackages.ps1'

期待結果は次です。

Driver import completed.
Local archive: C:\Drivers\Windows-Driver-Transfer\Current
Import log: C:\Drivers\Windows-Driver-Transfer\Current\Target-Logs\DriverImport.log
Restart Windows after checking the log.

ログを確認します。

Get-Content -LiteralPath (
    'C:\Drivers\Windows-Driver-Transfer\Current\Target-Logs\DriverImport.log'
)

手順6:Windowsを再起動する

作業中のファイルを閉じてから実行します。

Restart-Computer -Force

手順7:再起動後に確認する

問題デバイスの確認

管理者PowerShellで実行します。

pnputil.exe /enum-devices /problem /deviceids

問題がなければ、問題デバイスは表示されません。

PowerShellからも確認できます。

Get-PnpDevice -PresentOnly |
    Where-Object {
        $_.Status -ne 'OK'
    } |
    Select-Object Status, Class, FriendlyName, InstanceId |
    Format-Table -AutoSize

グラフィックスの確認

Get-CimInstance Win32_VideoController |
    Select-Object Name, DriverVersion, Status |
    Format-Table -AutoSize

AMD Ryzen 7 5700Uの場合、正常に認識されると、概ね次のように表示されます。

AMD Radeon(TM) Graphics

次の表示だけの場合、専用グラフィックスドライバーが不足しています。

Microsoft Basic Display Adapter

ネットワークの確認

Get-NetAdapter -IncludeHidden |
    Select-Object Name, InterfaceDescription, Status, LinkSpeed |
    Format-Table -AutoSize

次を確認します。

  • Ethernetアダプターが存在する
  • Wi-Fiアダプターが存在する
  • Bluetooth関連デバイスが存在する
  • Ethernet接続時にリンク速度が表示される

デバイスマネージャーの確認

devmgmt.msc

黄色い警告アイコンや「不明なデバイス」が残っていないことを確認します。

手順8:Windows Updateを実行する

移植ドライバーでネットワークが動作するようになったら、ネットワークへ接続します。

次を実行します。

設定
  → Windows Update
  → 更新プログラムのチェック

更新、再起動、再確認を、更新がなくなるまで繰り返します。

PnPUtil /add-driver ... /install は、追加したドライバーがWindowsのドライバーランキングで最上位でない場合、既存ドライバーを無条件に置き換えません。

そのため、次の流れが適切です。

同型機から移植
  → 不足デバイスを認識させる
  → Windows Update
  → より適切な更新版があれば更新

ただし、機種や基板リビジョンが明確でないミニPCでは、Windows Updateの「オプションの更新プログラム」に次が表示されても、無条件には適用しません。

Firmware
System Firmware
BIOS
UEFI
EC
SSD Firmware

ドライバーとファームウェアは別の変更として管理します。

発生したトラブル:Windows PowerShell 5.1で日本語が文字化けし、ParserErrorになる

当初作成したスクリプトには、日本語のコメントやエラーメッセージが含まれていました。

そのファイルをUTF-8 BOMなしで保存し、Windows PowerShell 5.1で実行すると、次のようなエラーになりました。

発生場所 C:\Temp\Export-DriverPackages.ps1:126 文字:14
+             '譛ェ鄂イ蜷阪→蝣ア蜻翫&繧後◆縺溘a髯、螟・
+              ~~~~~~~~~~~~~~~~~~~~~~
式またはステートメントのトークンを使用できません。

FullyQualifiedErrorId : UnexpectedToken

これは単なるコンソール上の文字化けではありません。

PowerShellがソースコード内の日本語を誤った文字コードで読み、文字列終端の引用符や周辺バイトまで別の文字として解釈したため、スクリプト構文そのものが壊れています。

原因

Windows PowerShell 5.1は、BOMがないスクリプトファイルについて、UTF-8ではなく従来のANSIコードページであると解釈する場合があります。

日本語環境では、UTF-8のバイト列をCP932系の文字として解釈し、次のような文字化けになります。

未署名と報告されたため除外
譛ェ鄂イ蜷阪→蝣ア蜻翫&繧後◆縺溘a髯、螟・

PowerShell 6以降ではBOMなしUTF-8が標準ですが、Windows PowerShell 5.1とは動作が異なります。

chcp 65001 では直らない

次を実行しても、スクリプトファイルの読み取り方式は修正されません。

chcp 65001

chcp が変更するのは主にコンソール入出力のコードページです。

.ps1 ファイルをPowerShellエンジンがどの文字コードとして読むかは、別の問題です。

恒久的な対策

Windows PowerShell 5.1で日本語を含むスクリプトを実行する場合は、次のいずれかを使用します。

  1. UTF-8 BOM付きで保存する
  2. UTF-16LEで保存する
  3. スクリプト内をASCII文字だけにする
  4. PowerShell 7の pwsh.exe を使用する

この記事のドライバー抽出/導入スクリプトは、コピー時の事故を減らすためASCII文字だけで構成しています。

UTF-8 BOMを追加する修正スクリプト

既存の .ps1 がUTF-8 BOMなしで保存されており、ファイル自体は正しいUTF-8である場合は、次でBOMを追加できます。

実行対象例:

C:\Temp\Export-DriverPackages.ps1

管理者PowerShellで実行します。

$Path = 'C:\Temp\Export-DriverPackages.ps1'
$Backup = "${Path}.before-encoding-fix"

if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) {
    throw "File not found: $Path"
}

Copy-Item -LiteralPath $Path -Destination $Backup -Force

[byte[]]$bytes = [System.IO.File]::ReadAllBytes($Path)

$hasUtf8Bom = (
    $bytes.Length -ge 3 -and
    $bytes[0] -eq 0xEF -and
    $bytes[1] -eq 0xBB -and
    $bytes[2] -eq 0xBF
)

if (-not $hasUtf8Bom) {
    $utf8Strict = [System.Text.UTF8Encoding]::new($false, $true)

    try {
        $text = $utf8Strict.GetString($bytes)
    }
    catch {
        throw "The file is not valid UTF-8. Backup: $Backup"
    }

    $utf8Bom = [System.Text.UTF8Encoding]::new($true)

    [System.IO.File]::WriteAllText(
        $Path,
        $text,
        $utf8Bom
    )
}

[byte[]]$header = [System.IO.File]::ReadAllBytes($Path)

if ($header.Length -lt 3) {
    throw 'The script file is empty or too short.'
}

$headerText = '{0:X2} {1:X2} {2:X2}' -f (
    $header[0],
    $header[1],
    $header[2]
)

Write-Host "Header: $headerText"

$tokens = $null
$parseErrors = $null

$null = [System.Management.Automation.Language.Parser]::ParseFile(
    $Path,
    [ref]$tokens,
    [ref]$parseErrors
)

if (@($parseErrors).Count -ne 0) {
    $parseErrors |
        ForEach-Object {
            Write-Host (
                'Line {0}, Column {1}: {2}' -f (
                    $_.Extent.StartLineNumber,
                    $_.Extent.StartColumnNumber,
                    $_.Message
                )
            )
        }

    throw 'Parser check failed.'
}

Write-Host 'Parser check: OK'
Write-Host "Backup: $Backup"

期待結果は次です。

Header: EF BB BF
Parser check: OK
Backup: C:\Temp\Export-DriverPackages.ps1.before-encoding-fix

EF BB BF がUTF-8 BOMです。

エラー時の分岐

USBメモリが見つからない

エラー例:

USB volume 'DRIVERTRANSFER' was not found uniquely.

確認します。

Get-Volume |
    Select-Object DriveLetter, FileSystemLabel, FileSystem, HealthStatus |
    Format-Table -AutoSize

条件は次です。

  • DRIVERTRANSFER が1件だけ存在する
  • ドライブ文字が割り当てられている
  • HealthStatus が正常である

ドライバーが1件も抽出されない

エラー例:

No active OEM INF package was found.

次を確認します。

pnputil.exe /enum-drivers /files

または次のファイルを確認します。

<USBドライブ>:\Windows-Driver-Transfer\Inventory\DriverStore.txt

対象機がMicrosoft標準ドライバーだけで動作している場合、第三者製OEM INFが存在しないことがあります。

一部のドライバー抽出に失敗する

確認対象は次です。

<USBドライブ>:\Windows-Driver-Transfer\Logs\DriverExport.log
<USBドライブ>:\Windows-Driver-Transfer\Logs\ExportFailures.txt

失敗したINF名を確認し、次で個別に調査します。

pnputil.exe /enum-drivers /files

SHA-256検証に失敗する

確認対象は次です。

C:\Drivers\Windows-Driver-Transfer\Current\Target-Logs\HashErrors.txt

この場合、ドライバーをインストールしてはいけません。

USBから再コピーするか、SOURCE-PC で再抽出します。

移植後も不明なデバイスが残る

問題デバイスのハードウェアIDを取得します。

pnputil.exe /enum-devices /problem /deviceids

保存済みの一覧と照合します。

C:\Drivers\Windows-Driver-Transfer\Current\Inventory\PnPSignedDrivers.csv

たとえば対象ハードウェアIDが PCI\VEN_10EC で始まる場合は、次で検索します。

Import-Csv (
    'C:\Drivers\Windows-Driver-Transfer\Current\Inventory\PnPSignedDrivers.csv'
) |
    Where-Object {
        $_.HardWareID -like 'PCI\VEN_10EC*' -or
        $_.DeviceID -like 'PCI\VEN_10EC*'
    } |
    Select-Object (
        'DeviceName',
        'DeviceClass',
        'DriverProviderName',
        'DriverVersion',
        'InfName',
        'HardWareID',
        'DeviceID'
    ) |
    Format-List

一致しない場合は、同じ販売型番でも内部部品が異なる可能性があります。

その場合は、販売型番ではなく、残ったデバイスのハードウェアIDを基準にチップメーカー公式ドライバーを探します。

PnPUtilが既存ドライバーを置き換えない

PnPUtil /add-driver ... /install は、追加したドライバーがWindowsのランキングで最上位でない場合、強制的に適用しません。

対象デバイスのインスタンスIDを確認します。

pnputil.exe /enum-devices /problem /deviceids

候補ドライバーと順位を確認します。

pnputil.exe /enum-devices /instanceid "<デバイスインスタンスID>" /drivers

順位が低いドライバーを無理に適用するために、署名検証やSecure Bootを無効化してはいけません。

未署名ドライバーで失敗する

次は禁止します。

  • Secure Bootの無効化
  • ドライバー署名強制の無効化
  • テスト署名モードの有効化
  • INFファイルの手動改変

次の順で代替ドライバーを探します。

  1. Windows Update
  2. PCメーカー公式
  3. デバイスメーカー公式
  4. チップメーカー公式

問題が起きたドライバーを削除する

導入後に特定デバイスが不調になった場合、最初に対象デバイスと使用中のINFを特定します。

pnputil.exe /enum-devices /problem /deviceids /drivers

例として、問題のパッケージが oem42.inf だった場合は次です。

pnputil.exe /delete-driver oem42.inf /uninstall /reboot

oem42.inf は例です。

実際には、対象デバイスに関連付けられているPublished Nameを確認して指定します。

最初から /force を付けてはいけません。

次は、対象パッケージと影響範囲を理解している場合に限ります。

pnputil.exe /delete-driver oem42.inf /uninstall /force /reboot

TARGET-PC がWindows新規インストール直後で、削除対象を特定できない場合は、Windowsを再度クリーンインストールする方が安全です。

SOURCE-PC とUSB上の抽出データは変更されません。

一時対応と恒久対応

一時対応

今回の端末を復旧するだけなら、次で完了です。

同型正常機から抽出
  → TARGET-PCへ導入
  → 再起動
  → Windows Update
  → デバイス確認

恒久対応

今後の再インストールに備え、次のディレクトリを社内ストレージへ保存します。

<USBドライブ>:\Windows-Driver-Transfer

推奨する保存単位は次です。

DriverArchive
└─ AskHand-Ryzen7-5700U
   └─ 2026-08-22
      ├─ Drivers
      ├─ Inventory
      ├─ Logs
      └─ READY.txt

管理情報として、次を残します。

  • メーカー名
  • 販売型番
  • ベースボード製品名
  • ベースボードリビジョン
  • Windowsビルド
  • 抽出日
  • 抽出元コンピューター
  • INF数
  • SHA-256
  • Firmwareパッケージを除外したこと
  • 動作確認結果

抽出したドライバーファイルは、ライセンス条件を確認せずQiitaやGitHubへ公開してはいけません。

公開するのは次に限定します。

  • 抽出スクリプト
  • 導入スクリプト
  • マニフェストの項目定義
  • 実行手順
  • 匿名化したログ
  • 匿名化した画面キャプチャ

記事用の画面キャプチャ構成

画面キャプチャは、次の4枚を必須とすると記事が理解しやすくなります。

ファイル名例 内容 マスク対象
01-device-manager-before.png 導入前の不明なデバイス コンピューター名
02-driver-export-completed.png 抽出成功とINF数 コンピューター名、USBドライブ文字
03-encoding-parser-error-and-fix.png 文字化けエラーとBOM修正結果 ユーザー名を含むパス
04-device-manager-after.png 導入後の正常なデバイスマネージャー コンピューター名

次は任意です。

ファイル名例 内容
05-hardware-inventory.png Win32_ComputerSystemWin32_BaseBoard の比較
06-driver-transfer-tree.png USB上のディレクトリ構成
07-pnputil-import-log.png DriverImport.log の成功部分
08-problem-devices-empty.png pnputil /enum-devices /problem /deviceids で問題なし

公開前に次をマスクします。

  • Windowsプロダクトキー
  • シリアル番号
  • Microsoftアカウント
  • メールアドレス
  • ユーザー名
  • 社内コンピューター名
  • 社内ドメイン名
  • USBのボリュームシリアル
  • MACアドレス
  • パブリックIPアドレス

得られた知見

今回の作業から得られた汎用的な知見は次のとおりです。

1. 同型正常機は最も信頼できるドライバー供給元になる

メーカーサイトにドライバーがなくても、正常動作機のDriver Storeには、実際にそのハードウェアで使用されているドライバーが残っています。

Web検索で似た機種のドライバーを集めるより、ハードウェア一致を確認した同型機から抽出する方が再現性があります。

2. 製品名よりハードウェアIDが重要

販売名や筐体名が同じでも、内部デバイスが同一とは限りません。

最終的な判断基準は次です。

PCI\VEN_xxxx&DEV_xxxx
USB\VID_xxxx&PID_xxxx
ACPI\xxxx

3. ドライバーとファームウェアを分離する

Windowsでは、ファームウェア更新もINFを含むドライバーパッケージとして扱われることがあります。

そのため、Driver Storeからの全件抽出と全件導入を行う場合は、FirmwareクラスやUEFIデバイスを識別する必要があります。

4. PnPUtilだけで抽出と導入を実行できる

第三者製のドライバー更新ソフトは不要です。

Windows標準の PnPUtil.exe で次を実行できます。

  • Driver Storeの列挙
  • ドライバーパッケージの抽出
  • Driver Storeへの追加
  • 一致するデバイスへのインストール
  • 問題デバイスの列挙
  • ドライバーパッケージの削除

5. 文字コード検証も再現手順の一部である

スクリプトのロジックが正しくても、保存時の文字コードが異なれば実行できません。

特に次の組み合わせは注意が必要です。

Windows PowerShell 5.1
  + UTF-8 BOMなし
  + 日本語を含む.ps1

構文検査と文字コード検査を実行前に行うことで、原因の異なる問題を分離できます。

まとめ

メーカー配布ドライバーが見つからないWindows PCでも、正常動作している同型機があれば、Windows標準機能だけでドライバーを復旧できます。

推奨する構成は次です。

Microsoft公式Windows 11
  + 同型正常機から抽出した署名済みOEMドライバー
  + Firmware/UEFIパッケージの除外
  + SHA-256検証
  + Windows Update

重要な点は、単にドライバーフォルダーをコピーすることではありません。

  • 移植元と移植先のハードウェアを記録する
  • 現在使用されているOEM INFを対象にする
  • Firmware/UEFIを除外する
  • SHA-256で転送結果を検証する
  • 導入前後の問題デバイスを記録する
  • Windows PowerShell 5.1では文字コードを確認する
  • 抽出したドライバー本体は公開しない

この形で保存しておけば、メーカーサポートが限定的なミニPCやホワイトボックスPCでも、再インストール可能な社内ドライバーアーカイブとして再利用できます。

参考資料

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?