2024-12-09

Windows 11 Custom Setup without 8dot3name


Outcome after Windows 11 24H2 install:

fsutil 8dot3name scan /s C:

Total affected registry keys: 40
files & directories scanned: 163804
found: 0

To get this result requires an install.wim without 8dot3 names, and timely use of

fsutil.exe behavior set disable8dot3 1
fsutil.exe 8dot3name set C: 1

Note: Some of the wide, unbroken pre-formatted lines below do not display well in this web page. To see them better, hightlight all, copy, and paste into text editor, e.g., Notepad++.


Preparation and Windows Install Steps:

Step 0: Obtain ISO and install.wim


Obtain Windows Install ISO from trusted source, e.g., Microsoft. Extract sources/install.wim from ISO with unzip program, e.g., 7Zip. From this original install.wim, extract parts if wanted, e.g., only Pro, only Pro N, Home and Pro.

Example to extract just Pro N:
dism /Export-Image /SourceImageFile:"Z:\wims\oem\install.wim" /SourceIndex:7 /DestinationImageFile:"C:\temp\pro_n\install.wim"

Step 1: Prepare wim file:


Note 0: This is the only step where stripping 8dot3name is performed.

Note 1: Removing the 8dot3 names from the wim file is necessary, else the installed files will not be free of 8dot3 names, despite other steps later.

Note 2: This example includes sideloading Windows Updates. Edit Updates_List.txt as wanted, e.g., empty for no updates.

For more info on sideloading Windows Updates, RTFM the DISM docs, and do not follow incomplete instructions from web searches.
https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/dism-operating-system-package-servicing-command-line-options?view=windows-11
https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/add-or-remove-packages-offline-using-dism?view=windows-11
Note 3: Contents of my Updates_List.txt:
C:\temp\wip\Updates\cumulative\windows11.0-kb5048667-x64_d4ad0ca69de9a02bc356757581e0e0d6960c9f93.msu
C:\temp\wip\Updates\cumulative_.net\windows11.0-kb5045934-x64-ndp481_fa9c3adfb0532eb8f4e521f4fb92a179380184c5.msu
Note: Dependency in folder C:\temp\wip\Updates\cumulative\ but not listed in Updates_List.txt:
windows11.0-kb5043080-x64_953449672073f8fb99badb4cc6d5d7849b9c83e8.msu

Updates downloaded from Windows Catalog:
https://catalog.update.microsoft.com/Search.aspx?q=24h2+x64&scol=DateComputed&sdir=desc
The following scripts are modifications of works by https://schneegans.de and https://github.com/memstechtips.

Wim-Add-Updates-and-Strip-8dot3names.ps1:
# Note: Requires elevated privileges to run.
# Note: Before first use, maybe need to call PsExec.exe/PsExec64.exe manually to accept eula.
#
# https://answers.microsoft.com/en-us/windows/forum/all/remove-multiple-indexes-with-dism/b57d2169-47a2-41f6-b667-af2c8ef44b06
#
# https://www.xda-developers.com/integrate-updates-windows-11-iso/
# https://woshub.com/add-updates-into-windows-image/
# https://woshub.com/integrate-drivers-to-windows-install-media/
#
# https://learn.microsoft.com/en-us/windows/deployment/customize-boot-image?tabs=powershell#step-12-export-boot-image-to-reduce-size
#
# https://schneegans.de/windows/no-8.3/
# https://learn.microsoft.com/de-de/archive/blogs/josebda/windows-server-2012-file-server-tip-disable-8-3-naming-and-strip-those-short-names-too
#

function Wim-Add-Updates-and-Strip-8dot3names {
    [CmdletBinding()]
    param(
        [Parameter( Mandatory )]
        [string]
        $WimFile,

        [Parameter( Mandatory )]
        [string]
        $UpdatesList,

        [Parameter( Mandatory )]
        [string]
        $UpdatesDir,

        [Parameter( Mandatory )]
        [string]
        $TempMountDirectory,

        [ValidateSet( 'Errors', 'Warnings', 'WarningsInfo' )]
        [string]
        $LogLevel = 'Errors',

        [Parameter( Mandatory )]
        [string]
        $PsExecPath,

        [string]
        $Dism = "$env:windir\system32\Dism.exe",

        [string]
        $fsutil = "$env:windir\system32\fsutil.exe",
        
        [Parameter( Mandatory )]
        [string]
        $TempWimDirectory
    );

    $ErrorActionPreference = "Stop";

    $RequiredInput = @($WimFile, $PsExecPath, $Dism, $fsutil, $UpdatesList);
    $RequiredInput.ForEach( {
        if( -not [System.IO.File]::Exists( $_ ) ) {
            throw "'$_' not found.";
        }
    } );

    $params = @{
        LogLevel = $LogLevel;
    };

    Write-Output $(Get-Date -Format u);
    dir -Path $WimFile;
    & $PsExecPath -accepteula -nobanner -s $Dism /Get-WimInfo /WimFile:$WimFile;
    & $PsExecPath -accepteula -nobanner -s $Dism /Get-WimInfo /WimFile:$WimFile /Index:1;
    if( $LASTEXITCODE ) {
        throw "exited with error code $LASTEXITCODE.";
    };
    $TempDirs = @($TempMountDirectory, $TempWimDirectory);
    $TempDirs.ForEach( {
        if (Test-Path -Path "$_") {
            throw "$_ should not already exist.";
        };
        New-Item -Path "$_" -Type Directory;
    } );
    Get-WindowsImage -ImagePath $WimFile @params | ForEach-Object -Process {
        Write-Output $(Get-Date -Format u);
        Write-Output ("Processing edition '{0}'. Mounting to '{1}'." -f $_.ImageName, $TempMountDirectory);
        Mount-WindowsImage -Path $TempMountDirectory -ImagePath $WimFile -Name $_.ImageName @params;
        Write-Output $(Get-Date -Format u);
        Write-Output "Adding updates...";
        & $PsExecPath -accepteula -nobanner -s $Dism /Image:$TempMountDirectory /Get-Packages /Format:Table;
        $UpdateFiles = Get-Content -Path $UpdatesList;
        $UpdateFiles.ForEach( {
            Write-Output $(Get-Date -Format u);
            $UpdateFile = "$UpdatesDir\$_";
            Write-Output ("Processing Update: '{0}'" -f $UpdateFile);
            & $PsExecPath -accepteula -nobanner -s $Dism /Image:$TempMountDirectory /Add-Package /PackagePath:$UpdateFile;
            if( $LASTEXITCODE ) {
                throw "exited with error code $LASTEXITCODE.";
            };
            Write-Output $(Get-Date -Format u);
            & $PsExecPath -accepteula -nobanner -s $Dism /Image:$TempMountDirectory /Get-Packages /Format:Table;
        } );
        Write-Output ("Stripping 8dot3names in '{0}'..." -f $TempMountDirectory);
        & $PsExecPath -accepteula -nobanner -s $fsutil 8dot3name strip /f /s "$TempMountDirectory";
        if( $LASTEXITCODE ) {
            throw "exited with error code $LASTEXITCODE.";
        };
        & $PsExecPath -accepteula -nobanner -s $fsutil 8dot3name scan /s "$TempMountDirectory";
        Write-Output $(Get-Date -Format u);
        Dismount-WindowsImage -Path $TempMountDirectory -Save @params;
        Write-Output $(Get-Date -Format u);
    };
    Remove-Item -LiteralPath "$TempMountDirectory" -Force;
    & $PsExecPath -accepteula -nobanner -s $Dism /Get-WimInfo /WimFile:$WimFile;
    & $PsExecPath -accepteula -nobanner -s $Dism /Get-WimInfo /WimFile:$WimFile /Index:1;
    if( $LASTEXITCODE ) {
        throw "exited with error code $LASTEXITCODE.";
    };
    
    Write-Output $(Get-Date -Format u);
    $WimFileExported = "$TempWimDirectory\$(Split-Path $WimFile -Leaf)";
    Write-Output ("Compressing '{0}' to '{1}'..." -f $WimFile, $WimFileExported);
    Export-WindowsImage -SourceImagePath $WimFile -DestinationImagePath $WimFileExported -CompressionType max -Verbose;
    Remove-Item -LiteralPath "$WimFile" -Force;
    Move-Item -LiteralPath "$WimFileExported" -Destination "$WimFile" -Force;
    Remove-Item -LiteralPath "$TempWimDirectory" -Force;

    Write-Output $(Get-Date -Format u);

    $WimDirectory = Split-Path -Path "$WimFile";
    $WimExtension = [System.IO.Path]::GetExtension($WimFile)
    Set-Location "$WimDirectory";
    $FinalWimFiles = Get-ChildItem -Path "$WimDirectory" -Name "*$WimExtension" -File;
    $FinalWimFiles.ForEach( {
        Write-Output ("Hashing '{0}'..." -f $_);
        dir -Path $_;
        $WimFileHash = Get-FileHash -Algorithm SHA256 -Path $_ | % {$_.Hash + "  " + (Resolve-Path -Path $_.Path -Relative)};
        $WimFileHashStream = [System.IO.StreamWriter]::new("$WimDirectory\$_.SHA256.txt");
        $WimFileHashStream.WriteLine($WimFileHash);
        $WimFileHashStream.Close();
    } );
    
    Write-Output $(Get-Date -Format u);
    Write-Output "FINI";
};

$WimFile="$(Get-Location)\prepare_wim\pro_n\install.wim"
$UpdatesList="$(Get-Location)\prepare_wim\Updates_List.txt"
$UpdatesDir="$(Get-Location)\prepare_wim\Updates\"
$TempMountDirectory="Z:\mnt"
$TempWimDirectory="Z:\temp\TempWim"
$PsExecPath="C:\tools\SysinternalsSuite--2024-10-14\PsExec64.exe"

Wim-Add-Updates-and-Strip-8dot3names `
-WimFile $WimFile `
-UpdatesList $UpdatesList `
-UpdatesDir $UpdatesDir `
-TempMountDirectory $TempMountDirectory `
-TempWimDirectory $TempWimDirectory `
-PsExecPath $PsExecPath `
| Tee-Object -FilePath "$(Get-Location)\prepare_wim\Wim-Add-Updates-and-Strip-8dot3names--pro_n.log" -Append

Step 2: Create ISO:

Note: The "sourceroot" contains all contents from original OEM ISO, with sources/install.wim replaced with the one created in previous step.

bootOrder.txt contents:
boot\bcd
boot\boot.sdi
boot\bootfix.bin
boot\bootsect.exe
boot\etfsboot.com
boot\memtest.exe
boot\en-us\bootsect.exe.mui
boot\fonts\chs_boot.ttf
boot\fonts\cht_boot.ttf
boot\fonts\jpn_boot.ttf
boot\fonts\kor_boot.ttf
boot\fonts\wgl4_boot.ttf
sources\boot.wim
Create-ISO.ps1:
# Note: Requires elevated privileges to run.
#
# https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/oscdimg-command-line-options?view=windows-11
# https://github.com/memstechtips/WIMUtil/blob/main/src/WIMUtil.ps1

function Create-ISO {
    [CmdletBinding()]
    param(
        [Parameter( Mandatory )]
        [string]
        $PsExecPath,
        
        [Parameter( Mandatory )]
        [string]
        $oscdimg,
        
        [Parameter( Mandatory )]
        [string]
        $isoVolumeLabel,
        
        [Parameter( Mandatory )]
        [string]
        $BootOrderFile,
        
        [Parameter( Mandatory )]
        [string]
        $etfsboot,
        
        [Parameter( Mandatory )]
        [string]
        $efisys,
        
        [Parameter( Mandatory )]
        [string]
        $sourceroot,
        
        [Parameter( Mandatory )]
        [string]
        $targetfile
    );
    
    $ErrorActionPreference = "Stop";

    $RequiredInput = @($PsExecPath, $oscdimg, $BootOrderFile, $etfsboot, $efisys);
    $RequiredInput.ForEach( {
        if( -not [System.IO.File]::Exists( "$_" ) ) {
            throw "'$_' not found.";
        }
    } );
    
    Write-Output $(Get-Date -Format u);

    $targetroot = Split-Path -Path "$targetfile";
    if (-not(Test-Path $targetroot -PathType Container)) {
        New-Item -path $targetroot -ItemType Directory;
    };
    # Set-Location "$targetroot";
    
    & $PsExecPath -accepteula -nobanner -s "$oscdimg" -m -o -g -h -u2 -udfver102 -l"$isoVolumeLabel" -yo"$BootOrderFile" -bootdata:2#p0,e,b"$etfsboot"#pEF,e,b"$efisys" "$sourceroot" "$targetfile";
    
    dir -Path "$targetfile";
    $isoFileHash = Get-FileHash -Algorithm SHA256 -Path "$targetfile" | % {$_.Hash + "  " + (Resolve-Path -Path $_.Path -Relative)};
    $isoFileHashStream = [System.IO.StreamWriter]::new("$targetfile.SHA256.txt");
    $isoFileHashStream.WriteLine($isoFileHash);
    $isoFileHashStream.Close();
    
    Write-Output $(Get-Date -Format u);
    Write-Output "FINI";
};

$sourceroot = "$(Get-Location)\create_iso\oem_iso";
$targetfile = "Z:\temp\created_iso\Win11PN_24H2_20241214_en-us_x64.iso";
$PsExecPath = "C:\tools\SysinternalsSuite--2024-10-14\PsExec64.exe";
$oscdimg = "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg\oscdimg.exe";
$isoVolumeLabel = "W11PN_2605";
$BootOrderFile = "$(Get-Location)\create_iso\bootOrder.txt";
$etfsboot = "$sourceroot\boot\etfsboot.com";
$efisys = "$sourceroot\efi\microsoft\boot\efisys.bin";

Create-ISO `
-sourceroot $sourceroot `
-targetfile $targetfile `
-PsExecPath $PsExecPath `
-oscdimg $oscdimg `
-isoVolumeLabel $isoVolumeLabel `
-BootOrderFile $BootOrderFile `
-etfsboot $etfsboot `
-efisys $efisys `
| Tee-Object -FilePath "$(Get-Location)\create_iso\Create-ISO--pro_n.log" -Append
Note: Keep the length of the isoVolumeLabel to just 11 characters, if preferred.


Step 3: Prepare notautounattend.xml


Generate schneegans.de notautounattend.xml with at least these two commands in the section, "Scripts to run in the system context, before user accounts are created":

fsutil.exe 8dot3name set C: 1
fsutil.exe behavior set disable8dot3 1

The first command might not be necessary, but it does not hurt, the second command is necessary.

Note: USB device with notautounattend.xml attached and automatically mounted as C:. After reboot, Windows Setup will re-label volumes.

Step 4: Windows Install:


Note: This does not work in full unattend mode with autounattend.xml. The "Previous Version of Setup" is required.

At Screen: Select setup option: Select "Previous Version of Setup".

At Screen: Windows Setup > Language, Local, Input...

Either of these work, depending on other needs:
X:\sources\setup.exe /Unattend:C:\notautounattend.xml
Or:
X:\sources\setup.exe /NoReboot /Unattend:C:\notautounattend.xml

If using /NoReboot:

Back at Screen: Windows Setup > Language, Local, Input...

X:\Windows\System32\wpeutil.exe reboot

After setup reboot finishes, first boot and first logon scripts run.

Confirmed 8dot3 disabled:

fsutil 8dot3name query C:
fsutil 8dot3name scan /s C:

Total affected registry keys: 40
files & directories scanned: 163804
found: 0

2011-07-11

Gson v Jackson - Part 6

tl;dnr

Use Jackson, not Gson. Use this article as a reference for basic features.

Part 5 of this short series of articles ended with section 5.17 of the Gson user guide, "Streaming". (Sections 6 and 7 will not be reviewed.) This sixth part includes a summary of the key differences between Gson and Jackson noted so far, along with a table of contents listing for easy navigation to the various sections of this review.
Link To This ArticleQR Code Link To This Articlehttp://goo.gl/0oR3t

2011-07-10

Gson v Jackson - Part 5

tl;dnr

Use Jackson, not Gson. Use this article as a reference for basic features.

Part 4 of this short series of articles ended with section 5.14.3 of the Gson user guide, "User Defined Exclusion Strategies". This fifth part continues with section 5.15 on "JSON Field Naming Support", and ends with section 5.17 on "Streaming", which is the end of the Gson user guide. Part 6 will include a summary of the key differences between Gson and Jackson noted so far, along with a table of contents listing for easy navigation to the various sections of this review.
Link To This ArticleQR Code Link To This Articlehttp://goo.gl/fWihj

2011-07-03

Gson v Jackson - Part 4

tl;dnr

Use Jackson, not Gson. Use this article as a reference for basic features.

Part 3 of this short series of articles ended with section 5.10.1 of the Gson user guide, "InstanceCreator for a Parameterized Type". This fourth part continues with section 5.11 on "Compact Vs. Pretty Printing for JSON Output Format", and ends with section 5.14.3 on "User Defined Exclusion Strategies". Part 5 will continue with section 5.15 on "JSON Field Naming Support".
Link To This ArticleQR Code Link To This Articlehttp://goo.gl/RpFIE

2011-07-02

Gson v Jackson - Part 3

tl;dnr

Use Jackson, not Gson. Use this article as a reference for basic features.

Part 2 of this short series of articles ended with section 5.7 of the Gson user guide, titled "Serializing and Deserializing Collection with Objects of Arbitrary Types". This third part continues with section 5.8 on "Built-in Serializers and Deserializers", and ends with section 5.10.1 on "InstanceCreator for a Parameterized Type". Part 4 will continue with section 5.11 on "Compact Vs. Pretty Printing for JSON Output Format".
Link To This ArticleQR Code Link To This Articlehttp://goo.gl/HsIea

2011-06-27

Gson v Jackson - Part 2

tl;dnr

Use Jackson, not Gson. Use this article as a reference for basic features.

Part 1 of this short series of articles ended with section 5.3 of the Gson user guide, titled "Nested Classes (including Inner Classes)". This second part continues with section 5.4 on "Array Examples", and ends with section 5.7 on "Serializing and Deserializing Collection with Objects of Arbitrary Types". Part 3 will continue with section 5.8 on "Built-in Serializers and Deserializers".
Link To This ArticleQR Code Link To This Articlehttp://goo.gl/qkceb

2011-06-25

Gson v Jackson - Part 1

tl;dnr

Use Jackson, not Gson. Use this article as a reference for basic features.

What is this post?

Is this yet another JSON-to/from-Java API comparison? Yes, it is. It's the most comprehensive comparison of using Gson versus Jackson for common JSON-to/from-Java tasks known. The sections below walk through each section of the Gson user guide, demonstrating similar implementations with Jackson. API features beyond what is described in the Gson user guide are then reviewed.
Link To This ArticleQR Code Link To This Articlehttp://goo.gl/YuYhx