546 lines
24 KiB
PowerShell
546 lines
24 KiB
PowerShell
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 "<MessageCenter>k__BackingField" -Value $fakeMessageCenter | Out-Null
|
|
$fakeFrmMain = [System.Runtime.Serialization.FormatterServices]::GetUninitializedObject($frmMainType)
|
|
Set-FieldInHierarchy -Object $mgr -Type $mgrType -Name "<frmMain>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 "<Connection>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 "<IsConnected>k__BackingField" -Value $true | Out-Null
|
|
Set-FieldInHierarchy -Object $null -Type ($smack.GetType("com.vision.smack.SmarkManager", $true)) -Name "<SmarkManagerInstance>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 "<BaseConfig>k__BackingField" -Value $baseConfig | Out-Null
|
|
Set-FieldInHierarchy -Object $null -Type $configType -Name "<UserInfo>k__BackingField" -Value $myUser | Out-Null
|
|
Set-FieldInHierarchy -Object $null -Type $configType -Name "<LoginInfo>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 "<PresenceManager>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 "<P2PChatManager>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 "<RosterManager>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 "<RosterManager>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 "<OtherJid>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
|
|
}
|