param( [string]$ClientDir = "runs/offline-real-client-window/full/zyl/Impp", [string]$OutDir = "runs/offline-chat-window", [ValidateSet("Minimal", "Deps")] [string]$RuntimeMode = "Deps", [switch]$SelfChat, [switch]$SkipRosterManager, [switch]$ShowWindow, [string]$HelperExe = "runs/win-helper/ISphereWinHelper.exe", [switch]$NoScreenshot, [int]$HoldSeconds = 8, [int]$X = 180, [int]$Y = 180, [int]$Width = 900, [int]$Height = 680 ) $ErrorActionPreference = "Stop" if ([Environment]::Is64BitProcess) { $x86PowerShell = Join-Path $env:WINDIR "SysWOW64\WindowsPowerShell\v1.0\powershell.exe" if (-not (Test-Path -LiteralPath $x86PowerShell)) { throw "32-bit PowerShell not found: $x86PowerShell" } $relayArgs = @("-NoProfile", "-ExecutionPolicy", "Bypass", "-File", $PSCommandPath) foreach ($entry in $PSBoundParameters.GetEnumerator()) { $name = [string]$entry.Key $value = $entry.Value if ($value -is [System.Management.Automation.SwitchParameter]) { if ($value.IsPresent) { $relayArgs += "-$name" } } else { $relayArgs += "-$name" $relayArgs += [string]$value } } & $x86PowerShell @relayArgs exit $LASTEXITCODE } $repo = Resolve-Path -LiteralPath (Join-Path $PSScriptRoot "..") function Resolve-RepoPath([string]$PathValue) { if ([System.IO.Path]::IsPathRooted($PathValue)) { return $PathValue } return (Join-Path $repo $PathValue) } function Get-TypeMemberInHierarchy([Type]$Type, [string]$Name, [string]$Kind) { $flags = [System.Reflection.BindingFlags]"Public,NonPublic,Instance,Static" $cursor = $Type while ($null -ne $cursor) { if ($Kind -eq "Property") { $member = $cursor.GetProperty($Name, $flags) } else { $member = $cursor.GetField($Name, $flags) } if ($member) { return $member } $cursor = $cursor.BaseType } return $null } function Set-PropInHierarchy([object]$Object, [Type]$Type, [string]$Name, [object]$Value) { $prop = Get-TypeMemberInHierarchy -Type $Type -Name $Name -Kind "Property" if ($prop -and $prop.CanWrite) { $target = if ($prop.GetSetMethod($true).IsStatic) { $null } else { $Object } $prop.SetValue($target, $Value, $null) return $true } return $false } function Set-FieldInHierarchy([object]$Object, [Type]$Type, [string]$Name, [object]$Value) { $field = Get-TypeMemberInHierarchy -Type $Type -Name $Name -Kind "Field" if ($field) { $target = if ($field.IsStatic) { $null } else { $Object } $field.SetValue($target, $Value) return $true } return $false } function New-ByConstructor([Type]$Type, [Type[]]$ParameterTypes, [object[]]$Arguments) { $ctor = $Type.GetConstructor($ParameterTypes) if (-not $ctor) { $sig = ($ParameterTypes | ForEach-Object { $_.FullName }) -join ", " throw "constructor not found: $($Type.FullName)($sig)" } return $ctor.Invoke($Arguments) } function Convert-ExceptionToRecord([Exception]$Exception) { $records = @() $cursor = $Exception $depth = 0 while ($cursor) { $trace = New-Object System.Diagnostics.StackTrace($cursor, $true) $frames = @() for ($i = 0; $i -lt $trace.FrameCount; $i++) { $frame = $trace.GetFrame($i) $method = $frame.GetMethod() $offset = $frame.GetILOffset() $frames += [pscustomobject]@{ index = $i method_type = if ($method.DeclaringType) { $method.DeclaringType.FullName } else { $null } method_name = $method.Name il_offset = $offset il_offset_hex = if ($offset -ge 0) { "IL_{0:x4}" -f $offset } else { $null } native_offset = $frame.GetNativeOffset() file = $frame.GetFileName() line = $frame.GetFileLineNumber() } } $records += [pscustomobject]@{ depth = $depth type = $cursor.GetType().FullName message = $cursor.Message target_site = if ($cursor.TargetSite) { "$($cursor.TargetSite.DeclaringType.FullName)::$($cursor.TargetSite.Name)" } else { $null } frames = $frames } $cursor = $cursor.InnerException $depth++ } return $records } function Get-FirstRelevantFrame([object[]]$ExceptionRecords) { foreach ($record in $ExceptionRecords) { foreach ($frame in $record.frames) { if ($frame.method_type -and $frame.method_type -notlike "System.*" -and $frame.method_type -notlike "Microsoft.*" -and $frame.method_type -notlike "MS.*") { return $frame } } } return $null } function Find-IlLine([string]$OutDirFull, [string]$IlOffsetHex) { if ([string]::IsNullOrWhiteSpace($IlOffsetHex)) { return $null } $ctorIl = Join-Path $OutDirFull "frmP2PChat-ctor.il.txt" if (-not (Test-Path -LiteralPath $ctorIl)) { return $null } $match = Select-String -LiteralPath $ctorIl -Pattern $IlOffsetHex -SimpleMatch | Select-Object -First 1 if ($match) { return @{ path = $ctorIl line_number = $match.LineNumber text = $match.Line.Trim() } } return $null } function Invoke-HelperJson([string]$HelperPath, [string]$Op, [hashtable]$OpArgs, [string]$RequestId) { $request = @{ protocol = "isphere.helper.v1" request_id = $RequestId op = $Op timeout_ms = 5000 args = $OpArgs } $json = $request | ConvertTo-Json -Depth 16 -Compress $output = $json | & $HelperPath --json try { return $output | ConvertFrom-Json } catch { throw "helper output was not JSON: $output" } } function Save-WindowScreenshot([string]$OutputPath, [int]$ScreenX, [int]$ScreenY, [int]$CaptureWidth, [int]$CaptureHeight) { $bitmap = New-Object System.Drawing.Bitmap $CaptureWidth, $CaptureHeight $graphics = [System.Drawing.Graphics]::FromImage($bitmap) try { $graphics.CopyFromScreen($ScreenX, $ScreenY, 0, 0, $bitmap.Size) $bitmap.Save($OutputPath, [System.Drawing.Imaging.ImageFormat]::Png) } finally { $graphics.Dispose() $bitmap.Dispose() } } function New-UserInfo([Type]$UserInfoType, [string]$Id, [string]$Jid, [string]$Name, [string]$CompanyID) { $user = [Activator]::CreateInstance($UserInfoType) foreach ($kv in @( @("Id", $Id), @("Jid", $Jid), @("LoginName", $Id), @("Domain", "offline.local"), @("Name", $Name), @("CompanyID", $CompanyID), @("DeptID", "codex-dept"), @("DeptName", "Codex Dept"), @("Enable", "1"), @("chatAble", "1"), @("Mobile", ""), @("Tel", ""), @("Email", "") )) { Set-PropInHierarchy -Object $user -Type $UserInfoType -Name $kv[0] -Value $kv[1] | Out-Null } return $user } $clientFull = Resolve-RepoPath $ClientDir $outFull = Resolve-RepoPath $OutDir $helperFull = Resolve-RepoPath $HelperExe New-Item -ItemType Directory -Force -Path $outFull | Out-Null if (-not (Test-Path -LiteralPath (Join-Path $clientFull "IMPlatformClient.exe"))) { throw "IMPlatformClient.exe not found under $clientFull" } Set-Location -LiteralPath $clientFull [AppDomain]::CurrentDomain.add_AssemblyResolve({ param($sender, $eventArgs) $assemblyName = New-Object System.Reflection.AssemblyName($eventArgs.Name) foreach ($extension in @(".dll", ".exe")) { $candidate = Join-Path $script:ClientDirectoryForResolve ($assemblyName.Name + $extension) if (Test-Path -LiteralPath $candidate) { return [System.Reflection.Assembly]::LoadFrom($candidate) } } return $null }) $script:ClientDirectoryForResolve = $clientFull $timestamp = Get-Date -Format "yyyyMMdd-HHmmss" $resultFile = Join-Path $outFull ("real-frmP2PChat-result-{0}.json" -f $timestamp) try { Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Drawing $script:RealFrmP2PThreadExceptions = New-Object System.Collections.ArrayList [System.Windows.Forms.Application]::SetUnhandledExceptionMode([System.Windows.Forms.UnhandledExceptionMode]::CatchException) [System.Windows.Forms.Application]::add_ThreadException({ param($sender, $eventArgs) [void]$script:RealFrmP2PThreadExceptions.Add($eventArgs.Exception) }) $asm = [System.Reflection.Assembly]::LoadFrom((Join-Path $clientFull "IMPlatformClient.exe")) $smack = [System.Reflection.Assembly]::LoadFrom((Join-Path $clientFull "smack.dll")) $common = [System.Reflection.Assembly]::LoadFrom((Join-Path $clientFull "IMPP.Common.dll")) $mgrType = $asm.GetType("IMPP.Client.IMPPManager", $true) $mcType = $asm.GetType("IMPP.Client.control.MessageCenter", $true) $configType = $asm.GetType("IMPP.Client.Config", $true) $baseConfigType = $asm.GetType("IMPP.Client.BaseConfig", $true) $configMgrType = $asm.GetType("IMPP.Client.ConfigSystemManager", $true) $configModelType = $asm.GetType("IMPP.Client.ConfigSystemModel", $true) $userInfoCacheType = $asm.GetType("IMPP.Client.Framework.Configuration.UserInfoCache", $true) $loginInfoType = $asm.GetType("IMPP.Client.LoginInfo", $true) $p2pFormType = $asm.GetType("IMPP.Client.Business.ChatManager.SingleChat.frmP2PChat", $true) $chatBaseType = $asm.GetType("IMPP.Client.Forms.frmChatBase", $true) $frmMainType = $asm.GetType("IMPP.Client.frmMain", $true) $connType = $smack.GetType("com.vision.smack.XMPPConnection", $true) $jidType = $smack.GetType("com.vision.smack.Jid", $true) $chatType = $smack.GetType("com.vision.smack.Chat", $true) $p2pChatType = $smack.GetType("com.vision.smack.P2PChat", $true) $rosterType = $smack.GetType("com.vision.smack.RosterManager", $true) $presenceType = $smack.GetType("com.vision.smack.PresenceManager", $true) $p2pMgrType = $smack.GetType("com.vision.smack.P2PChatManager", $true) $bookMarkType = $smack.GetType("com.vision.smack.muc.BookMarkManager", $true) $packetCollectorType = $smack.GetType("com.vision.smack.PacketCollector", $true) $userInfoType = $common.GetType("IMPP.Common.UserInfo", $true) $mgr = $mgrType.GetProperty("Instance", [System.Reflection.BindingFlags]"Public,Static").GetValue($null, $null) $fakeMessageCenter = [System.Runtime.Serialization.FormatterServices]::GetUninitializedObject($mcType) Set-FieldInHierarchy -Object $mgr -Type $mgrType -Name "k__BackingField" -Value $fakeMessageCenter | Out-Null $fakeFrmMain = [System.Runtime.Serialization.FormatterServices]::GetUninitializedObject($frmMainType) Set-FieldInHierarchy -Object $mgr -Type $mgrType -Name "k__BackingField" -Value $fakeFrmMain | Out-Null $login = [Activator]::CreateInstance($loginInfoType) Set-PropInHierarchy -Object $login -Type $loginInfoType -Name "Trait" -Value "codex_probe" | Out-Null Set-PropInHierarchy -Object $login -Type $loginInfoType -Name "TrueTraid" -Value "codex_probe" | Out-Null Set-PropInHierarchy -Object $login -Type $loginInfoType -Name "Status" -Value "online" | Out-Null Set-PropInHierarchy -Object $mgr -Type $mgrType -Name "LogonUser" -Value $login | Out-Null $companyId = "codex-company" $otherBareJid = "codex-probe@offline.local" $myBareJid = if ($SelfChat) { $otherBareJid } else { "codex-user@offline.local" } $myName = if ($SelfChat) { "Codex Probe" } else { "Codex User" } $jidCtor3 = $jidType.GetConstructor(@([string], [string], [string])) $myJid = $jidCtor3.Invoke(@(($myBareJid -split "@")[0], "offline.local", "pc")) $fakeConn = [System.Runtime.Serialization.FormatterServices]::GetUninitializedObject($connType) Set-PropInHierarchy -Object $fakeConn -Type $connType -Name "Jid" -Value $myJid | Out-Null Set-PropInHierarchy -Object $fakeConn -Type $connType -Name "UserName" -Value (($myBareJid -split "@")[0]) | Out-Null Set-PropInHierarchy -Object $fakeConn -Type $connType -Name "LoginState" -Value "online" | Out-Null Set-PropInHierarchy -Object $fakeConn -Type $connType -Name "CompanyID" -Value $companyId | Out-Null Set-PropInHierarchy -Object $fakeConn -Type $connType -Name "Resource" -Value "pc" | Out-Null Set-FieldInHierarchy -Object $fakeConn -Type $connType -Name "_packetListeners" -Value (New-Object System.Collections.ArrayList) | Out-Null $packetCollectorListType = [System.Collections.Generic.List``1].MakeGenericType($packetCollectorType) Set-FieldInHierarchy -Object $fakeConn -Type $connType -Name "_packetCollectors" -Value ([Activator]::CreateInstance($packetCollectorListType)) | Out-Null Set-PropInHierarchy -Object $mgr -Type $mgrType -Name "Connection" -Value $fakeConn | Out-Null Set-FieldInHierarchy -Object $mgr -Type $mgrType -Name "k__BackingField" -Value $fakeConn | Out-Null Set-PropInHierarchy -Object $mgr -Type $mgrType -Name "IsConnected" -Value $true | Out-Null Set-FieldInHierarchy -Object $mgr -Type $mgrType -Name "k__BackingField" -Value $true | Out-Null Set-FieldInHierarchy -Object $null -Type ($smack.GetType("com.vision.smack.SmarkManager", $true)) -Name "k__BackingField" -Value $mgr | Out-Null if ($RuntimeMode -eq "Deps") { $myUser = New-UserInfo -UserInfoType $userInfoType -Id (($myBareJid -split "@")[0]) -Jid $myBareJid -Name $myName -CompanyID $companyId $otherUser = New-UserInfo -UserInfoType $userInfoType -Id "codex-probe" -Jid $otherBareJid -Name "Codex Probe" -CompanyID $companyId Set-FieldInHierarchy -Object $mgr -Type $mgrType -Name "_userInfo" -Value $myUser | Out-Null $stringDictType = [System.Collections.Generic.Dictionary``2].MakeGenericType([string], [string]) $baseConfigDict = [Activator]::CreateInstance($stringDictType, [StringComparer]::OrdinalIgnoreCase) foreach ($kv in @( @("Remote_Control_Visible", "false"), @("VisualPhone_enable", "false"), @("showacctno", "no"), @("ShowDepartment", "NO"), @("StrucPPLNo", "NO"), @("MaintenanceAssistant", ""), @("CustomerServiceSuffix", ""), @("CustomerCN", ""), @("CustomerPhone", "") )) { $baseConfigDict[$kv[0]] = $kv[1] $baseConfigDict[$kv[0].ToLowerInvariant()] = $kv[1] } $baseConfigRawDict = $baseConfigDict.PSObject.BaseObject $baseConfigCtor = $baseConfigType.GetConstructor(@($baseConfigRawDict.GetType())) if (-not $baseConfigCtor) { throw "constructor not found: $($baseConfigType.FullName)($($baseConfigRawDict.GetType().FullName))" } $baseConfig = $baseConfigCtor.Invoke([object[]]@($baseConfigRawDict)) Set-FieldInHierarchy -Object $null -Type $configType -Name "k__BackingField" -Value $baseConfig | Out-Null Set-FieldInHierarchy -Object $null -Type $configType -Name "k__BackingField" -Value $myUser | Out-Null Set-FieldInHierarchy -Object $null -Type $configType -Name "k__BackingField" -Value $login | Out-Null $userListType = [System.Collections.Generic.List``1].MakeGenericType($userInfoType) $rosterUsers = [Activator]::CreateInstance($userListType) $rosterUsers.Add($myUser) | Out-Null $rosterUsers.Add($otherUser) | Out-Null Set-FieldInHierarchy -Object $mgr -Type $mgrType -Name "_rosterUserInfoList" -Value $rosterUsers | Out-Null $cfg = [Activator]::CreateInstance($configModelType) foreach ($kv in @( @("Font", "Microsoft YaHei UI"), @("Size", [single]9), @("FontStyle", "Regular"), @("Color", "Black"), @("SendMsgKeyPress", "Enter"), @("SendFilePath", $outFull), @("SendFileSize", 10), @("SendFileConf", $false), @("AllEnable", $true) )) { Set-PropInHierarchy -Object $cfg -Type $configModelType -Name $kv[0] -Value $kv[1] | Out-Null } $configMgrType.GetField("config", [System.Reflection.BindingFlags]"NonPublic,Static").SetValue($null, $cfg) $presence = New-ByConstructor -Type $presenceType -ParameterTypes @($connType) -Arguments @($fakeConn) Set-PropInHierarchy -Object $mgr -Type $mgrType -Name "PresenceManager" -Value $presence | Out-Null Set-FieldInHierarchy -Object $mgr -Type $mgrType -Name "k__BackingField" -Value $presence | Out-Null $bookmark = [System.Runtime.Serialization.FormatterServices]::GetUninitializedObject($bookMarkType) $p2pManager = New-ByConstructor -Type $p2pMgrType -ParameterTypes @($connType, $bookMarkType) -Arguments @($fakeConn, $bookmark) Set-PropInHierarchy -Object $mgr -Type $mgrType -Name "P2PChatManager" -Value $p2pManager | Out-Null Set-FieldInHierarchy -Object $mgr -Type $mgrType -Name "k__BackingField" -Value $p2pManager | Out-Null if (-not $SkipRosterManager) { $roster = New-ByConstructor -Type $rosterType -ParameterTypes @($connType) -Arguments @($fakeConn) Set-PropInHierarchy -Object $mgr -Type $mgrType -Name "RosterManager" -Value $roster | Out-Null Set-FieldInHierarchy -Object $mgr -Type $mgrType -Name "k__BackingField" -Value $roster | Out-Null } else { Set-PropInHierarchy -Object $mgr -Type $mgrType -Name "RosterManager" -Value $null | Out-Null Set-FieldInHierarchy -Object $mgr -Type $mgrType -Name "k__BackingField" -Value $null | Out-Null } } $p2pChat = New-ByConstructor -Type $p2pChatType -ParameterTypes @($connType, [string]) -Arguments @($fakeConn, $otherBareJid) $otherJid = $p2pChatType.GetProperty("OtherJid").GetValue($p2pChat, $null) if ($null -eq $otherJid) { $otherJid = $jidCtor3.Invoke(@("codex-probe", "offline.local", "")) Set-FieldInHierarchy -Object $p2pChat -Type $p2pChatType -Name "k__BackingField" -Value $otherJid | Out-Null } $formCtor = $p2pFormType.GetConstructor(@($chatType, [string], [string])) $form = $formCtor.Invoke(@($p2pChat, "", "")) if ($RuntimeMode -eq "Deps") { $userInfoCache = $userInfoCacheType.GetMethod("GetInstance", [System.Reflection.BindingFlags]"Public,Static").Invoke($null, @()) $addUserInfoCache = $userInfoCacheType.GetMethod("AddCache", [Type[]]@([string], $userInfoType)) foreach ($cacheEntry in @( @{ Key = $myBareJid; User = $myUser }, @{ Key = $myBareJid.ToLowerInvariant(); User = $myUser }, @{ Key = (($myBareJid -split "@")[0]); User = $myUser }, @{ Key = $otherBareJid; User = $otherUser }, @{ Key = $otherBareJid.ToLowerInvariant(); User = $otherUser }, @{ Key = "codex-probe"; User = $otherUser } )) { $addUserInfoCache.Invoke($userInfoCache, [object[]]@([string]$cacheEntry.Key, $cacheEntry.User.PSObject.BaseObject)) } } $form.Name = "frmP2PChat" $form.Text = "Codex Real frmP2PChat Offline Probe" $form.StartPosition = [System.Windows.Forms.FormStartPosition]::Manual $form.Left = $X $form.Top = $Y $form.Width = $Width $form.Height = $Height $uiaDumpFile = $null $uiaRoot = $null $screenshotFile = $null if ($ShowWindow) { $form.Show() [System.Windows.Forms.Application]::DoEvents() Start-Sleep -Milliseconds 800 [System.Windows.Forms.Application]::DoEvents() if ($script:RealFrmP2PThreadExceptions.Count -gt 0) { $threadExceptionRecords = @() foreach ($threadException in $script:RealFrmP2PThreadExceptions) { $threadExceptionRecords += Convert-ExceptionToRecord -Exception $threadException } $relevantFrame = Get-FirstRelevantFrame -ExceptionRecords $threadExceptionRecords $threadResult = [pscustomobject]@{ ok = $false stage = "show_thread_exception" process_bitness = 32 runtime_mode = $RuntimeMode self_chat = [bool]$SelfChat skip_roster_manager = [bool]$SkipRosterManager client_dir = $clientFull type = $p2pFormType.FullName hwnd = if ($form.IsHandleCreated) { "0x" + $form.Handle.ToInt64().ToString("X") } else { $null } visible = $form.Visible relevant_frame = $relevantFrame exceptions = $threadExceptionRecords result_file = $resultFile } $threadResult | ConvertTo-Json -Depth 32 | Set-Content -LiteralPath $resultFile -Encoding UTF8 $threadResult | ConvertTo-Json -Depth 32 -Compress try { $form.Close() } catch {} exit 3 } if ($form.IsHandleCreated -and (Test-Path -LiteralPath $helperFull)) { $hwndForDump = "0x" + $form.Handle.ToInt64().ToString("X") $dump = Invoke-HelperJson -HelperPath $helperFull -Op "dump_uia" -OpArgs @{ hwnd = $hwndForDump max_depth = 8 max_children = 250 include_text = $true } -RequestId "real-frmP2PChat-dump-uia" $uiaDumpFile = Join-Path $outFull ("uia-dump-real-frmP2PChat-{0}.json" -f $timestamp) $dump | ConvertTo-Json -Depth 40 | Set-Content -LiteralPath $uiaDumpFile -Encoding UTF8 if ($dump.ok) { $uiaRoot = $dump.data.root } } if (-not $NoScreenshot) { $screenshotFile = Join-Path $outFull ("real-frmP2PChat-{0}.png" -f $timestamp) Save-WindowScreenshot -OutputPath $screenshotFile -ScreenX $form.Left -ScreenY $form.Top -CaptureWidth $form.Width -CaptureHeight $form.Height } $remainingHold = [Math]::Max(0, $HoldSeconds) if ($remainingHold -gt 0) { Start-Sleep -Seconds $remainingHold } } $result = [pscustomobject]@{ ok = $true process_bitness = 32 runtime_mode = $RuntimeMode self_chat = [bool]$SelfChat skip_roster_manager = [bool]$SkipRosterManager client_dir = $clientFull type = $p2pFormType.FullName chat_type = $p2pChat.GetType().FullName hwnd = if ($form.IsHandleCreated) { "0x" + $form.Handle.ToInt64().ToString("X") } else { $null } visible = $form.Visible name = $form.Name text = $form.Text size = @{ width = $form.Width; height = $form.Height } result_file = $resultFile uia_dump_file = $uiaDumpFile screenshot_file = $screenshotFile uia_root = $uiaRoot } $result | ConvertTo-Json -Depth 24 | Set-Content -LiteralPath $resultFile -Encoding UTF8 $result | ConvertTo-Json -Depth 24 -Compress } catch { $exceptionRecords = Convert-ExceptionToRecord -Exception $_.Exception $ctorFrame = $null foreach ($record in $exceptionRecords) { foreach ($frame in $record.frames) { if ($frame.method_type -eq "IMPP.Client.Business.ChatManager.SingleChat.frmP2PChat" -and $frame.method_name -eq ".ctor") { $ctorFrame = $frame break } } if ($ctorFrame) { break } } $matchedIlLine = if ($ctorFrame) { Find-IlLine -OutDirFull $outFull -IlOffsetHex $ctorFrame.il_offset_hex } else { $null } $result = [pscustomobject]@{ ok = $false process_bitness = 32 runtime_mode = $RuntimeMode self_chat = [bool]$SelfChat skip_roster_manager = [bool]$SkipRosterManager client_dir = $clientFull error = $_.Exception.ToString() category = $_.CategoryInfo.ToString() script_stack = $_.ScriptStackTrace ctor_il_offset_hex = if ($ctorFrame) { $ctorFrame.il_offset_hex } else { $null } ctor_frame = $ctorFrame matched_il_line = $matchedIlLine exceptions = $exceptionRecords result_file = $resultFile } $result | ConvertTo-Json -Depth 32 | Set-Content -LiteralPath $resultFile -Encoding UTF8 $result | ConvertTo-Json -Depth 32 -Compress exit 2 }