User32.dll Import Cause Windows Terminal Crash #22787

Closed
opened 2026-01-31 08:23:28 +00:00 by claunia · 2 comments
Owner

Originally created by @3wweiweiwu on GitHub (Jan 23, 2025).

Windows Terminal version

1.21.3231.0

Windows build number

10.0.26100.0

Other Software

No response

Steps to reproduce

Problem Statement

It seems like the use of user32.dll could cause stability issue on Windows terminal. Eventually, it will cause the crash.

This issue only happens with a small possibility when the command is executed (<0.1% chance/cycle). But when we put this in a while loop a run the same script across 200+ machines, it will happen everyday.

In this case, I isolated the code to reproduce this problem.

  • run following code as administrator.
  • start another 15 terminals to run that in parallel.

Code to reproduce

function Out-ProcessEx {
    param
    (
        [Parameter(Mandatory, ValueFromPipeline)]
        $process
    )
  
    
    process {
        #Get Window Title, We use this function because default PowerShell function cannot get 
        #title for hidden window
        $wTitle = Get-WindowTitleFromProcessId -processId $process.Id

        # update MainWindowTitle
        $field = $process.GetType().GetField("mainWindowTitle", [System.Reflection.BindingFlags]::Instance -bor [System.Reflection.BindingFlags]::NonPublic -bor [System.Reflection.BindingFlags]::Public)
        $field.SetValue($process, $wTitle)

    
        $process 
    }  
}


function Get-WindowHandleFromProcessId {
    param (
        [Parameter(Mandatory = $true)]
        [int]$ProcessId
    )
 
    # Define the necessary functions from user32.dll
    Add-Type @"
using System;
using System.Runtime.InteropServices;
 
public class User32 {
    public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
 
    [DllImport("user32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
 
    [DllImport("user32.dll", SetLastError = true)]
    public static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
}
"@ -ErrorAction SilentlyContinue
 
    $Script:windowHandle = 0
 
    $callback = [User32+EnumWindowsProc] {
        param ($hWnd, $lParam)
        $processId = 0
        [User32]::GetWindowThreadProcessId($hWnd, [ref]$processId) | Out-Null
        if ([int]($processId) -eq [int]($lParam)) {
            $Script:windowHandle = $hWnd
            return $false  # Stop enumeration
        }
 
        return $true  # Continue enumeration
    }
 
    [User32]::EnumWindows($callback, [IntPtr]$ProcessId) | Out-Null
 
    return $Script:windowHandle
}

 
function Get-WindowTitleFromProcessId {
    param (
        [Parameter(Mandatory = $true)]
        [Int]$processId
    )
    # Define the necessary functions from user32.dll
    Add-Type @"
using System;
using System.Text;
using System.Runtime.InteropServices;
 
public class WindowTitleFromHandle {
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
 
    [DllImport("user32.dll", SetLastError = true)]
    public static extern int GetWindowTextLength(IntPtr hWnd);
}
"@ -ErrorAction SilentlyContinue
    $WindowHandle = Get-WindowHandleFromProcessId -ProcessId $processId
    $length = [WindowTitleFromHandle]::GetWindowTextLength($WindowHandle)
    if ($length -gt 0) {
        $builder = New-Object System.Text.StringBuilder -ArgumentList ($length + 1)
        [WindowTitleFromHandle]::GetWindowText($WindowHandle, $builder, $builder.Capacity) | Out-Null
        return $builder.ToString()
    }
    else {
        return "No title found"
    }
}
while($true)
{
$s1 = Get-Process|Out-ProcessEx
#start 16 process and see......
}

Error Log

Image

Image

Expected Behavior

16 instances of windows terminal should persist after 20 minutes

Actual Behavior

  • The crash issue typically occur after ~ 15-20mins.
Originally created by @3wweiweiwu on GitHub (Jan 23, 2025). ### Windows Terminal version 1.21.3231.0 ### Windows build number 10.0.26100.0 ### Other Software _No response_ ### Steps to reproduce ## Problem Statement It seems like the use of user32.dll could cause stability issue on Windows terminal. Eventually, it will cause the crash. This issue only happens with a small possibility when the command is executed (<0.1% chance/cycle). But when we put this in a while loop a run the same script across 200+ machines, it will happen everyday. In this case, I isolated the code to reproduce this problem. * run following code as administrator. * start another 15 terminals to run that in parallel. ## Code to reproduce ```powershell function Out-ProcessEx { param ( [Parameter(Mandatory, ValueFromPipeline)] $process ) process { #Get Window Title, We use this function because default PowerShell function cannot get #title for hidden window $wTitle = Get-WindowTitleFromProcessId -processId $process.Id # update MainWindowTitle $field = $process.GetType().GetField("mainWindowTitle", [System.Reflection.BindingFlags]::Instance -bor [System.Reflection.BindingFlags]::NonPublic -bor [System.Reflection.BindingFlags]::Public) $field.SetValue($process, $wTitle) $process } } function Get-WindowHandleFromProcessId { param ( [Parameter(Mandatory = $true)] [int]$ProcessId ) # Define the necessary functions from user32.dll Add-Type @" using System; using System.Runtime.InteropServices; public class User32 { public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam); [DllImport("user32.dll", SetLastError = true)] public static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId); } "@ -ErrorAction SilentlyContinue $Script:windowHandle = 0 $callback = [User32+EnumWindowsProc] { param ($hWnd, $lParam) $processId = 0 [User32]::GetWindowThreadProcessId($hWnd, [ref]$processId) | Out-Null if ([int]($processId) -eq [int]($lParam)) { $Script:windowHandle = $hWnd return $false # Stop enumeration } return $true # Continue enumeration } [User32]::EnumWindows($callback, [IntPtr]$ProcessId) | Out-Null return $Script:windowHandle } function Get-WindowTitleFromProcessId { param ( [Parameter(Mandatory = $true)] [Int]$processId ) # Define the necessary functions from user32.dll Add-Type @" using System; using System.Text; using System.Runtime.InteropServices; public class WindowTitleFromHandle { [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); [DllImport("user32.dll", SetLastError = true)] public static extern int GetWindowTextLength(IntPtr hWnd); } "@ -ErrorAction SilentlyContinue $WindowHandle = Get-WindowHandleFromProcessId -ProcessId $processId $length = [WindowTitleFromHandle]::GetWindowTextLength($WindowHandle) if ($length -gt 0) { $builder = New-Object System.Text.StringBuilder -ArgumentList ($length + 1) [WindowTitleFromHandle]::GetWindowText($WindowHandle, $builder, $builder.Capacity) | Out-Null return $builder.ToString() } else { return "No title found" } } while($true) { $s1 = Get-Process|Out-ProcessEx #start 16 process and see...... } ``` ## Error Log ![Image](https://github.com/user-attachments/assets/e98509ef-bf6a-44c6-acc3-54ea18276cb7) ![Image](https://github.com/user-attachments/assets/7ed03a72-3995-4db8-b1d1-5a824d4ef95b) ### Expected Behavior 16 instances of windows terminal should persist after 20 minutes ### Actual Behavior * The crash issue typically occur after ~ 15-20mins.
Author
Owner

@lhecker commented on GitHub (Jan 23, 2025):

It may take a little bit for us to come around to test this. If you don't mind, could you try our latest Canary build? You can find it here: https://aka.ms/terminal-canary-installer
It's an app package just like the regular Windows Terminal so you can cleanly uninstall it again. I'm asking for this, because that version contains a rewrite of our windowing architecture which fixes bugs that look just like yours.

If that doesn't work, another thing that may help would be a crash dump: https://github.com/microsoft/terminal/wiki/Troubleshooting-Tips#capture-automatically
...but that's optional given that you wrote such a nice repro. 🙂 If the Canary build doesn't fix it, then that will make it a lot easier to debug this.

@lhecker commented on GitHub (Jan 23, 2025): It may take a little bit for us to come around to test this. If you don't mind, could you try our latest Canary build? You can find it here: https://aka.ms/terminal-canary-installer It's an app package just like the regular Windows Terminal so you can cleanly uninstall it again. I'm asking for this, because that version contains a rewrite of our windowing architecture which fixes bugs that look just like yours. If that doesn't work, another thing that may help would be a crash dump: https://github.com/microsoft/terminal/wiki/Troubleshooting-Tips#capture-automatically ...but that's optional given that you wrote such a nice repro. 🙂 If the Canary build doesn't fix it, then that will make it a lot easier to debug this.
Author
Owner

@microsoft-github-policy-service[bot] commented on GitHub (Feb 3, 2025):

This issue has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for 4 days. It will be closed if no further activity occurs within 3 days of this comment.

@microsoft-github-policy-service[bot] commented on GitHub (Feb 3, 2025): This issue has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for **4 days**. It will be closed if no further activity occurs **within 3 days of this comment**. <!-- Policy app identification https://img.shields.io/static/v1?label=PullRequestIssueManagement. -->
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/terminal#22787